Skip to content

Fix bilibili search popup opening detached window#2836

Open
austinywang wants to merge 2 commits intomainfrom
issue-2834-bilibili-search-popup
Open

Fix bilibili search popup opening detached window#2836
austinywang wants to merge 2 commits intomainfrom
issue-2834-bilibili-search-popup

Conversation

@austinywang
Copy link
Copy Markdown
Contributor

@austinywang austinywang commented Apr 12, 2026

Summary

  • route user-triggered .other nil-target browser navigations back to tab fallback instead of detached popup windows
  • preserve scripted popup behavior for non-gesture OAuth/opener flows
  • add regression coverage for key-driven bilibili-style popup submits

Closes #2834


Note

Medium Risk
Changes WebKit popup/new-tab classification for .other navigations based on detected user gestures, which could affect edge-case window.open/OAuth flows or form submissions if misclassified.

Overview
Refines popup vs tab behavior for WebKit .other navigations by introducing browserNavigationHasExplicitUserGesture (mouse/button/key events) and using it to avoid creating popup windows for user-triggered actions, reserving popups for likely scripted opener flows.

Updates nil-target handling so .other navigations with an explicit user gesture now fall back to the new-tab path, and expands DEBUG logging in navigation/popup delegates to include request method/URL, target-frame info, and WKWindowFeatures. Adds regression tests covering key-driven .other navigations for both popup creation and nil-target fallback.

Reviewed by Cursor Bugbot for commit 4c837b3. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes bilibili search opening a detached popup by routing user-initiated .other navigations (e.g., key-driven submits) to a tab instead, closing #2834. Scripted/OAuth popups remain unchanged.

  • Bug Fixes
    • Route .other nil-target navigations with a user gesture (key or click) to open in a tab instead of a popup.
    • Preserve scripted popups for OAuth/opener flows so window.opener/postMessage keep working.
    • Add regression tests for key-driven submit behavior; improve debug logs for createWebView/decidePolicy.

Written for commit 4c837b3. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Improved popup window creation controls with stricter event type validation.
    • Enhanced fallback behavior for navigation targets based on event context.
    • Added comprehensive debug logging for navigation requests and popup operations.
  • Tests

    • Added unit tests to verify popup and navigation fallback behavior.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 12, 2026 3:04am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

These changes add event type classification and gesture-aware navigation routing to the browser panel. They introduce browserNavigationHasExplicitUserGesture() to identify keyboard and mouse events, update popup creation logic to reject popups when explicit user gestures occur, modify nil-target fallback logic to accept optional event type parameters, and expand debug logging with request metadata and window features.

Changes

Cohort / File(s) Summary
Navigation Gesture Detection & Routing
Sources/Panels/BrowserPanel.swift
Added browserNavigationHasExplicitUserGesture(currentEventType:) helper; updated browserNavigationShouldCreatePopup(...) to reject popup creation for explicit user gestures; updated browserNavigationShouldFallbackNilTargetToNewTab(...) signature to accept optional currentEventType parameter and incorporate gesture detection into decision logic.
Gesture-Based Navigation Tests
cmuxTests/BrowserConfigTests.swift
Added two unit tests validating .other navigation type behavior with .keyDown events: one confirming popup rejection, another confirming nil-target fallback to new tab.

Sequence Diagram

