Keep focus in Orca when parking webviews (#2362)

- Move focus back to the renderer before detaching a focused webview, including when it is parked rather than destroyed.
- Cover focused, descendant-focused, and unfocused detach cases.
This commit is contained in:
Jinjing 2026-05-19 15:51:08 -07:00 committed by GitHub
parent 49b504dc45
commit b6f2b6eed4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 10 deletions

View File

@ -75,6 +75,7 @@ import {
destroyPersistentWebview,
getHiddenContainer,
MAX_PARKED_WEBVIEWS,
moveFocusToRendererBeforeWebviewDetach,
parkedAtByTabId,
registerPersistentWebview,
registeredWebContentsIds,
@ -3571,6 +3572,7 @@ function BrowserPagePane({
}
if (webviewRegistry.get(browserTab.id) === webview) {
moveFocusToRendererBeforeWebviewDetach(webview)
getHiddenContainer().appendChild(webview)
parkedAtByTabId.set(browserTab.id, Date.now())
evictParkedWebviews(browserTab.id)

View File

@ -6,11 +6,13 @@ type ListenerRecord = {
options?: boolean | AddEventListenerOptions
}
function createWebview(): Electron.WebviewTag {
function createWebview(overrides: Partial<Electron.WebviewTag> = {}): Electron.WebviewTag {
return {
style: {},
blur: vi.fn(),
remove: vi.fn(),
contains: vi.fn(() => false)
contains: vi.fn(() => false),
...overrides
} as unknown as Electron.WebviewTag
}
@ -96,4 +98,39 @@ describe('webview registry drag listeners', () => {
expect(addedListeners).toHaveLength(3)
})
it('moves focus back to the renderer before detaching the focused webview', async () => {
const { moveFocusToRendererBeforeWebviewDetach } = await import('./webview-registry')
const webview = createWebview()
vi.stubGlobal('document', { activeElement: webview })
moveFocusToRendererBeforeWebviewDetach(webview)
expect(webview.blur).toHaveBeenCalledTimes(1)
expect(window.focus).toHaveBeenCalledTimes(1)
})
it('moves focus back to the renderer before detaching a webview that contains focus', async () => {
const { moveFocusToRendererBeforeWebviewDetach } = await import('./webview-registry')
const activeElement = { blur: vi.fn() } as unknown as HTMLElement
const webview = createWebview({ contains: vi.fn(() => true) })
vi.stubGlobal('document', { activeElement })
moveFocusToRendererBeforeWebviewDetach(webview)
expect(activeElement.blur).toHaveBeenCalledTimes(1)
expect(window.focus).toHaveBeenCalledTimes(1)
})
it('leaves focus alone before detaching an unfocused webview', async () => {
const { moveFocusToRendererBeforeWebviewDetach } = await import('./webview-registry')
const activeElement = { blur: vi.fn() } as unknown as HTMLElement
const webview = createWebview()
vi.stubGlobal('document', { activeElement })
moveFocusToRendererBeforeWebviewDetach(webview)
expect(activeElement.blur).not.toHaveBeenCalled()
expect(window.focus).not.toHaveBeenCalled()
})
})

View File

@ -100,6 +100,17 @@ export function unregisterPersistentWebview(browserTabId: string): void {
}
}
export function moveFocusToRendererBeforeWebviewDetach(webview: Electron.WebviewTag): void {
// Why: if this webview currently owns focus, removing it lets macOS hand
// activation back to the previously-active app (Slack, etc.) because the
// focused webContents is gone with no replacement. Move focus back into the
// main renderer first so Electron keeps focus inside the Orca window.
if (webview === document.activeElement || webview.contains(document.activeElement)) {
;(document.activeElement as HTMLElement | null)?.blur?.()
window.focus()
}
}
export function destroyPersistentWebview(browserTabId: string): void {
const webview = webviewRegistry.get(browserTabId)
if (!webview) {
@ -109,14 +120,7 @@ export function destroyPersistentWebview(browserTabId: string): void {
return
}
void window.api.browser.unregisterGuest({ browserPageId: browserTabId })
// Why: if this webview currently owns focus, removing it lets macOS hand
// activation back to the previously-active app (Slack, etc.) because the
// focused webContents is gone with no replacement. Move focus back into the
// main renderer first so Electron keeps focus inside the Orca window.
if (webview === document.activeElement || webview.contains(document.activeElement)) {
;(document.activeElement as HTMLElement | null)?.blur?.()
window.focus()
}
moveFocusToRendererBeforeWebviewDetach(webview)
webview.remove()
unregisterPersistentWebview(browserTabId)
registeredWebContentsIds.delete(browserTabId)