import type { BrowserWindow } from "electron/main"; import { STANDARD_USER_AGENT } from "./user-agent"; const ignoredNavigationFailurePattern = /ERR_ABORTED|ERR_BLOCKED_BY_CLIENT/i; const errorPagePrefix = "data:text/html"; interface WindowNavigationState { readyForDetection: boolean; lastMainFrameError: string | null; } const navigationStateRegistry = new WeakMap(); function escapeHtml(value: string): string { return value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll("\"", """) .replaceAll("'", "'"); } function errorPageDataURL(context: string, targetURL: string, message: string): string { const html = ` ${escapeHtml(context)}

授权页加载失败

${escapeHtml(context)} 在打开远程页面时出现网络或 TLS 握手异常。窗口已保留,不再直接闪退。

如果这是偶发网络抖动,点“重新加载”即可;如果持续失败,再看控制台里打印的 URL 和错误码定位。

${escapeHtml(message)}

${escapeHtml(targetURL)}
打开目标地址
`; return `data:text/html;charset=UTF-8,${encodeURIComponent(html)}`; } export function attachWindowDiagnostics(window: BrowserWindow, context: string): void { navigationStateRegistry.set(window, { readyForDetection: false, lastMainFrameError: null, }); window.webContents.on("did-start-loading", () => { const state = navigationStateRegistry.get(window); if (!state) { return; } state.readyForDetection = false; }); window.webContents.on( "did-fail-load", (_event, errorCode, errorDescription, validatedURL, isMainFrame) => { if (!isMainFrame || errorCode === -3) { return; } const state = navigationStateRegistry.get(window); if (state) { state.readyForDetection = false; state.lastMainFrameError = `${errorCode}:${errorDescription}`; } console.warn(`[desktop-window] ${context} main-frame load failed`, { errorCode, errorDescription, validatedURL, }); }, ); window.webContents.on("did-finish-load", () => { const state = navigationStateRegistry.get(window); if (!state) { return; } const currentURL = window.webContents.getURL(); state.readyForDetection = !currentURL.startsWith(errorPagePrefix); if (state.readyForDetection) { state.lastMainFrameError = null; } }); window.webContents.on("render-process-gone", (_event, details) => { const state = navigationStateRegistry.get(window); if (state) { state.readyForDetection = false; } console.error(`[desktop-window] ${context} renderer gone`, details); }); } export function isWindowReadyForDetection(window: BrowserWindow): boolean { const state = navigationStateRegistry.get(window); if (!state) { return !window.webContents.getURL().startsWith(errorPagePrefix); } return state.readyForDetection; } export async function loadWindowURLSafely( window: BrowserWindow, targetURL: string, context: string, ): Promise { try { await window.loadURL(targetURL, { userAgent: STANDARD_USER_AGENT }); } catch (error) { const message = error instanceof Error ? error.message : String(error); if (ignoredNavigationFailurePattern.test(message) || window.isDestroyed()) { return; } console.error(`[desktop-window] ${context} loadURL failed`, { targetURL, message, }); if (window.webContents.getURL().startsWith(errorPagePrefix)) { return; } try { await window.loadURL(errorPageDataURL(context, targetURL, message)); } catch (fallbackError) { const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError); console.error(`[desktop-window] ${context} error-page loadURL failed`, { targetURL, message: fallbackMessage, }); } } }