Summary
PlaywrightBrowser.ensureOptimizedPageLoad() waits for the domcontentloaded load state with no timeout:
// packages/core/src/browser/playwrightBrowser.ts (ensureOptimizedPageLoad)
await this.page.waitForLoadState("domcontentloaded");
The subsequent waitForLoadState("load", { timeout: this.actionTimeoutMs }) and waitForTimeout(1000) are bounded, but the first domcontentloaded wait is not. If a navigation commits to a page (or SPA soft-navigation) that never fires DOMContentLoaded as Playwright tracks it, this await can block indefinitely — the same class of indefinite freeze fixed for frame.evaluate in #511.
Why it matters
ensureOptimizedPageLoad runs after every navigation (click-triggered nav, goto, back/forward). An unbounded wait here can freeze the whole agent with no error and no progress, just like the aria-tree frame hang.
Suggested fix
Give the domcontentloaded wait an explicit timeout (it's already wrapped in a try/catch that continues on failure, so a timeout would simply proceed to the bounded load wait). Reuse actionTimeoutMs or a dedicated constant.
Context
Discovered while diagnosing #511 (aria-tree frame.evaluate hang). Same root pattern: an unbounded Playwright wait against an adversarial page.
Summary
PlaywrightBrowser.ensureOptimizedPageLoad()waits for thedomcontentloadedload state with no timeout:The subsequent
waitForLoadState("load", { timeout: this.actionTimeoutMs })andwaitForTimeout(1000)are bounded, but the firstdomcontentloadedwait is not. If a navigation commits to a page (or SPA soft-navigation) that never firesDOMContentLoadedas Playwright tracks it, this await can block indefinitely — the same class of indefinite freeze fixed forframe.evaluatein #511.Why it matters
ensureOptimizedPageLoadruns after every navigation (click-triggered nav,goto, back/forward). An unbounded wait here can freeze the whole agent with no error and no progress, just like the aria-tree frame hang.Suggested fix
Give the
domcontentloadedwait an explicit timeout (it's already wrapped in a try/catch that continues on failure, so a timeout would simply proceed to the boundedloadwait). ReuseactionTimeoutMsor a dedicated constant.Context
Discovered while diagnosing #511 (aria-tree
frame.evaluatehang). Same root pattern: an unbounded Playwright wait against an adversarial page.