fix(desktop): report and mute a Browser guest that attaches after its session is hidden
The off-screen hook only re-ran when hidden/parked changed, and a guest has no webContents id until it attaches. A page whose session was parked between mount and attach was never reported hidden to main (so mouse back/reload could still target it) and never muted. The pane now feeds the guest's dom-ready to the hook, which re-reports and re-mutes for the attached id.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { type RefObject, useEffect } from 'react'
|
||||
import { type RefObject, useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import { usePaneVisible } from '@/components/pane-shell/pane-visibility'
|
||||
import { $parkedTreePanes } from '@/components/pane-shell/tree/parked-panes'
|
||||
@@ -55,10 +55,16 @@ function muteGuest(webview: OffscreenGuest): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export function usePreviewGuestOffscreen(webviewRef: RefObject<null | OffscreenGuest>, tabId?: string): void {
|
||||
/** Reports and mutes the guest as it goes off screen. Returns a `dom-ready`
|
||||
* listener the pane must attach to its `<webview>`: a guest has no id until it
|
||||
* attaches, and one that attaches while its session is already hidden would
|
||||
* otherwise never be reported or muted (the effects only re-run on change). */
|
||||
export function usePreviewGuestOffscreen(webviewRef: RefObject<null | OffscreenGuest>, tabId?: string): () => void {
|
||||
const hidden = !usePaneVisible()
|
||||
const parkedPanes = useStore($parkedTreePanes)
|
||||
const parked = Boolean(tabId) && parkedPanes.has(`${PREVIEW_TILE_PREFIX}:${tabId}`)
|
||||
const [attachedId, setAttachedId] = useState<null | number>(null)
|
||||
const noteGuestReady = useCallback(() => setAttachedId(guestId(webviewRef.current)), [webviewRef])
|
||||
|
||||
useEffect(() => {
|
||||
const id = guestId(webviewRef.current)
|
||||
@@ -66,7 +72,7 @@ export function usePreviewGuestOffscreen(webviewRef: RefObject<null | OffscreenG
|
||||
if (id !== null) {
|
||||
window.hermesDesktop?.setPreviewGuestHidden?.(id, hidden)
|
||||
}
|
||||
}, [hidden, webviewRef])
|
||||
}, [attachedId, hidden, webviewRef])
|
||||
|
||||
useEffect(() => {
|
||||
const webview = webviewRef.current
|
||||
@@ -88,5 +94,7 @@ export function usePreviewGuestOffscreen(webviewRef: RefObject<null | OffscreenG
|
||||
// The guest went away while parked; nothing to restore.
|
||||
}
|
||||
}
|
||||
}, [parked, webviewRef])
|
||||
}, [attachedId, parked, webviewRef])
|
||||
|
||||
return noteGuestReady
|
||||
}
|
||||
|
||||
@@ -945,6 +945,40 @@ describe('PreviewPane off-screen guest', () => {
|
||||
expect(setPreviewGuestHidden).toHaveBeenLastCalledWith(41, false)
|
||||
})
|
||||
|
||||
// A guest has no id until it attaches; one that attaches after its session
|
||||
// was already parked must still be reported to main and muted.
|
||||
it('reports and mutes a guest that attaches while its session is already hidden', async () => {
|
||||
const setPreviewGuestHidden = vi.fn()
|
||||
desktopWindow.hermesDesktop = { setPreviewGuestHidden } as unknown as Window['hermesDesktop']
|
||||
act(() => setTreePaneParked('preview-tile:url:late', true))
|
||||
|
||||
let rendered!: ReturnType<typeof render>
|
||||
|
||||
await act(async () => {
|
||||
rendered = render(
|
||||
<PaneVisibleContext value={false}>
|
||||
<PreviewPane tabId="url:late" target={target} />
|
||||
</PaneVisibleContext>
|
||||
)
|
||||
})
|
||||
|
||||
let muted = false
|
||||
const webview = rendered.container.querySelector('webview')!
|
||||
Object.assign(webview, {
|
||||
getWebContentsId: () => 52,
|
||||
isAudioMuted: () => muted,
|
||||
setAudioMuted: (next: boolean) => (muted = next)
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
webview.dispatchEvent(new Event('dom-ready'))
|
||||
})
|
||||
|
||||
expect(setPreviewGuestHidden).toHaveBeenLastCalledWith(52, true)
|
||||
expect(muted).toBe(true)
|
||||
act(() => setTreePaneParked('preview-tile:url:late', false))
|
||||
})
|
||||
|
||||
// A hidden session's kept page keeps running, but is not heard from the chat
|
||||
// the user switched to; it comes back with the sound it had.
|
||||
async function renderAudibleGuest(tabId: string, mutedBefore: boolean) {
|
||||
|
||||
@@ -276,7 +276,7 @@ export function PreviewPane({
|
||||
const lastRestartEventRef = useRef('')
|
||||
const previewContentRef = useRef<HTMLDivElement | null>(null)
|
||||
const webviewRef = useRef<PreviewWebview | null>(null)
|
||||
usePreviewGuestOffscreen(webviewRef, tabId)
|
||||
const noteGuestReady = usePreviewGuestOffscreen(webviewRef, tabId)
|
||||
const previewServerRestart = useStore($previewServerRestart)
|
||||
const consoleHeight = useStore(consoleState.$height)
|
||||
const consoleOpen = useStore(consoleState.$open)
|
||||
@@ -1361,6 +1361,7 @@ export function PreviewPane({
|
||||
webview.addEventListener('page-title-updated', notePage)
|
||||
// #101880: never let a guest reach the native print panel.
|
||||
webview.addEventListener('dom-ready', armPrintGuard)
|
||||
webview.addEventListener('dom-ready', noteGuestReady)
|
||||
host.appendChild(webview)
|
||||
webviewRef.current = webview
|
||||
|
||||
@@ -1378,10 +1379,11 @@ export function PreviewPane({
|
||||
webview.removeEventListener('did-stop-loading', onStop)
|
||||
webview.removeEventListener('page-title-updated', notePage)
|
||||
webview.removeEventListener('dom-ready', armPrintGuard)
|
||||
webview.removeEventListener('dom-ready', noteGuestReady)
|
||||
webview.remove()
|
||||
setAnnotate(session => (session.mode ? { ...endAnnotateMode(session), stack: emptyAnnotateStack() } : session))
|
||||
}
|
||||
}, [appendConsoleEntry, consoleState, copy, isRemoteHtml, isWebPreview, tabId, target.kind])
|
||||
}, [appendConsoleEntry, consoleState, copy, isRemoteHtml, isWebPreview, noteGuestReady, tabId, target.kind])
|
||||
|
||||
// Steers the LIVE guest when the session opens a new URL (#120265): loadURL
|
||||
// keeps the webview instance (JS state, cookies, form data, scroll, refs,
|
||||
|
||||
Reference in New Issue
Block a user