Skip to content

fix(browser): keep annotate session alive across iframe loads - #24032

Merged
nwparker merged 1 commit into
stablyai:mainfrom
Brant-lzh:fix/browser-annotate-session-stuck-on-guest-load
Oct 26, 2026
Merged

nwparker merged 1 commit into
stablyai:mainfrom
Brant-lzh:fix/browser-annotate-session-stuck-on-guest-load

Conversation

@Brant-lzh

@Brant-lzh Brant-lzh commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

When you annotate a page in Orca's browser and that page quietly reloads an iframe in the background (ads, chat widgets, dashboards that poll), Orca thought the whole page had reloaded. It closed your half-written note, stopped grab mode, and hid your saved note markers. Now only a real page navigation or reload resets the annotate session.

What Changed

  • Before: the webview's did-start-loading handler invalidated the annotation document and reset the overlay viewport. Electron fires did-start-loading for subframe loads too, so a page that reloads an iframe on each poll tore down the annotate session every cycle: the pending card closed, grab mode was cancelled, and saved-note markers were retired.
  • After: the did-start-loading listener is gone. The main-frame did-start-navigation handler already invalidates the annotation document for every document replacement (including same-URL reloads) and for same-document navigation. The overlay viewport reset now runs next to that invalidation. Iframe-only loads no longer touch the annotate session.
  • Tests that went through handleDidStartLoading now drive handleDidStartNavigation instead. The same-URL reload test still checks that saved notes are kept and capture is cancelled.

Why

did-start-loading cannot tell main-frame loads apart from subframe loads. did-start-navigation carries isMainFrame, and an event-order probe on Electron 43.8.1 shows it fires on the main frame for every case the old handler was there to cover: location.reload(), webview.reload(), loadURL, hash and pushState changes, and 204/download navigations. Iframe reloads, src swaps, and appended iframes emit only main=false. The original PR kept did-start-loading and added subframe filtering on top. This version drops the redundant signal and reuses the one that is already correct.

Linked Issue

Fixes #23988

Visual Proof

N/A for screenshots: an iframe reloading looks the same on screen before and after this change. What differs is whether the annotate session gets torn down, so the proof is an event trace plus a regression probe.

Electron 43.8.1 webview event order (excerpt):

--- iframe-reload
did-start-loading
did-start-navigation main=false /frame
did-stop-loading
--- location.reload
did-start-loading
did-start-navigation main=true /pushed
did-navigate /pushed
--- hash
did-start-loading
did-start-navigation main=true /#sec
did-navigate-in-page main=true /#sec

Listener probe ("iframe reload/src-swap does not invalidate the annotate session"):

=== MAIN ===    × invalidate called 3 times   (1 failed | 1 passed)
=== BRANCH ===  Tests 2 passed (2)

Testing

  • Reproduced on macOS with a listener probe: on main, iframe reloads, src swaps, and appended iframes each invalidate the annotate session. On this branch they do not, and main-frame reload and navigation still invalidate it.

  • pnpm test src/renderer/src/components/browser-pane/host-guest/: 24 files, 190 tests pass.

  • oxlint, oxfmt, the anti-slop, code-quality, and ast-grep gates, and the changed-code quality gate are all clean on the touched files.

  • Platforms: renderer-only webview event wiring with no OS-specific paths. It behaves the same on macOS, Linux, and Windows, and for SSH/remote and folder workspaces.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Review

Original fix by @Brant-lzh in #24032.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Renderer-only change to webview listener wiring. No IPC, protocol, or persisted-state changes, so mixed client/server versions are unaffected. It also does less work: one fewer listener, and no re-render on every subframe load.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Refactors annotation session lifecycle during page navigation.

The PR appears safe to merge; no new blocking issue was established.

Summary

The PR moves annotation teardown from any guest load to main-frame navigation commit and adds tests for main-frame, in-page, subframe, and loading-only paths.

  • Subframe loads retain the annotation session.
  • Main-frame commits clear annotations and invoke the grab-session reset.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Webview event] --> B{Main-frame did-navigate?}
  B -- Yes --> C[Clear annotations and viewport]
  C --> D[Cancel grab session]
  B -- No --> E[Keep annotation session]
Loading

Reviews (2) · Last reviewed commit: "test(browser): pin annotate-session tear..."

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 5 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ddabe30b-6a42-4075-b9b9-04b815cf4ef3
Commits

Reviewing files that changed from the base of the PR and between 90b3f6f and 3182fed.

Files selected for processing: 6
  • src/renderer/src/components/browser-pane/host-guest/bind-browser-page-webview-listeners.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-annotation-document.test.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-navigation.test.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-mount-admission.test.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-webview-loading-handlers.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-webview-navigation-handlers.ts

