Repository navigation
Fix browser WebView teardown memory leak (#2962) - #4013
austinywang wants to merge 9 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Looking for one thing? Review this PR in Change Stack to search files, summaries, diffs, and code without losing your place. 📝 WalkthroughWalkthroughBrowserPanel.close() is refactored to enforce idempotent one-shot teardown by introducing a private ChangesBrowserPanel Teardown Hardening
Possibly related PRs
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 18 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (18 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR fixes a memory leak when closing
Confidence Score: 5/5Safe to merge — teardown ordering is correct, all handler removals are guarded against unbound panels, and the placeholder swap is coherent with the webViewInstanceID invalidation that rejects in-flight callbacks. The changes are well-scoped to the close/teardown path, previous-thread issues (downloadDelegate leak, isClosed invariant comment, removeScriptMessageHandler crash) have all been addressed in this revision, and the two regression tests directly exercise the portal-detach and idempotent-teardown cases. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Caller
participant BrowserPanel
participant releaseWebViewForTeardown
participant BrowserWindowPortalRegistry
participant OldWebView as Old WKWebView
participant Placeholder as Placeholder CmuxWebView
Caller->>BrowserPanel: close()
BrowserPanel->>BrowserPanel: "guard !isClosed → isClosed = true"
BrowserPanel->>BrowserPanel: unfocus() (resign first responder)
BrowserPanel->>BrowserPanel: "closedWebView = webView"
BrowserPanel->>releaseWebViewForTeardown: releaseWebViewForTeardown(closedWebView)
releaseWebViewForTeardown->>OldWebView: window.makeFirstResponder(nil) if still responder
releaseWebViewForTeardown->>BrowserWindowPortalRegistry: detach(webView)
releaseWebViewForTeardown->>BrowserWindowPortalRegistry: discard(webView, preserveCurrentSuperview: false)
releaseWebViewForTeardown->>OldWebView: stopLoading()
releaseWebViewForTeardown->>OldWebView: "navigationDelegate = nil"
releaseWebViewForTeardown->>OldWebView: "uiDelegate = nil"
releaseWebViewForTeardown->>OldWebView: removeAllUserScripts()
releaseWebViewForTeardown->>OldWebView: teardownReactGrabMessageHandler (guarded)
releaseWebViewForTeardown->>OldWebView: teardownMediaPlaybackMessageHandler (guarded)
releaseWebViewForTeardown->>OldWebView: clear CmuxWebView callbacks
releaseWebViewForTeardown->>OldWebView: removeFromSuperview()
BrowserPanel->>Placeholder: "makeWebView() → allowsFirstResponderAcquisition = false"
BrowserPanel->>BrowserPanel: "webViewInstanceID = UUID()"
BrowserPanel->>BrowserPanel: "webView = Placeholder"
BrowserPanel->>BrowserPanel: nil navigationDelegate, uiDelegate, downloadDelegate
BrowserPanel->>BrowserPanel: reset loading/download/portal/ReactGrab state
Reviews (6): Last reviewed commit: "fix: guard browser script handler teardo..." | Re-trigger Greptile |
|
Reviewed the latest bot feedback after c7a7521. Greptile's latest item is a safe-to-merge summary with "No files require special attention," so no code change is needed. CodeRabbit's latest item is a review-rate-limit/usage notice rather than actionable code feedback; the CodeRabbit check itself is green. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a79a0c3. Configure here.

Summary
Testing
Closes #2962
Note
Medium Risk
Changes WebKit lifecycle and portal teardown on a critical close path; behavior is guarded and covered by new tests but mistakes could cause crashes or lingering WebKit callbacks.
Overview
Fixes a memory leak on browser panel close (#2962) by tearing down the live
WKWebViewimmediately instead of leaving it portal-hosted with delegates and script handlers still attached.close()is now one-shot (isClosedguard). It runsreleaseWebViewForTeardownon the old web view—clear first responder, portal detach/discard, stop loading, nil delegates, strip user scripts,teardownReactGrabMessageHandler/teardownMediaPlaybackMessageHandler, clearCmuxWebViewcontext-menu callbacks, remove from superview—theninstallClosedPlaceholderWebView(inert replacement, no first responder). Panel state is reset more aggressively (download/loading UI, portal leases, ReactGrab, etc.).New
teardown*MessageHandlerhelpers unregister WebKit handlers and clear panel-owned handler references (media playback also resets tracking).Tests assert portal detach and handler removal on close, and that
close()is safe if handlers were already torn down.Reviewed by Cursor Bugbot for commit 6dae266. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Fixes a memory leak when closing browser panels by tearing down the portal-hosted
WKWebViewimmediately and installing a non-interactive placeholder. Adds one-shot close guarding and idempotent script handler teardown to avoid late callbacks and leaks. Closes #2962.ReactGrab/media handlers, nil navigation/UI/download delegates, clearCmuxWebViewcallbacks, disable first-responder, remove from superview.isClosed; install a placeholder; reset state (cancel favicon/loading work, observers/cancellables, delegates, downloads/progress/loading flags,shouldRenderWebView, portal leases/locks,ReactGraband media playback tracking).Written for commit 6dae266. Summary will update on new commits.
Summary by CodeRabbit
Bug Fixes
Tests