diff --git a/src/renderer/src/components/browser-pane/host-guest/bind-browser-page-webview-listeners.ts b/src/renderer/src/components/browser-pane/host-guest/bind-browser-page-webview-listeners.ts index e80409fa821..f8699324889 100644 --- a/src/renderer/src/components/browser-pane/host-guest/bind-browser-page-webview-listeners.ts +++ b/src/renderer/src/components/browser-pane/host-guest/bind-browser-page-webview-listeners.ts @@ -116,6 +116,7 @@ export function bindBrowserPageWebviewListeners({ const { handleDidStartNavigation, + handleDidRedirectNavigation, handleFullDidNavigate, handleDidNavigateInPage, handleTitleUpdate, @@ -149,6 +150,7 @@ export function bindBrowserPageWebviewListeners({ webview.addEventListener('focus', dismissAddressBarSuggestions) webview.addEventListener('did-start-loading', handleDidStartLoading) webview.addEventListener('did-start-navigation', handleDidStartNavigation) + webview.addEventListener('did-redirect-navigation', handleDidRedirectNavigation) webview.addEventListener('did-stop-loading', handleDidStopLoading) // Why: close find only on full 'did-navigate', not the shared handler, which also fires on SPA in-page hash/pushState changes. const handleFindCloseOnNavigate = (): void => { @@ -186,6 +188,7 @@ export function bindBrowserPageWebviewListeners({ webview.removeEventListener('focus', dismissAddressBarSuggestions) webview.removeEventListener('did-start-loading', handleDidStartLoading) webview.removeEventListener('did-start-navigation', handleDidStartNavigation) + webview.removeEventListener('did-redirect-navigation', handleDidRedirectNavigation) webview.removeEventListener('did-stop-loading', handleDidStopLoading) webview.removeEventListener('did-navigate', handleFullDidNavigate) webview.removeEventListener('did-navigate', handleFindCloseOnNavigate) diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts index 628e6cce75a..924e7834319 100644 --- a/src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts +++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts @@ -98,6 +98,21 @@ describe('favicon retention across navigations', () => { expect(harness.updates.at(-1)).toEqual({ faviconUrl: null }) }) + it('drops the icon when a same-origin navigation redirects to another origin', () => { + const harness = createHarness('https://github.com/nodejs/node') + harness.navigation.handleFaviconUpdate({ favicons: [GITHUB_ICON] }) + harness.navigateTo('https://github.com/login') + + harness.navigation.handleDidRedirectNavigation({ + isMainFrame: true, + isInPlace: false, + url: 'https://example.com/after-login' + } as Electron.DidRedirectNavigationEvent) + + expect(harness.faviconUrlRef.current).toBeNull() + expect(harness.updates.at(-1)).toEqual({ faviconUrl: null }) + }) + it('does not clear on a same-document navigation', () => { const harness = createHarness('https://github.com/nodejs/node') harness.navigation.handleFaviconUpdate({ favicons: [GITHUB_ICON] }) diff --git a/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-navigation-handlers.ts b/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-navigation-handlers.ts index 766641c752a..240e763cf63 100644 --- a/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-navigation-handlers.ts +++ b/src/renderer/src/components/browser-pane/host-guest/browser-page-webview-navigation-handlers.ts @@ -45,6 +45,7 @@ export type BrowserPageWebviewNavigationHandlersArgs = { export type BrowserPageWebviewNavigationHandlers = { handleDidStartNavigation: (event: Electron.DidStartNavigationEvent) => void + handleDidRedirectNavigation: (event: Electron.DidRedirectNavigationEvent) => void handleFullDidNavigate: (event: BrowserPageNavigateEvent) => void handleDidNavigateInPage: (event: BrowserPageNavigateEvent) => void handleTitleUpdate: (event: { title?: string }) => void @@ -68,6 +69,28 @@ export function createBrowserPageWebviewNavigationHandlers({ annotationViewportBridgeTokenRef, setBrowserOverlayViewport }: BrowserPageWebviewNavigationHandlersArgs): BrowserPageWebviewNavigationHandlers { + const clearFaviconIfOriginChanges = ( + event: Electron.DidStartNavigationEvent | Electron.DidRedirectNavigationEvent + ): void => { + if (!event.isMainFrame || event.isInPlace || !event.url) { + return + } + const browserStartedUrl = redactKagiSessionToken(event.url) + const startedUrl = normalizeBrowserNavigationUrl(browserStartedUrl) ?? browserStartedUrl + // Why getURL() and not lastKnownWebviewUrlRef: Orca-driven navigations point that ref at the + // destination before assigning src, so it can't identify the document being left. + let committedUrl: string | null = null + try { + committedUrl = webview.getURL() || null + } catch { + // Why: a guest that hasn't attached yet rejects getURL(); an unknown origin keeps the icon. + } + if (browserNavigationLeavesFaviconOrigin(committedUrl, startedUrl)) { + faviconUrlRef.current = null + onUpdatePageStateRef.current(browserTabId, { faviconUrl: null }) + } + } + const handleDidStartNavigation = (event: Electron.DidStartNavigationEvent): void => { if (!event.isMainFrame || event.isInPlace || !event.url) { return @@ -81,18 +104,11 @@ export function createBrowserPageWebviewNavigationHandlers({ // Why here and not on did-start-loading: Chromium re-announces a favicon only when the icon URL // list changes, so clearing on every load strands same-origin navigations with no icon and no // event that would ever restore one. - // Why getURL() and not lastKnownWebviewUrlRef: Orca-driven navigations point that ref at the - // destination before assigning src, so it can't identify the document being left. - let committedUrl: string | null = null - try { - committedUrl = webview.getURL() || null - } catch { - // Why: a guest that hasn't attached yet rejects getURL(); an unknown origin keeps the icon. - } - if (browserNavigationLeavesFaviconOrigin(committedUrl, startedUrl)) { - faviconUrlRef.current = null - onUpdatePageStateRef.current(browserTabId, { faviconUrl: null }) - } + clearFaviconIfOriginChanges(event) + } + + const handleDidRedirectNavigation = (event: Electron.DidRedirectNavigationEvent): void => { + clearFaviconIfOriginChanges(event) } const handleDidNavigate = ( @@ -187,6 +203,7 @@ export function createBrowserPageWebviewNavigationHandlers({ return { handleDidStartNavigation, + handleDidRedirectNavigation, handleFullDidNavigate, handleDidNavigateInPage, handleTitleUpdate,