mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-23 14:33:36 +08:00
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import type { BrowserWindow } from 'electron'
|
|
|
|
export const DEFAULT_RENDERER_UNRESPONSIVE_RECOVERY_DELAY_MS = 10_000
|
|
|
|
export type RendererLifecycleOptions = {
|
|
window: BrowserWindow
|
|
isQuitting: () => boolean
|
|
recordDiagnostic: (detail: string) => string
|
|
writeSnapshot: (reason: string) => void
|
|
onRendererProcessGone?: (detail: string) => void
|
|
onRecoveryExhausted: (detail: string) => void
|
|
unresponsiveRecoveryDelayMs?: number
|
|
}
|
|
|
|
export function installRendererLifecycle({
|
|
window,
|
|
isQuitting,
|
|
recordDiagnostic,
|
|
writeSnapshot,
|
|
onRendererProcessGone,
|
|
onRecoveryExhausted,
|
|
unresponsiveRecoveryDelayMs = DEFAULT_RENDERER_UNRESPONSIVE_RECOVERY_DELAY_MS,
|
|
}: RendererLifecycleOptions): void {
|
|
let recoveryAttempted = false
|
|
let failureReported = false
|
|
let unresponsiveRecoveryTimer: ReturnType<typeof setTimeout> | null = null
|
|
let rendererReloadTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
const clearUnresponsiveRecovery = () => {
|
|
if (!unresponsiveRecoveryTimer) return
|
|
clearTimeout(unresponsiveRecoveryTimer)
|
|
unresponsiveRecoveryTimer = null
|
|
}
|
|
const clearRendererReload = () => {
|
|
if (!rendererReloadTimer) return
|
|
clearTimeout(rendererReloadTimer)
|
|
rendererReloadTimer = null
|
|
}
|
|
const reportRecoveryFailure = (detail: string) => {
|
|
if (failureReported || isQuitting()) return
|
|
failureReported = true
|
|
onRecoveryExhausted(recordDiagnostic(`[recovery-exhausted] ${detail}`))
|
|
}
|
|
const recoverRenderer = (trigger: string) => {
|
|
clearUnresponsiveRecovery()
|
|
if (isQuitting() || window.isDestroyed() || window.webContents.isDestroyed()) {
|
|
recordDiagnostic(`[recovery-skipped] trigger=${trigger} quitting=${isQuitting()}`)
|
|
return
|
|
}
|
|
if (rendererReloadTimer) {
|
|
recordDiagnostic(`[recovery-already-scheduled] trigger=${trigger}`)
|
|
return
|
|
}
|
|
if (recoveryAttempted) {
|
|
reportRecoveryFailure(`trigger=${trigger}`)
|
|
return
|
|
}
|
|
|
|
recoveryAttempted = true
|
|
recordDiagnostic(`[recovery-started] trigger=${trigger}`)
|
|
rendererReloadTimer = setTimeout(() => {
|
|
rendererReloadTimer = null
|
|
if (isQuitting() || window.isDestroyed() || window.webContents.isDestroyed()) {
|
|
recordDiagnostic(`[recovery-skipped] trigger=${trigger} quitting=${isQuitting()}`)
|
|
return
|
|
}
|
|
try {
|
|
window.webContents.reload()
|
|
} catch (error) {
|
|
reportRecoveryFailure(
|
|
`trigger=${trigger} reloadError=${error instanceof Error ? error.message : String(error)}`,
|
|
)
|
|
}
|
|
}, 0)
|
|
}
|
|
|
|
window.webContents.on('did-finish-load', () => {
|
|
clearUnresponsiveRecovery()
|
|
if (recoveryAttempted) recordDiagnostic('[recovery-loaded]')
|
|
writeSnapshot('did-finish-load')
|
|
})
|
|
window.webContents.on('did-fail-load', (
|
|
_event,
|
|
errorCode,
|
|
errorDescription,
|
|
validatedURL,
|
|
isMainFrame,
|
|
) => {
|
|
writeSnapshot(`did-fail-load:${errorCode}:${errorDescription}:${validatedURL}`)
|
|
if (recoveryAttempted && isMainFrame && errorCode !== -3) {
|
|
reportRecoveryFailure(`loadError=${errorCode}:${errorDescription}`)
|
|
}
|
|
})
|
|
window.webContents.on('render-process-gone', (_event, details) => {
|
|
clearUnresponsiveRecovery()
|
|
const detail = recordDiagnostic(
|
|
`[process-gone] reason=${details.reason} exitCode=${details.exitCode}`,
|
|
)
|
|
onRendererProcessGone?.(detail)
|
|
writeSnapshot(`render-process-gone:${details.reason}:${details.exitCode}`)
|
|
recoverRenderer(`process-gone:${details.reason}`)
|
|
})
|
|
window.webContents.on('unresponsive', () => {
|
|
recordDiagnostic('[unresponsive]')
|
|
writeSnapshot('unresponsive')
|
|
if (unresponsiveRecoveryTimer) return
|
|
unresponsiveRecoveryTimer = setTimeout(() => {
|
|
unresponsiveRecoveryTimer = null
|
|
recoverRenderer('unresponsive-timeout')
|
|
}, unresponsiveRecoveryDelayMs)
|
|
})
|
|
window.webContents.on('responsive', () => {
|
|
clearUnresponsiveRecovery()
|
|
recordDiagnostic('[responsive]')
|
|
writeSnapshot('responsive')
|
|
})
|
|
window.on('closed', () => {
|
|
clearUnresponsiveRecovery()
|
|
clearRendererReload()
|
|
})
|
|
}
|