mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(desktop): guard link-title readTitle against destroyed windows
Grace and timeout timers in runRenderTitleJob can call getTitle after finish() tears down the hidden BrowserWindow, throwing in the main process when the Artifacts page resolves many link titles concurrently.
This commit is contained in:
parent
1c4cc00f73
commit
359518beac
3 changed files with 75 additions and 6 deletions
|
|
@ -49,4 +49,23 @@ function guardLinkTitleSession(partitionSession) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = { createLinkTitleWindow, guardLinkTitleSession, linkTitleWindowOptions }
|
||||
// Read the page title from a title-fetch window. Callers schedule this from
|
||||
// timers that can fire after finish() destroys the window, so every access must
|
||||
// guard isDestroyed and swallow Electron's "Object has been destroyed" throws.
|
||||
function readLinkTitleWindowTitle(window) {
|
||||
try {
|
||||
if (!window || window.isDestroyed()) return ''
|
||||
const contents = window.webContents
|
||||
if (!contents || contents.isDestroyed()) return ''
|
||||
return contents.getTitle() || ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createLinkTitleWindow,
|
||||
guardLinkTitleSession,
|
||||
linkTitleWindowOptions,
|
||||
readLinkTitleWindowTitle
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
const assert = require('node:assert/strict')
|
||||
const test = require('node:test')
|
||||
|
||||
const { createLinkTitleWindow, guardLinkTitleSession, linkTitleWindowOptions } = require('./link-title-window.cjs')
|
||||
const {
|
||||
createLinkTitleWindow,
|
||||
guardLinkTitleSession,
|
||||
linkTitleWindowOptions,
|
||||
readLinkTitleWindowTitle
|
||||
} = require('./link-title-window.cjs')
|
||||
|
||||
function makeFakeBrowserWindow() {
|
||||
const calls = { audioMuted: [] }
|
||||
|
|
@ -66,3 +71,44 @@ test('guardLinkTitleSession cancels downloads triggered by the title-fetch windo
|
|||
test('guardLinkTitleSession is a no-op when session.on throws', () => {
|
||||
assert.doesNotThrow(() => guardLinkTitleSession({ on() { throw new Error() } }))
|
||||
})
|
||||
|
||||
test('readLinkTitleWindowTitle returns empty for missing or destroyed windows', () => {
|
||||
assert.equal(readLinkTitleWindowTitle(null), '')
|
||||
assert.equal(readLinkTitleWindowTitle(undefined), '')
|
||||
assert.equal(readLinkTitleWindowTitle({ isDestroyed: () => true }), '')
|
||||
})
|
||||
|
||||
test('readLinkTitleWindowTitle returns empty when webContents is destroyed', () => {
|
||||
const window = {
|
||||
isDestroyed: () => false,
|
||||
webContents: { isDestroyed: () => true, getTitle: () => 'Should Not Read' }
|
||||
}
|
||||
|
||||
assert.equal(readLinkTitleWindowTitle(window), '')
|
||||
})
|
||||
|
||||
test('readLinkTitleWindowTitle swallows getTitle throws after teardown', () => {
|
||||
const window = {
|
||||
isDestroyed: () => false,
|
||||
webContents: {
|
||||
isDestroyed: () => false,
|
||||
getTitle: () => {
|
||||
throw new Error('Object has been destroyed')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.equal(readLinkTitleWindowTitle(window), '')
|
||||
})
|
||||
|
||||
test('readLinkTitleWindowTitle returns trimmed page title', () => {
|
||||
const window = {
|
||||
isDestroyed: () => false,
|
||||
webContents: {
|
||||
isDestroyed: () => false,
|
||||
getTitle: () => 'Example Domain'
|
||||
}
|
||||
}
|
||||
|
||||
assert.equal(readLinkTitleWindowTitle(window), 'Example Domain')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,7 +36,11 @@ const {
|
|||
SESSION_WINDOW_MIN_WIDTH
|
||||
} = require('./session-windows.cjs')
|
||||
const { canImportHermesCli, verifyHermesCli } = require('./backend-probes.cjs')
|
||||
const { createLinkTitleWindow, guardLinkTitleSession } = require('./link-title-window.cjs')
|
||||
const {
|
||||
createLinkTitleWindow,
|
||||
guardLinkTitleSession,
|
||||
readLinkTitleWindowTitle
|
||||
} = require('./link-title-window.cjs')
|
||||
const { probeGatewayWebSocket } = require('./gateway-ws-probe.cjs')
|
||||
const { adoptServedDashboardToken } = require('./dashboard-token.cjs')
|
||||
const { waitForDashboardPortAnnouncement } = require('./backend-ready.cjs')
|
||||
|
|
@ -3557,13 +3561,13 @@ function runRenderTitleJob(rawUrl) {
|
|||
return finish('')
|
||||
}
|
||||
|
||||
const readTitle = () => window?.webContents?.getTitle?.() || ''
|
||||
const finishWithTitle = () => finish(readLinkTitleWindowTitle(window))
|
||||
const scheduleGrace = () => {
|
||||
if (graceTimer) clearTimeout(graceTimer)
|
||||
graceTimer = setTimeout(() => finish(readTitle()), RENDER_TITLE_GRACE_MS)
|
||||
graceTimer = setTimeout(finishWithTitle, RENDER_TITLE_GRACE_MS)
|
||||
}
|
||||
|
||||
hardTimer = setTimeout(() => finish(readTitle()), RENDER_TITLE_TIMEOUT_MS)
|
||||
hardTimer = setTimeout(finishWithTitle, RENDER_TITLE_TIMEOUT_MS)
|
||||
|
||||
window.webContents.setUserAgent(TITLE_USER_AGENT)
|
||||
window.webContents.on('page-title-updated', scheduleGrace)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue