Skip to content

fix(browser): keep webview guests alive across worktree switches#6408

Merged
AmethystLiang merged 2 commits into
stablyai:mainfrom
Jingyue-Wu:fix/browser-worktree-switch-no-reload
Jun 26, 2026
Merged

fix(browser): keep webview guests alive across worktree switches#6408
AmethystLiang merged 2 commits into
stablyai:mainfrom
Jingyue-Wu:fix/browser-worktree-switch-no-reload

Conversation

@Jingyue-Wu

Copy link
Copy Markdown
Contributor

Electron <webview> guests are destroyed when their DOM parent is removed. When switching worktrees, BrowserPane chrome unmounts and previously tore down the webview, forcing a reload on return.

Introduce persistent page viewports inside overlay slots so webviews stay in a stable parent without keeping the full React chrome mounted. On worktree switch, park hidden viewports instead of destroying guests; explicit close paths still call destroyPersistentWebview.

Fixes #6385

Summary

  • Fix: Browser tabs no longer reload when switching between worktrees/projects. A page you left open (e.g. a long-running QA session) keeps its loaded document, scroll position, and in-page state when you switch away and back.
  • How: Each browser page gets a persistent viewport shell under its worktree-level overlay slot. The <webview> guest lives in that stable DOM parent and is registered in the existing webviewRegistry. When BrowserPane chrome unmounts on worktree switch, the viewport is parked (display: none) instead of destroyed. React chrome (toolbar, address bar, find bar, overlays) continues to mount/unmount normally via shouldMountPane.
  • Why not “keep everything mounted”: We deliberately do not keep the full BrowserPane React subtree mounted for inactive worktrees. That caused performance issues (React reconciliation, event listeners, layout work for every hidden browser). Only the lightweight viewport shell + webview guest persist; the heavy chrome unmounts.
  • Performance tradeoff: Inactive worktrees still hold live webview guests (same memory/compositor cost as an open tab in a normal browser). We avoid the extra cost of repeatedly destroying/recreating guests and re-running full page loads. Hidden guests are parked and non-interactive (display: none, inert, pointer-events: none).

Screenshots

Behavior change only, same browser UI as before. Verified on macOS that pages render correctly after the fix (Google homepage and loaded sites display in the browser pane; no blank/white regression).

No visual change to layout or chrome.

image

Testing

  • pnpm lint
  • pnpm typecheck
  • pnpm test — browser-pane suite: 112 tests passed (src/renderer/src/components/browser-pane)
  • pnpm build — not run end-to-end in this session
  • Added or updated high-quality tests that would catch regressions, or explained why tests were not needed

Manual verification (macOS, local dev):

  • Open browser tab, load a real page (google.com / custom site).
  • Switch to another worktree, then switch back.
  • Confirmed via Electron CDP: same getWebContentsId() before and after switch (no guest recreation); URL and title preserved.
  • Confirmed viewport shell is visible and sized when active (display: flex, non-zero container rect).

New tests: browser-page-viewport.test.ts covers viewport creation under slot root, chrome inset sync, layout show/hide, and park behavior.

AI Review Report

Reviewed the full diff with focus on Electron webview lifecycle, overlay-slot anchoring, and worktree switch paths.

Risks checked:

  • Webview reparenting: Electron destroys guests on DOM parent change. Viewports are created once under the overlay slot root and never moved; destroyPersistentWebview only runs on explicit close.
  • Blank-page regression: Initial navigation must happen while the viewport shell is visible. applyBrowserPageViewportLayout is called in the webview lifecycle effect before webview.src is set, and again in a useLayoutEffect keyed on slotViewportReady.
  • Focus on macOS: moveFocusToRendererBeforeWebviewDetach runs before parking to avoid macOS reactivating the previous app when a focused webview is hidden.
  • Drag/drop: File drop handlers moved to native listeners on the persistent container via refs so they survive chrome unmount.
  • Overlay stacking: Page overlays (find bar, failure UI, annotations) portal into the persistent container; toolbar stays in React chrome with pointer-events-auto.

Cross-platform (macOS, Linux, Windows):

  • No platform-specific code added. Uses standard DOM APIs (ResizeObserver, inert, display, pointer-events) available in Electron’s Chromium on all platforms.
  • No keyboard shortcut, menu accelerator, or path-handling changes.
  • Electron <webview> OOPIF behavior is the same across Orca’s desktop targets; the persistent-parent pattern applies uniformly.
  • Remote/SSH browser path (RemoteBrowserPagePane) is untouched.