sequenceDiagram
    actor User
    participant NavigationDelegate as Navigation Delegate
    participant GestureDetector as Gesture Detector
    participant UIDelegate as UI Delegate
    participant Router as Routing Logic

    User->>NavigationDelegate: Navigation Action Triggered
    NavigationDelegate->>GestureDetector: Check currentEventType
    GestureDetector-->>NavigationDelegate: Explicit Gesture? (keyDown/mouse)
    
    alt navigationType == .other
        NavigationDelegate->>Router: Check popup creation rules
        Router->>Router: Has explicit gesture?
        alt Explicit gesture present
            Router-->>NavigationDelegate: Reject popup creation
            NavigationDelegate->>UIDelegate: Fallback to new tab
        else No explicit gesture
            Router-->>NavigationDelegate: Allow popup creation
            NavigationDelegate->>UIDelegate: Create popup window
        end
    else navigationType != .other
        NavigationDelegate->>Router: Check nil-target fallback
        Router->>GestureDetector: Has explicit gesture?
        alt Explicit gesture present
            Router-->>NavigationDelegate: Fallback to new tab
        else No explicit gesture
            Router-->>NavigationDelegate: Use standard handling
        end
    end
    
    UIDelegate-->>User: Navigate in appropriate context
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A hop, a skip, a gesture caught—
No popups where the user sought!
With keystroke keen and click so true,
We route to tabs both old and new.
The form submits, yet stays in sight,
A browser pane, forever bright! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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 'Fix bilibili search popup opening detached window' clearly summarizes the main change—fixing a bug where bilibili search form submission opens a detached popup instead of staying in the pane.
Description check ✅ Passed The description includes a summary of changes and why (routing user-triggered .other navigations to tabs, preserving scripted popups, adding regression tests), but lacks explicit sections for Testing and Demo Video as specified in the template.
Linked Issues check ✅ Passed The changes directly address issue #2834 by implementing detection of explicit user gestures in navigation events and routing user-triggered .other navigations to tabs instead of popups, while preserving scripted popup behavior.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the bilibili search popup issue: added gesture detection, updated popup/fallback logic, added regression tests, and expanded debug logging—all aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-2834-bilibili-search-popup

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/Panels/BrowserPanel.swift`:
- Around line 5955-5959: The current check in BrowserPanel.swift (the if using
isUserNewTab and browserNavigationHasExplicitUserGesture(currentEventType:)) is
too broad and downgrades legitimate user-initiated popups; restrict the
gesture-based tab fallback to only the form-submit/same-origin case. Replace the
use of browserNavigationHasExplicitUserGesture(...) with a focused helper (e.g.,
browserNavigationIsUserFormSubmitSameOrigin(currentEventType:)) or add an
explicit condition that detects same-origin form submit/new-tab semantics, and
only return false when isUserNewTab ||
browserNavigationIsUserFormSubmitSameOrigin(...) is true; leave other
explicit-gesture paths (OAuth/payment popups) to continue opening as popups so
window.opener/postMessage flows are preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e8182d55-a3d0-478f-afe5-ee66e1eed138

📥 Commits

Reviewing files that changed from the base of the PR and between 44b31eb and 4c837b3.

📒 Files selected for processing (2)
  • Sources/Panels/BrowserPanel.swift
  • cmuxTests/BrowserConfigTests.swift

Comment on lines +5955 to +5959
// User-triggered new-window requests from form submits / clicks should
// reuse tab semantics; leave popup windows for scripted opener flows.
if isUserNewTab || browserNavigationHasExplicitUserGesture(currentEventType: currentEventType) {
return false
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t suppress all user-gesture .other popups.

This blanket check can downgrade legitimate user-initiated OAuth/payment popups to tabs, which may break window.opener/postMessage flows. Restrict gesture-based tab fallback to the bilibili-like submit case (e.g., same-origin form-submit heuristics), not all explicit gestures.

💡 Suggested direction
 func browserNavigationShouldCreatePopup(
     navigationType: WKNavigationType,
     modifierFlags: NSEvent.ModifierFlags,
     buttonNumber: Int,
     hasRecentMiddleClickIntent: Bool = false,
     currentEventType: NSEvent.EventType? = NSApp.currentEvent?.type,
-    currentEventButtonNumber: Int? = NSApp.currentEvent?.buttonNumber
+    currentEventButtonNumber: Int? = NSApp.currentEvent?.buttonNumber,
+    allowGestureFallbackToTab: Bool = false
 ) -> Bool {
@@
-    if isUserNewTab || browserNavigationHasExplicitUserGesture(currentEventType: currentEventType) {
+    if isUserNewTab {
+        return false
+    }
+    if allowGestureFallbackToTab &&
+        browserNavigationHasExplicitUserGesture(currentEventType: currentEventType) {
         return false
     }
     return true
 }
+let sameOrigin = navigationAction.request.url?.host == webView.url?.host
+let allowGestureFallbackToTab = (navigationAction.navigationType == .other) && sameOrigin
 let isScriptedPopup = browserNavigationShouldCreatePopup(
     navigationType: navigationAction.navigationType,
     modifierFlags: navigationAction.modifierFlags,
     buttonNumber: navigationAction.buttonNumber,
-    hasRecentMiddleClickIntent: CmuxWebView.hasRecentMiddleClickIntent(for: webView)
+    hasRecentMiddleClickIntent: CmuxWebView.hasRecentMiddleClickIntent(for: webView),
+    allowGestureFallbackToTab: allowGestureFallbackToTab
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Panels/BrowserPanel.swift` around lines 5955 - 5959, The current
check in BrowserPanel.swift (the if using isUserNewTab and
browserNavigationHasExplicitUserGesture(currentEventType:)) is too broad and
downgrades legitimate user-initiated popups; restrict the gesture-based tab
fallback to only the form-submit/same-origin case. Replace the use of
browserNavigationHasExplicitUserGesture(...) with a focused helper (e.g.,
browserNavigationIsUserFormSubmitSameOrigin(currentEventType:)) or add an
explicit condition that detects same-origin form submit/new-tab semantics, and
only return false when isUserNewTab ||
browserNavigationIsUserFormSubmitSameOrigin(...) is true; leave other
explicit-gesture paths (OAuth/payment popups) to continue opening as popups so
window.opener/postMessage flows are preserved.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 12, 2026

Greptile Summary

This PR fixes bilibili-style search popups opening as detached windows by introducing browserNavigationHasExplicitUserGesture to detect user input events (mouse/keyboard) on .other navigations and routing them to tab fallback instead of popup creation. Two regression tests are added covering the keyDown trigger case for both decision functions.

  • The existing test testOtherNavigationPlainLeftClickCreatesPopup is now semantically incorrect: it passes no currentEventType (nil in tests), so it actually tests the scripted/no-gesture case, while the name implies a real left click. A real .leftMouseDown event now causes browserNavigationHasExplicitUserGesture to return true, which would prevent popup creation — the opposite of what the test name says. This test should be renamed and a complementary test with currentEventType: .leftMouseDown asserting false should be added.

Confidence Score: 4/5

Core fix is sound; two P2 gaps in test coverage and OAuth regression risk should be addressed before merge.

The keyDown regression is well-covered and the routing logic is correct. However, the existing testOtherNavigationPlainLeftClickCreatesPopup now tests the wrong thing (scripted nil-event instead of real click), and the effect of mouse-event gestures on click-triggered OAuth popups is unverified.

cmuxTests/BrowserConfigTests.swift — testOtherNavigationPlainLeftClickCreatesPopup needs renaming and a real left-click gesture test needs to be added.

Important Files Changed

Filename Overview
Sources/Panels/BrowserPanel.swift Adds browserNavigationHasExplicitUserGesture helper and threads it into both browserNavigationShouldCreatePopup and browserNavigationShouldFallbackNilTargetToNewTab; also expands debug logging. Mouse events in the gesture set may affect click-triggered popup flows.
cmuxTests/BrowserConfigTests.swift Adds two new keyDown regression tests, but the pre-existing testOtherNavigationPlainLeftClickCreatesPopup is now semantically wrong — it tests nil-event (scripted) behavior, not an actual left click, making the name misleading and missing a real left-click gesture regression test.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[WebKit navigation with targetFrame == nil] --> B{navigationType == .other?}
    B -- No --> C[browserNavigationShouldFallbackNilTargetToNewTab true - Open in new tab]
    B -- Yes --> D{browserNavigationHasExplicitUserGesture? leftMouse / rightMouse / otherMouse / keyDown}
    D -- Yes user gesture --> E[browserNavigationShouldFallbackNilTargetToNewTab true - Open in new tab - Fix]
    D -- No gesture scripted --> F[browserNavigationShouldFallbackNilTargetToNewTab false - Fall through to createWebViewWith]
    F --> G{browserNavigationShouldCreatePopup?}
    G -- navigationType != .other OR isUserNewTab OR gesture --> H[No popup - openInNewTab fallback]
    G -- .other AND no gesture AND not user new-tab --> I[isScriptedPopup = true - Create popup window - OAuth preserved]
Loading

Comments Outside Diff (1)

  1. cmuxTests/BrowserConfigTests.swift, line 1516-1524 (link)

    P2 Misleading test name after the fix

    testOtherNavigationPlainLeftClickCreatesPopup no longer tests what its name says. It passes no currentEventType, so the argument defaults to nil in the test environment — this models a scripted/no-gesture navigation, not a real left click. With the new logic, a real leftMouseDown event would make browserNavigationHasExplicitUserGesture return true, causing the function to return false (no popup) — the opposite of what the test name claims.

    Two changes would make the suite accurate:

    1. Rename this test to testOtherNavigationScriptedNoGestureCreatesPopup.
    2. Add a companion test that explicitly passes a mouse gesture and asserts false:
    func testOtherNavigationLeftClickGestureDoesNotCreatePopup() {
        XCTAssertFalse(
            browserNavigationShouldCreatePopup(
                navigationType: .other,
                modifierFlags: [],
                buttonNumber: 0,
                currentEventType: .leftMouseUp,
                currentEventButtonNumber: 0
            )
        )
    }

Reviews (1): Last reviewed commit: "Route user-triggered popup submits to ta..." | Re-trigger Greptile

Comment on lines +5921 to +5933
func browserNavigationHasExplicitUserGesture(
currentEventType: NSEvent.EventType? = NSApp.currentEvent?.type
) -> Bool {
switch currentEventType {
case .leftMouseDown, .leftMouseUp,
.rightMouseDown, .rightMouseUp,
.otherMouseDown, .otherMouseUp,
.keyDown:
return true
default:
return false
}
}
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.

P2 Mouse events in gesture set may silently break click-triggered OAuth popups

browserNavigationHasExplicitUserGesture treats .leftMouseDown/.leftMouseUp as user gestures, so any .other navigation that coincides with those events will be routed away from popup creation. The comment preserved in browserNavigationShouldFallbackNilTargetToNewTab calls out that OAuth/opener flows depend on createWebViewWith returning a live view — but those flows are typically initiated by a button click, meaning NSApp.currentEvent may well be .leftMouseUp when createWebViewWith is invoked on the main thread.

Whether this bites in practice depends on whether WebKit delivers createWebViewWith synchronously within the same event-loop turn as the click (when NSApp.currentEvent is still the mouse event) vs. after the event has been cleared. A test covering currentEventType: .leftMouseUp in the popup-creation path would lock in the intended behaviour and clarify whether click-launched window.open() is expected to produce a popup or fall through to a new tab.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/Panels/BrowserPanel.swift">

<violation number="1" location="Sources/Panels/BrowserPanel.swift:5957">
P1: This blanket gesture check will suppress popup creation for click-triggered `window.open()` calls (e.g. OAuth/payment flows). WebKit delivers `createWebViewWith` synchronously during JS execution, so `NSApp.currentEvent` is still the mouse event when the delegate fires. Since `browserNavigationHasExplicitUserGesture` matches `.leftMouseDown`/`.leftMouseUp`, any click-initiated popup (which OAuth flows rely on for `window.opener`/`postMessage`) will be downgraded to a tab.

Consider narrowing the gesture fallback to keyboard events only (the bilibili submit case), or adding a same-origin/form-submit heuristic so that click-triggered cross-origin popups are preserved.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


// User-triggered new-window requests from form submits / clicks should
// reuse tab semantics; leave popup windows for scripted opener flows.
if isUserNewTab || browserNavigationHasExplicitUserGesture(currentEventType: currentEventType) {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 12, 2026

Choose a reason for hiding this comment

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

P1: This blanket gesture check will suppress popup creation for click-triggered window.open() calls (e.g. OAuth/payment flows). WebKit delivers createWebViewWith synchronously during JS execution, so NSApp.currentEvent is still the mouse event when the delegate fires. Since browserNavigationHasExplicitUserGesture matches .leftMouseDown/.leftMouseUp, any click-initiated popup (which OAuth flows rely on for window.opener/postMessage) will be downgraded to a tab.

Consider narrowing the gesture fallback to keyboard events only (the bilibili submit case), or adding a same-origin/form-submit heuristic so that click-triggered cross-origin popups are preserved.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/Panels/BrowserPanel.swift, line 5957:

<comment>This blanket gesture check will suppress popup creation for click-triggered `window.open()` calls (e.g. OAuth/payment flows). WebKit delivers `createWebViewWith` synchronously during JS execution, so `NSApp.currentEvent` is still the mouse event when the delegate fires. Since `browserNavigationHasExplicitUserGesture` matches `.leftMouseDown`/`.leftMouseUp`, any click-initiated popup (which OAuth flows rely on for `window.opener`/`postMessage`) will be downgraded to a tab.

Consider narrowing the gesture fallback to keyboard events only (the bilibili submit case), or adding a same-origin/form-submit heuristic so that click-triggered cross-origin popups are preserved.</comment>

<file context>
@@ -5934,15 +5948,29 @@ func browserNavigationShouldCreatePopup(
+
+    // User-triggered new-window requests from form submits / clicks should
+    // reuse tab semantics; leave popup windows for scripted opener flows.
+    if isUserNewTab || browserNavigationHasExplicitUserGesture(currentEventType: currentEventType) {
+        return false
+    }
</file context>
Fix with Cubic

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.

Browser pane: bilibili.com search form submission opens a separate window instead of staying in pane

1 participant