No actionable comments were generated in the recent review.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: b3dd4070-d728-4eed-8c4b-1262f59d7374

📥 Commits

Reviewing files that changed from the base of the PR and between 1de6ccf and 90b3f6f.

📒 Files selected for processing (1)
  • src/renderer/src/components/browser-pane/host-guest/browser-page-annotation-session-teardown.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Walkthrough

The grab annotation hook now exposes a cancellation callback, which the browser pane passes to webview navigation handling. Full main-frame navigation commits clear page annotations, reset the overlay viewport, and cancel the grab session. Loading-start handling no longer performs annotation cleanup.

Priority: Normal

Severity of issue fixed: Medium

Merge Risk: Minimal · previous review 90b3f

The change preserves annotation sessions during subframe loads and tears them down on main-frame navigation. No actionable merge-blocking risk is identified; merge after normal CI checks.

Security architecture risk: Low · up to 90b3f

The change is confined to a browser tab’s annotation session. No new security exposure was identified, and stale capture results are rejected after cancellation. Assurance remains limited around completion of asynchronous cleanup.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A guest page can cause top-level navigation and trigger annotation teardown and grab cancellation for its browser tab. The inspected path does not supply guest-chosen asset identifiers or add credential, filesystem, or cross-service authority.

Trust Boundaries and Controls

  • observed — Document-replacement authority comes from the host webview navigation event, not the test helper or a newly exposed guest command. The teardown guard excludes explicitly identified subframes, and same-document navigation uses a separate path without teardown.

Resilience and Maintainability Implications

  • observed — The existing delivery cleanup removes captured annotation objects by identity rather than clearing all notes for the page. A delivery completing after navigation therefore preserves replacement annotations added afterward.
Pre-merge checks: Passed 4 · Failed 1

Failed 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%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed The changes satisfy the coding requirements in #23988. handleDidStartLoading no longer tears down annotation state. handleFullDidNavigate tears down the session only for main-frame commits by clea…
Out of Scope Changes check Passed The changes remain within #23988. They update annotation-session cancellation and navigation/loading wiring for the local BrowserPagePane, plus focused test fixtures and automated tests. The changes…
Title check Passed The title clearly and concisely describes the main behavior change: preserving the annotation session during iframe loads.
Description check Passed The description is complete and follows the repository template. It explains the user impact, implementation, rationale, linked issue, testing, scope, and checklist status.
Full details: Linked Issues check

Explanation

The changes satisfy the coding requirements in #23988. handleDidStartLoading no longer tears down annotation state. handleFullDidNavigate tears down the session only for main-frame commits by clearing annotations, resetting the overlay viewport, and calling cancelGrabSessionRef. In-page and subframe navigation preserve the session. browser-page-annotation-session-teardown.test.ts covers main-frame cleanup, in-page navigation, subframe navigation and loading, and did-start-loading alone.

Full details: Out of Scope Changes check

Explanation

The changes remain within #23988. They update annotation-session cancellation and navigation/loading wiring for the local BrowserPagePane, plus focused test fixtures and automated tests. The changes do not modify remote or client-hosted browser panes, workspace previews, or unrelated browsing flows.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

🧹 Nitpick comments (1)
src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts (1)

42-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add focused tests for navigation teardown.

The changed harness provides teardown callbacks, but it only invokes loading and navigation-start handlers. It never invokes handleDidNavigate or asserts the teardown callbacks. Add cases for main-frame teardown and for subframe, in-page, and loading-start-only events that must preserve the session.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 116acfe5-1d99-4890-a474-fbb04737176b

📥 Commits

Reviewing files that changed from the base of the PR and between d414033 and 1de6ccf.

📒 Files selected for processing (8)
  • src/renderer/src/components/browser-pane/annotate/use-browser-page-grab-annotations.ts
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-page-pane.tsx
  • src/renderer/src/components/browser-pane/host-guest/attach-browser-page-webview.ts
  • src/renderer/src/components/browser-pane/host-guest/bind-browser-page-webview-listeners.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-webview-loading-handlers.ts
  • src/renderer/src/components/browser-pane/host-guest/browser-page-webview-navigation-handlers.ts
  • src/renderer/src/components/browser-pane/host-guest/use-browser-page-webview-lifecycle.ts
💤 Files with no reviewable changes (1)
  • src/renderer/src/components/browser-pane/host-guest/browser-page-webview-loading-handlers.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@pullfrog pullfrog 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.

Important

The change itself is sound and I found no behavioral bug — but the PR's Testing section describes a new unit test that is not present in the branch. Reconcile that before merge (add the test, or correct the description).

