fix(browse): closeTab loses last-tab auto-create when 'close' event wins the race

The page 'close' event handler can run while page.close() is awaited and
reassign activeTabId before closeTab's post-close active check, so the
last-tab auto-create branch never ran and the manager was left with zero
tabs. Capture wasActive before close(); only reassign activeTabId when it
no longer points at a live tab. Event-dispatch order varies across
Playwright/Chromium versions, which is why this raced on some machines only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
t 2026-07-10 15:22:12 -07:00
parent 82a6778185
commit 383814366b
1 changed files with 16 additions and 4 deletions

View File

@ -798,19 +798,31 @@ export class BrowserManager {
const page = this.pages.get(tabId);
if (!page) throw new Error(`Tab ${tabId} not found`);
// Capture BEFORE close(): the page 'close' event handler wired in
// wirePageEvents() can fire while page.close() is awaited. It removes
// the tab from the maps and reassigns activeTabId (to 0 when no tabs
// remain), so a post-close `tabId === this.activeTabId` check is
// order-dependent — whether the event dispatches before or after
// close() resolves varies across Playwright/Chromium versions and
// machines, and losing the race means the last-tab auto-create below
// never runs, leaving the manager with zero tabs.
const wasActive = tabId === this.activeTabId;
await page.close();
this.pages.delete(tabId);
this.tabSessions.delete(tabId);
this.tabOwnership.delete(tabId);
// Switch to another tab if we closed the active one
if (tabId === this.activeTabId) {
if (wasActive) {
const remaining = [...this.pages.keys()];
if (remaining.length > 0) {
this.activeTabId = remaining[remaining.length - 1];
} else {
if (remaining.length === 0) {
// No tabs left — create a new blank one
await this.newTab();
} else if (!this.pages.has(this.activeTabId)) {
// The 'close' handler may have already switched to a valid tab;
// only reassign when activeTabId no longer points at a live tab.
this.activeTabId = remaining[remaining.length - 1];
}
}
}