Not flagged / verified:

  • BrowserPaneOverlayLayer slot viewport ref registration is keyed by workspace tab id, matching existing overlay architecture.
  • webview-registry.ts teardown now also removes orphaned viewport shells on explicit close.

Security Audit

  • No new IPC surface. Existing registerGuest / unregisterGuest paths unchanged.
  • No command execution or shell invocation added.
  • No auth/secrets handling changed.
  • DOM/event handling: Native drag/drop listeners on the viewport container only handle existing workspace file MIME types; same logic as before, moved to survive unmount.
  • Input isolation: Parked/inactive viewports use inert, pointer-events: none, and display: none so hidden guests cannot receive input.
  • No new dependencies.

No security follow-up identified.

Electron <webview> guests are destroyed when their DOM parent is removed.
When switching worktrees, BrowserPane chrome unmounts and previously tore
down the webview, forcing a reload on return.

Introduce persistent page viewports inside overlay slots so webviews stay
in a stable parent without keeping the full React chrome mounted. On
worktree switch, park hidden viewports instead of destroying guests;
explicit close paths still call destroyPersistentWebview.

Fixes stablyai#6385

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e5293677-ed2f-4f72-bb49-80da7b2f5992

📥 Commits

Reviewing files that changed from the base of the PR and between 0026899 and 4258aec.

📒 Files selected for processing (4)
  • src/renderer/src/components/browser-pane/BrowserPane.tsx
  • src/renderer/src/components/browser-pane/BrowserPaneOverlayLayer.tsx
  • src/renderer/src/components/browser-pane/browser-page-viewport.test.ts
  • src/renderer/src/components/browser-pane/browser-page-viewport.ts

📝 Walkthrough

Walkthrough

Adds persistent browser page viewport management for overlay slots. The renderer now registers slot viewport elements, creates and parks page viewport shells, applies viewport layout and chrome inset sizing, and coordinates BrowserPane webview setup, drag/drop handling, and overlay rendering inside the viewport container. Persistent webview destruction now removes the associated viewport. New tests cover viewport creation, layout, chrome inset syncing, and parking.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the core fix: preserving webview guests across worktree switches.
Description check ✅ Passed The description includes the required sections and most requested details, including screenshots, testing, review, security, and platform checks.
Linked Issues check ✅ Passed The changes directly address #6385 by preventing browser refreshes on worktree switches and preserving webview state.
Out of Scope Changes check ✅ Passed The added viewport, lifecycle, tests, and teardown changes all support the stated browser persistence fix.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/renderer/src/components/browser-pane/BrowserPane.tsx (1)

5718-5737: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use semantic style tokens for the grab toast colors.

This new UI hard-codes white, gray, red, and blue values. Please map these to existing semantic tokens/classes so the toast follows theme and dark-mode styling. As per coding guidelines, “Never invent new color values … when a documented one in STYLEGUIDE.md already covers the role.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a9c7230e-b086-464a-8a60-2179b9803632

📥 Commits

Reviewing files that changed from the base of the PR and between dd0fa77 and 0026899.

📒 Files selected for processing (5)
  • src/renderer/src/components/browser-pane/BrowserPane.tsx
  • src/renderer/src/components/browser-pane/BrowserPaneOverlayLayer.tsx
  • src/renderer/src/components/browser-pane/browser-page-viewport.test.ts
  • src/renderer/src/components/browser-pane/browser-page-viewport.ts
  • src/renderer/src/components/browser-pane/webview-registry.ts

Comment thread src/renderer/src/components/browser-pane/BrowserPane.tsx
Comment thread src/renderer/src/components/browser-pane/BrowserPane.tsx
@AmethystLiang AmethystLiang self-assigned this Jun 26, 2026
@AmethystLiang AmethystLiang merged commit 19ab395 into stablyai:main Jun 26, 2026
@AmethystLiang

Copy link
Copy Markdown
Contributor

thx for the PR! now fixed in the 1.4.101 version

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.

[Bug]: Regressions with the browser - when switching workspaces causes a refresh

2 participants