mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 00:03:52 +00:00
fix(browser): make a second wake wait for the first to finish rebuilding
A wake materializes the window before it swaps the helper session and reloads the retained address, so the page reads as resident well before it is usable. The coalescing promise was consulted after that resident check, so a second command targeting the same page returned immediately and ran against a blank renderer whose session was still being torn down underneath it. Join the in-flight wake first. Mutation-checked: restoring the old order turns the new test red.
This commit is contained in:
@@ -499,6 +499,37 @@ describe('OffscreenBrowserBackend reclamation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('makes a second wake wait for the first to finish rebuilding', async () => {
|
||||
// Why: materialize sets the window before the session swap and the reload,
|
||||
// so the page looks resident long before it is usable.
|
||||
let releaseSwap = (): void => {}
|
||||
const h = createHarness()
|
||||
await h.backend.createTab({ url: 'https://a', browserPageId: 'a' })
|
||||
h.clock.value += 120_000
|
||||
await h.backend.reclaimIdlePages()
|
||||
|
||||
const bridge = h.bridge as unknown as { onProcessSwap: ReturnType<typeof vi.fn> }
|
||||
bridge.onProcessSwap.mockImplementationOnce(
|
||||
async () => new Promise<void>((resolve) => (releaseSwap = resolve))
|
||||
)
|
||||
const first = h.backend.wakeTab('a')
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
let secondSettled = false
|
||||
const second = h.backend.wakeTab('a').then((value) => {
|
||||
secondSettled = true
|
||||
return value
|
||||
})
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
expect(secondSettled).toBe(false)
|
||||
|
||||
releaseSwap()
|
||||
expect(await first).toBe(true)
|
||||
expect(await second).toBe(true)
|
||||
})
|
||||
|
||||
it('reports false when waking a page it does not own', async () => {
|
||||
const h = createHarness()
|
||||
expect(await h.backend.wakeTab('nope')).toBe(false)
|
||||
|
||||
@@ -130,14 +130,18 @@ export class OffscreenBrowserBackend implements BrowserBackend {
|
||||
return false
|
||||
}
|
||||
page.lastActivityAt = this.now()
|
||||
const releasing = this.releasing.get(browserPageId)
|
||||
if (!releasing && page.window && !page.window.isDestroyed()) {
|
||||
return true
|
||||
}
|
||||
// Why: a wake materializes the window before it swaps the helper session
|
||||
// and reloads the address, so the page looks resident well before it is
|
||||
// usable. Join an in-flight wake first or a second command runs against a
|
||||
// blank page whose session is still being torn down underneath it.
|
||||
const inFlight = this.waking.get(browserPageId)
|
||||
if (inFlight) {
|
||||
return inFlight
|
||||
}
|
||||
const releasing = this.releasing.get(browserPageId)
|
||||
if (!releasing && page.window && !page.window.isDestroyed()) {
|
||||
return true
|
||||
}
|
||||
const wake = (async (): Promise<boolean> => {
|
||||
await releasing
|
||||
if (!this.pages.has(browserPageId)) {
|
||||
|
||||
Reference in New Issue
Block a user