Reviewed changes — the full local BrowserPagePane annotate-teardown fix against main, verified end-to-end (diff, wiring, and the Electron event contract):

  • Teardown moved from did-start-loading to did-navigate — the annotate clears now run on the main-frame commit signal instead of every load, so subframe/iframe polling no longer destroys the session (browser-page-webview-navigation-handlers.ts:149).
  • Full session reset via cancelGrabSessionRef — new ref owned by use-browser-page-grab-annotations.ts:104 clears the pending card and calls grab.cancel() when not idle, so no half-torn-down state survives (use-browser-page-grab-annotations.ts:115).
  • Loading handler de-cluttered — handleDidStartLoading now only reports loading: true (browser-page-webview-loading-handlers.ts:65); the removed props are unwound through the attach/lifecycle/bind chain.
  • Test harness args updated — browser-page-favicon-retention.test.ts:43 follows the moved props (passes locally, 6/6).

I verified the load-bearing premise against Electron 43.7.5 (the pinned version) rather than assuming it: did-start-loading does fire for iframe loads (electron#30479), and did-navigate fires only on a main-frame document commit — never for subframes (did-frame-navigate) or same-document changes (did-navigate-in-page). The fix does what it claims.

⚠️ Testing section references a test file that is not in the PR

The Testing section states a new browser-page-annotation-session-teardown.test.ts was added and mutation-checked ("re-adding the old clear inside handleDidStartLoading turns two of the tests red"). That file is not in the branch — it is absent from git ls-files and the working tree is clean. The only test touched is browser-page-favicon-retention.test.ts, whose harness never dispatches handleFullDidNavigate, so the central subframe-vs-main-frame teardown behavior currently has no automated coverage.

Technical details
# Missing test file and coverage gap

## Affected sites
- PR description "## Testing" → "Unit: new `browser-page-annotation-session-teardown.test.ts`" — file does not exist in the branch.
- `src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts:64-72` — `navigateTo` calls `handleDidStartLoading` + `handleDidStartNavigation` only; `handleFullDidNavigate` (the new teardown path) is never exercised.

## Required outcome
- Either commit the described test (asserting: main-frame `did-navigate` clears annotations, resets the viewport, and fires `cancelGrabSessionRef`; `did-navigate-in-page`, subframe, and `did-start-loading`-only paths clear nothing), or update the Testing section to reflect the actual coverage.

## Open questions for the human
- Was `browser-page-annotation-session-teardown.test.ts` intended to be part of this PR and dropped from the commit?

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes — the incremental delta since the prior pullfrog review (1de6ccf → 90b3f6ff0a, test-only):

  • Added the missing teardown regression test — browser-page-annotation-session-teardown.test.ts now drives a main-frame did-navigate commit and asserts the annotation clears, the viewport resets, and the grab session resets; it also proves the in-page, subframe, and did-start-loading-only paths clear nothing. This closes the prior review's only finding.
  • Favicon harness re-pointed at the moved props — browser-page-favicon-retention.test.ts passes clearBrowserPageAnnotationsRef/cancelGrabSessionRef to the navigation factory instead of the loading factory.

I mutation-checked the new test: deleting the cancelGrabSessionRef.current() call from handleFullDidNavigate turns the first test red (expected 1 calls, got 0), so the coverage is real rather than theatre. New file runs 4/4 under the project config.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@nwparker
nwparker force-pushed the fix/browser-annotate-session-stuck-on-guest-load branch from 90b3f6f to a85a067 Compare October 26, 2026 21:39
@nwparker nwparker changed the title fix(browser): keep annotate session alive across subframe loads and reset it fully on main-frame navigation fix(browser): keep annotate session alive across iframe loads Oct 26, 2026
did-start-loading fires for subframe loads too, so a page that reloads an
iframe on each poll invalidated the annotation document every cycle: the
pending card closed, grab mode was cancelled and saved-note markers were
retired. Main-frame did-start-navigation already invalidates for every
document replacement (including same-URL reloads) and same-document
navigation, so drop the did-start-loading handler and move the overlay
viewport reset next to that invalidation.

Fixes stablyai#23988

Co-authored-by: Brant <562916493@qq.com>
@nwparker
nwparker force-pushed the fix/browser-annotate-session-stuck-on-guest-load branch from a85a067 to 3182fed Compare October 26, 2026 22:27
@nwparker
nwparker merged commit 3f256ad into stablyai:main Oct 26, 2026
33 checks passed
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]: Annotate card vanishes and annotate mode hangs (Esc-only exit) while a page's timed requests trigger loads

2 participants