Przeglądaj źródła

fix(desktop): guard destroyed recovery windows (#37406)

Luke Parker 1 miesiąc temu
rodzic
commit
4436678f7d

+ 2 - 1
packages/desktop/src/main/index.ts

@@ -33,6 +33,7 @@ import {
   type SidecarListener,
 } from "./server"
 import { setupAutoUpdater, showUpdaterDialog } from "./updater"
+import { safeWebContentsURL } from "./window-state"
 import {
   getLastFocusedWindow,
   registerRendererProtocol,
@@ -233,7 +234,7 @@ const main = Effect.gen(function* () {
   })
 
   app.on("render-process-gone", (_event, webContents, details) => {
-    writeLog("window", "app render process gone", { url: webContents.getURL(), details }, "error")
+    writeLog("window", "app render process gone", { url: safeWebContentsURL(webContents), details }, "error")
   })
 
   setRelaunchHandler(() => {

+ 2 - 1
packages/desktop/src/main/unresponsive.ts

@@ -1,5 +1,6 @@
 import type { BrowserWindow } from "electron"
 import { write as writeLog } from "./logging"
+import { safeWindowURL } from "./window-state"
 
 const sampleInterval = 1000
 const samplePeriod = 15000
@@ -46,7 +47,7 @@ export function createUnresponsiveSampler(win: BrowserWindow, name: string) {
     const message = [
       "renderer unresponsive samples",
       `Window: ${name}`,
-      `URL: ${win.isDestroyed() ? "<destroyed>" : win.webContents.getURL()}`,
+      `URL: ${safeWindowURL(win)}`,
       ...entries.map((entry) => `<${entry[1]}> ${entry[0]}`),
       `Total Samples: ${total}`,
     ].join("\n")

+ 29 - 0
packages/desktop/src/main/window-state.ts

@@ -0,0 +1,29 @@
+export const destroyedWindowURL = "<destroyed>"
+
+type WebContentsURLState = {
+  isDestroyed(): boolean
+  getURL(): string
+}
+
+type WindowURLState = {
+  isDestroyed(): boolean
+  readonly webContents: WebContentsURLState
+}
+
+export function safeWebContentsURL(webContents: WebContentsURLState) {
+  try {
+    if (webContents.isDestroyed()) return destroyedWindowURL
+    return webContents.getURL()
+  } catch {
+    return destroyedWindowURL
+  }
+}
+
+export function safeWindowURL(win: WindowURLState) {
+  try {
+    if (win.isDestroyed()) return destroyedWindowURL
+    return safeWebContentsURL(win.webContents)
+  } catch {
+    return destroyedWindowURL
+  }
+}

+ 5 - 9
packages/desktop/src/main/windows.ts

@@ -13,6 +13,7 @@ import { getStore, removeStoreFile } from "./store"
 import { PINCH_ZOOM_ENABLED_KEY, WINDOW_IDS_KEY } from "./store-keys"
 import { createUnresponsiveSampler } from "./unresponsive"
 import { createWindowRegistry } from "./window-registry"
+import { safeWindowURL } from "./window-state"
 
 const root = dirname(fileURLToPath(import.meta.url))
 const rendererRoot = join(root, "../renderer")
@@ -364,7 +365,7 @@ function wireWindowRecovery(win: BrowserWindow, name: string) {
         errorCode,
         errorDescription,
         validatedURL,
-        currentURL: win.webContents.getURL(),
+        currentURL: safeWindowURL(win),
         isMainFrame,
       },
       "error",
@@ -386,12 +387,7 @@ function wireWindowRecovery(win: BrowserWindow, name: string) {
   })
   win.webContents.on("render-process-gone", (_event, details) => {
     sampler.stopAndFlush()
-    writeLog(
-      "window",
-      "renderer process gone",
-      { window: name, currentURL: win.webContents.getURL(), details },
-      "error",
-    )
+    writeLog("window", "renderer process gone", { window: name, currentURL: safeWindowURL(win), details }, "error")
     void show(
       "OpenCode window terminated unexpectedly",
       [`Window: ${name}`, `Reason: ${details.reason}`, `Code: ${details.exitCode ?? "<unknown>"}`].join("\n"),
@@ -399,12 +395,12 @@ function wireWindowRecovery(win: BrowserWindow, name: string) {
     )
   })
   win.on("unresponsive", () => {
-    writeLog("window", "renderer unresponsive", { window: name, currentURL: win.webContents.getURL() }, "error")
+    writeLog("window", "renderer unresponsive", { window: name, currentURL: safeWindowURL(win) }, "error")
     sampler.start()
     void show("OpenCode is not responding", "You can relaunch the app, open the logs, or keep waiting.", true)
   })
   win.on("responsive", () => {
-    writeLog("window", "renderer responsive", { window: name, currentURL: win.webContents.getURL() }, "error")
+    writeLog("window", "renderer responsive", { window: name, currentURL: safeWindowURL(win) }, "error")
     sampler.stopAndFlush()
   })
   win.webContents.on("console-message", (_event, level, message, line, sourceId) => {