Skip to content

fix(boot): sequence openCurrentPage after restoreSession to avoid duplicate windows#218

Merged
epeicher merged 1 commit into
trunkfrom
fix/boot-race-duplicate-window
May 15, 2026
Merged

fix(boot): sequence openCurrentPage after restoreSession to avoid duplicate windows#218
epeicher merged 1 commit into
trunkfrom
fix/boot-race-duplicate-window

Conversation

@epeicher
Copy link
Copy Markdown
Collaborator

@epeicher epeicher commented May 15, 2026

Summary

Reported by Roberto: in Incognito with a portal-intent URL (?desktop_mode_portal=1&desktop_mode_portal_intent=1) and a saved session, two Dashboard windows opened; the second one's spinner never resolved.

Root cause is a boot-time race in src/desktop.ts:

  • restoreSession and openCurrentPage were fired concurrently (both void …).
  • manager.open()'s dedupe-by-baseId check (src/window-manager/index.ts:406) runs synchronously, before createWindow() awaits ensureWindowSystemLoaded. So both calls pass the existing-check while the first window is still mid-await on the lazy bundle.
  • Result: two Window instances constructed with the same id/baseId. The chromeless bridge handshakes by id and only finishes one of them; the other hangs on the spinner forever.

Fix

Sequence them. openCurrentPage chains off the same promise restoreSession resolves. If the page the user asked for is already in the restored stack, manager.open() finds and focuses it; otherwise a fresh window opens on top — exactly what the existing case-2 comment already promised ("the page they asked for opens on top of the restored stack").

No new manager-level state. The change is one block in init().

Test plan

  • Incognito, log in, hit …/wp-admin/index.php?desktop_mode_portal=1&desktop_mode_portal_intent=1 with Dashboard in the saved session → exactly one Dashboard window opens, spinner resolves.
  • Fresh user with no saved session, direct admin URL (fromPortal=false) → window opens normally.
  • Bare /desktop-mode/ visit (fromPortal=true, fromPortalIntent=false, session exists) → session restores, no extra window.
  • npm run lint, tsc --noEmit, npm run test:js (1214 tests) all pass.

…licate windows

`restoreSession` and `openCurrentPage` were fired concurrently at the
end of `init()`. `manager.open()`'s dedupe-by-baseId check happens
before `createWindow()` awaits the lazy `window-system[.min].js`
bundle, so two concurrent opens for the same window (e.g. portal
intent + saved Dashboard) both pass the check and construct
separate Window instances. The user sees two copies of the same
window; the second one's iframe never finishes because the
chromeless bridge only handshakes with one instance per id.

Sequence them: openCurrentPage chains off the same promise restore
runs on. The existing comment on case 2 already says "the page they
asked for opens on top of the restored stack" — that's what
sequencing actually delivers. If the page is in the restored stack,
`manager.open()` finds and focuses it; otherwise it opens fresh on
top.
@epeicher epeicher enabled auto-merge (squash) May 15, 2026 08:02
@github-actions
Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@epeicher epeicher merged commit b6419dd into trunk May 15, 2026
4 of 9 checks passed
@epeicher epeicher deleted the fix/boot-race-duplicate-window branch May 15, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant