Skip to content

Commit 2eb0950

Browse files
committed
fix(desktop-browser): expire stale recovery history
1 parent af4cf71 commit 2eb0950

2 files changed

Lines changed: 50 additions & 9 deletions

File tree

apps/desktop/src/main/browser-agent/session.test.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ function hostResizeHandler(win: BrowserWindow): () => void {
134134
return handler as () => void
135135
}
136136

137+
function mainFrameNavigationStarted(
138+
contents: MockView['webContents'],
139+
isSameDocument = false
140+
): void {
141+
const handler = contents.on.mock.calls
142+
.filter(([eventName]) => eventName === 'did-start-navigation')
143+
.at(-1)?.[1]
144+
if (typeof handler !== 'function') throw new Error('no navigation-start listener bound')
145+
handler({ isMainFrame: true, isSameDocument })
146+
}
147+
137148
describe('browser-agent session', () => {
138149
let win: BrowserWindow
139150
let session: SessionModule
@@ -893,7 +904,7 @@ describe('browser-agent session', () => {
893904
mockContents.navigationHistory.canGoForward.mockReturnValue(true)
894905
expect(session.goForward(contents)).toBe(true)
895906
expect(mockContents.navigationHistory.goForward).toHaveBeenCalledTimes(1)
896-
session.notePageLoadStarted(contents)
907+
mainFrameNavigationStarted(mockContents)
897908

898909
mockContents.navigationHistory.getActiveIndex.mockReturnValue(3)
899910
expect(session.goForward(contents)).toBe(true)
@@ -911,7 +922,31 @@ describe('browser-agent session', () => {
911922
})
912923
session.goBack(contents)
913924

914-
session.notePageLoadStarted(contents)
925+
mainFrameNavigationStarted(mockContents)
926+
927+
expect(session.canGoForward(contents)).toBe(false)
928+
})
929+
930+
it('discards synthetic Forward after same-document traversal and a fresh navigation', () => {
931+
const mockContents = (session.ensureTab().view as unknown as MockView).webContents
932+
const contents = mockContents as unknown as WebContents
933+
mockContents.navigationHistory.getActiveIndex.mockReturnValue(3)
934+
session.recordPageLoadFailure(contents, {
935+
kind: 'load-error',
936+
code: -102,
937+
description: 'ERR_CONNECTION_REFUSED',
938+
url: 'https://example.com/failed',
939+
})
940+
941+
session.goBack(contents)
942+
mockContents.navigationHistory.canGoBack.mockReturnValue(true)
943+
expect(session.goBack(contents)).toBe(true)
944+
945+
mainFrameNavigationStarted(mockContents, true)
946+
947+
expect(session.canGoForward(contents)).toBe(true)
948+
949+
mainFrameNavigationStarted(mockContents)
915950

916951
expect(session.canGoForward(contents)).toBe(false)
917952
})

apps/desktop/src/main/browser-agent/session.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export interface AgentTab {
8989
pendingRestoreUrl?: string
9090
pageIssue?: BrowserPageIssue
9191
syntheticForward?: { url: string; baseHistoryIndex: number }
92-
preserveSyntheticForwardOnNextLoad?: boolean
92+
preserveSyntheticForwardOnNextNavigation?: boolean
9393
recoveringUnresponsive?: boolean
9494
}
9595

@@ -1043,18 +1043,23 @@ export function recordPageLoadFailure(
10431043
publishPageIssue(tab, true)
10441044
}
10451045

1046-
/** Clears transient recovery state when Chromium begins a new top-level load. */
1046+
/** Clears transient recovery state when Chromium begins loading a new document. */
10471047
export function notePageLoadStarted(contents: WebContents): void {
10481048
const tab = tabForContents(contents)
10491049
if (!tab) return
10501050
const changed = Boolean(tab.pageIssue)
10511051
tab.pageIssue = undefined
1052-
if (tab.preserveSyntheticForwardOnNextLoad) {
1053-
tab.preserveSyntheticForwardOnNextLoad = false
1052+
if (changed) publishPageIssue(tab)
1053+
}
1054+
1055+
function notePageNavigationStarted(contents: WebContents): void {
1056+
const tab = tabForContents(contents)
1057+
if (!tab) return
1058+
if (tab.preserveSyntheticForwardOnNextNavigation) {
1059+
tab.preserveSyntheticForwardOnNextNavigation = false
10541060
} else {
10551061
tab.syntheticForward = undefined
10561062
}
1057-
if (changed) publishPageIssue(tab)
10581063
}
10591064

10601065
/** Includes Sim's failed-navigation entry in the browser's Back availability. */
@@ -1085,7 +1090,7 @@ export function goBack(contents: WebContents): boolean {
10851090
return true
10861091
}
10871092
if (!contents.navigationHistory.canGoBack()) return false
1088-
tab.preserveSyntheticForwardOnNextLoad = Boolean(tab.syntheticForward)
1093+
tab.preserveSyntheticForwardOnNextNavigation = Boolean(tab.syntheticForward)
10891094
contents.navigationHistory.goBack()
10901095
return true
10911096
}
@@ -1100,7 +1105,7 @@ export function goForward(contents: WebContents): boolean {
11001105
contents.navigationHistory.getActiveIndex() < syntheticForward.baseHistoryIndex &&
11011106
contents.navigationHistory.canGoForward()
11021107
) {
1103-
tab.preserveSyntheticForwardOnNextLoad = true
1108+
tab.preserveSyntheticForwardOnNextNavigation = true
11041109
contents.navigationHistory.goForward()
11051110
return true
11061111
}
@@ -1498,6 +1503,7 @@ function createTabView(): WebContentsView {
14981503
'did-start-navigation',
14991504
bindToBrowserScope(scopeId, (details) => {
15001505
if (!details.isMainFrame) return
1506+
notePageNavigationStarted(contents)
15011507
events?.onTabNavigated(contents, false)
15021508
})
15031509
)

0 commit comments

Comments
 (0)