Skip to content

feat: support window.open() popup windows#1150

Merged
lawrencecchen merged 8 commits intomanaflow-ai:mainfrom
SuperManfred:feat/browser-popup-windows
Mar 16, 2026
Merged

feat: support window.open() popup windows#1150
lawrencecchen merged 8 commits intomanaflow-ai:mainfrom
SuperManfred:feat/browser-popup-windows

Conversation

@SuperManfred
Copy link
Copy Markdown
Contributor

@SuperManfred SuperManfred commented Mar 10, 2026

Summary

  • Return a live WKWebView from createWebViewWith using WebKit’s supplied configuration so scripted popup flows preserve popup browsing-context behavior, including a working window.opener
  • Add BrowserPopupWindowController: standalone NSPanel popup windows with opener-owned lifecycle, nested popup depth limit, title/URL updates, and a read-only URL label
  • Route scripted window.open() requests to popup windows while keeping user-initiated new-window actions as tabs, including from popup contexts
  • Intercept context-menu Open Link in New Tab so it always opens a real tab, including when invoked from a popup or nested popup
  • Mirror main-browser handling for popup insecure-HTTP prompts and auth challenges
  • Make Cmd+W close only the focused popup window and safely tear down opener-owned popups
  • Add behavioral unit coverage for popup/tab routing decisions and popup frame calculation helpers

Closes #742

Verification

Manual verification:

  • OAuth/OIDC popup flow completes and closes on successful auth
  • Popup page reports window.opener is: SET
  • Cmd-click opens a new tab instead of a popup
  • Right-click Open Link in New Tab opens a tab in the main browser
  • Right-click Open Link in New Tab inside a popup opens a tab in the opener’s main browser
  • Right-click Open Link in New Tab inside a nested popup opens a tab in the opener’s main browser
  • localhost loads without an insecure-HTTP prompt under the default allowlist
  • Cmd+W closes only the popup window

Not yet manually verified:

  • Middle-click opens a new tab

Local build:

  • ./scripts/reload.sh --tag popup-pr1150
  • Unit tests not run locally per repo policy

Demo Video

Screen.Recording.2026-03-10.at.19.50.29.mov

Summary by CodeRabbit

  • New Features

    • Added browser popup windows with nested popups (up to 3 levels), lifecycle cleanup, JS dialogs, and insecure-HTTP prompt handling.
    • Localized "Loading…" title added.
    • Context menu "Open Link in New Tab" now opens as a tab and routes correctly.
    • Cmd+W now prioritizes closing an active popup window.
  • Tests

    • Added tests covering popup creation decisions and popup geometry/positioning behavior.

Return a live WKWebView from createWebViewWith using WebKit's supplied
configuration, preserving popup browsing-context semantics (window.opener,
postMessage). This fixes OAuth/OIDC flows and any site relying on standard
popup patterns.

- Add BrowserPopupWindowController: NSPanel-based popup with self-retention,
  KVO title/URL, read-only URL label, nested popup depth limit (3),
  insecure-HTTP prompt parity, auth challenge parity, download delegate
- Classifier: scripted requests (window.open) create popups; user-initiated
  actions (Cmd+click, middle-click, context menu) open tabs
- Retarget context menu "Open Link in New Tab" to bypass createWebViewWith,
  wired in both main browser and popup web views
- Cmd+W fast path in AppDelegate for popup windows
- Opener panel owns popup lifecycle; close() tears down all child popups
Add BrowserPopupPanel (NSPanel subclass) that intercepts Cmd+W in
performKeyEquivalent before the swizzled cmux_performKeyEquivalent
can dispatch it to the main menu's "Close Tab" action.

Also refine the popup classifier to reuse browserNavigationShouldOpenInNewTab
for Cmd+click/middle-click detection, add download delegate wiring, and
wire onContextMenuOpenLinkInNewTab for popup web views.
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 10, 2026

@SuperManfred is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a macOS popup window system: new BrowserPopupWindowController/NSPanel, BrowserPanel popup integration and decision logic, CmuxWebView context-menu hook, AppDelegate Cmd+W handling for popups, localization for a popup loading title, and unit tests for popup decisions and geometry.

Changes

Cohort / File(s) Summary
Popup controller & project file
Sources/Panels/BrowserPopupWindowController.swift, GhosttyTabs.xcodeproj/project.pbxproj
Adds BrowserPopupWindowController and BrowserPopupPanel NSPanel: creates CmuxWebView using passed WKWebViewConfiguration/WKWindowFeatures, enforces nesting depth, manages child popups, delegates, insecure-HTTP alert, lifecycle cleanup; project file includes the new source.
Browser panel integration
Sources/Panels/BrowserPanel.swift
Adds per-panel popupControllers, createFloatingPopup(configuration:windowFeatures:) -> WKWebView?, removePopupController(_:), wires browserUIDelegate.openPopup, integrates popup creation into createWebViewWith flow via browserNavigationShouldCreatePopup(...), and closes owned popups on teardown. Increases BrowserDownloadDelegate visibility.
Popup delegates & behavior
Sources/Panels/BrowserPopupWindowController.swift
Implements PopupUIDelegate and PopupNavigationDelegate for JS dialogs, file open panels, navigation policy, nested popup creation, external-scheme handling, title/URL observation, and webViewDidClose semantics.
CmuxWebView context-menu & callback
Sources/Panels/CmuxWebView.swift
Adds var onContextMenuOpenLinkInNewTab: ((URL) -> Void)?, changes “Open Link in New Window” → “Open Link in New Tab”, and routes context-menu-opened links through the new callback.
App-level shortcut handling
Sources/AppDelegate.swift
Updates Cmd+W handling to prefer closing an active browser popup window (cmux.browser-popup) before existing settings/workspace close logic; adds DEBUG logging path.
Localization
Resources/Localizable.xcstrings
Adds browser.popup.loadingTitle localization entries (English "Loading…", Japanese "読み込み中…").
Tests
cmuxTests/CmuxWebViewKeyEquivalentTests.swift
Adds BrowserPopupDecisionTests and BrowserPopupContentRectTests covering popup-creation decision logic and popup geometry (clamping/centering) behavior.

Sequence Diagram

sequenceDiagram
    participant JS as JavaScript (window.open)
    participant UI as BrowserUIDelegate / BrowserPanel
    participant PopupCtrl as BrowserPopupWindowController
    participant Panel as BrowserPopupPanel (NSPanel)
    participant Web as CmuxWebView

    JS->>UI: createWebViewWith(configuration, windowFeatures)
    UI->>UI: classify request (scripted popup vs tab)
    alt scripted popup
        UI->>PopupCtrl: openPopup(configuration, windowFeatures)
        PopupCtrl->>Web: create CmuxWebView with configuration
        PopupCtrl->>Panel: build NSPanel, add URL label + web view
        PopupCtrl->>Web: set uiDelegate/navigationDelegate
        PopupCtrl->>Panel: clamp/center and show window
        PopupCtrl-->>UI: return CmuxWebView
        UI-->>JS: return WKWebView (window.opener established)
    else fallback (new tab / external)
        UI->>UI: open in new tab or external handler
    end
    Web->>PopupCtrl: navigation / title / URL updates
    PopupCtrl->>Panel: update title/URL label
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped a tiny popup bright,
a window opened in the light.
Three deep I nest and tidy close,
Cmd+W taps — away it goes.
Carrot cheer — the web now flows!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.45% 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 PR title 'feat: support window.open() popup windows' accurately summarizes the main feature addition: native popup-window support for scripted window.open() flows.
Description check ✅ Passed The PR description comprehensively covers the summary, testing approach, includes a demo video, and provides a detailed verification checklist with completion status.
Linked Issues check ✅ Passed Code changes implement all core objectives from issue #742: popup window creation with WKWebView, window.opener support, WKWindowFeatures respect, lifecycle management, nested depth limits, security guards, and tab/popup routing logic.
Out of Scope Changes check ✅ Passed All changes align with issue #742 scope: popup window implementation, routing logic, security guards, and related test coverage. No extraneous modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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: 3

🧹 Nitpick comments (1)
Sources/Panels/BrowserPopupWindowController.swift (1)

474-539: Consider adding decidePolicyFor navigationResponse for download handling parity.

The main browser's BrowserNavigationDelegate implements decidePolicyFor navigationResponse to detect downloads via Content-Disposition: attachment headers and non-renderable MIME types, explicitly returning .download policy. Without this, popup windows may attempt to render binary content instead of initiating downloads for certain MIME types.

The didBecome download handlers are present and will work for WebKit-initiated download conversions, but explicit response-policy handling ensures consistent download behavior with the main browser.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Panels/BrowserPopupWindowController.swift` around lines 474 - 539,
Add a webView(_:decidePolicyFor:navigationResponse:decisionHandler:)
implementation to PopupNavigationDelegate that mirrors the main
BrowserNavigationDelegate: inspect navigationResponse.response (check
Content-Disposition for "attachment" and the MIME type for non-renderable/binary
types), call decisionHandler(.download) when those conditions are met and
decisionHandler(.allow) otherwise, and ensure any download conversion continues
to use downloadDelegate (the existing webView(_:navigationAction:didBecome:) /
webView(_:navigationResponse:didBecome:) handlers will attach the delegate).
This keeps PopupNavigationDelegate consistent with BrowserNavigationDelegate’s
download policy handling.
🤖 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/AppDelegate.swift`:
- Around line 7394-7401: The current conditional only checks event.window when
NSApp.keyWindow is nil, missing cases where the popup (identifier
"cmux.browser-popup") is present but not key; update the logic in the Cmd+W
handler to test both possible popup candidates (NSApp.keyWindow and
event.window) before falling back to the workspace-panel branch: explicitly
inspect both windows' identifier?.rawValue for "cmux.browser-popup" (e.g.,
iterate or check both variables) and call performClose(nil) and return true if
either matches, otherwise proceed to the existing fallback that uses
event.window ?? NSApp.keyWindow ?? NSApp.mainWindow.

In `@Sources/Panels/BrowserPanel.swift`:
- Around line 2260-2266: The loop mutates popupControllers because closePopup()
triggers windowWillClose -> removePopupController(self); fix by iterating a
copy: create a local copy of popupControllers (e.g., let controllers =
popupControllers) and iterate controllers calling popup.closeAllChildPopups()
and popup.closePopup(), then call popupControllers.removeAll() after the loop;
reference popupControllers, closeAllChildPopups(), closePopup(),
windowWillClose, and removePopupController(self) to locate the code.

In `@Sources/Panels/BrowserPopupWindowController.swift`:
- Around line 67-71: The BrowserPopupWindowController currently enables
webView.isInspectable only inside a `#if` DEBUG block; remove the `#if` DEBUG /
`#endif` so the isInspectable assignment runs in all builds (but keep the
availability check if `#available`(macOS 13.3, *) remains) — update the code in
BrowserPopupWindowController where webView.isInspectable = true is set to match
BrowserPanel.swift behavior so popup windows also have Web Inspector enabled in
production.

---

Nitpick comments:
In `@Sources/Panels/BrowserPopupWindowController.swift`:
- Around line 474-539: Add a
webView(_:decidePolicyFor:navigationResponse:decisionHandler:) implementation to
PopupNavigationDelegate that mirrors the main BrowserNavigationDelegate: inspect
navigationResponse.response (check Content-Disposition for "attachment" and the
MIME type for non-renderable/binary types), call decisionHandler(.download) when
those conditions are met and decisionHandler(.allow) otherwise, and ensure any
download conversion continues to use downloadDelegate (the existing
webView(_:navigationAction:didBecome:) /
webView(_:navigationResponse:didBecome:) handlers will attach the delegate).
This keeps PopupNavigationDelegate consistent with BrowserNavigationDelegate’s
download policy handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae6bad70-15c9-4f98-9d11-a22247001060

📥 Commits

Reviewing files that changed from the base of the PR and between dea60ea and 2cb2824.

📒 Files selected for processing (6)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/Panels/BrowserPopupWindowController.swift
  • Sources/Panels/CmuxWebView.swift

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR adds full window.open() popup support: a new BrowserPopupWindowController hosts scripted popup windows in standalone NSPanel instances, with window.opener / postMessage semantics preserved via WebKit's supplied configuration, a nesting depth limit, proper lifecycle teardown, and parity with the main browser for insecure-HTTP prompts, auth challenges, and downloads.

Key issues found:

  • Logic gap in PopupUIDelegate: createWebViewWith inside PopupUIDelegate has no scripted-popup classifier. Every new-window request from within a popup — including user-initiated ones like Cmd+click — will create a nested popup rather than opening a tab in the main browser. The main BrowserUIDelegate has the isScriptedPopup check; the same logic should be applied in PopupUIDelegate to maintain consistent behavior.
  • DispatchQueue.main.async inside a @MainActor class: The KVO callbacks in BrowserPopupWindowController.init use DispatchQueue.main.async to schedule UI updates. Since the class is @MainActor, the idiomatic Swift concurrency pattern would be Task { @MainActor in ... }. The urlLabel local variable is also captured strongly by these closures and not stored as an instance property, making the view's lifetime implicit.
  • Redundant Cmd+W handling: Both BrowserPopupPanel.performKeyEquivalent and the new AppDelegate early-exit path call performClose(nil) for the same popup window in response to Cmd+W. While performClose is idempotent, the dual-handler design could be simplified.

Confidence Score: 3/5

  • Mostly safe to merge but has a logic gap in user-initiated new-window handling from within popup windows.
  • The core popup infrastructure (lifecycle, nesting depth, teardown, security prompts) is solid and well-structured. The one actionable logic bug — PopupUIDelegate missing the scripted-popup classifier — means Cmd+click links inside popup windows will open nested popup windows instead of tabs, which contradicts the PR's stated routing goal. This is a behavioral regression for a supported navigation pattern. The KVO and Cmd+W concerns are style-level but straightforward to fix.
  • Pay close attention to Sources/Panels/BrowserPopupWindowController.swift, specifically the PopupUIDelegate.createWebViewWith implementation.

Important Files Changed

Filename Overview
Sources/Panels/BrowserPopupWindowController.swift New file that implements the full popup window lifecycle. Core logic is sound (self-retention via associated object, nested depth limit, clean teardown in windowWillClose), but PopupUIDelegate.createWebViewWith is missing the scripted-popup classifier, causing user-initiated new-window actions from within a popup to create nested popups rather than tabs. KVO callbacks also use DispatchQueue.main.async in a @MainActor class, which is inconsistent with Swift concurrency idioms.
Sources/Panels/BrowserPanel.swift Changes add popup controller tracking, the createFloatingPopup / removePopupController management methods, the openPopup closure wired into BrowserUIDelegate, and the scripted-popup classifier. The classifier logic in BrowserUIDelegate.createWebViewWith is well-designed. Cleanup during close() correctly handles the popup list iteration. BrowserDownloadDelegate is promoted from private to internal (necessary so BrowserPopupWindowController can reference it).
Sources/Panels/CmuxWebView.swift Adds onContextMenuOpenLinkInNewTab callback and the contextMenuOpenLinkInNewTab action method to retarget the "Open Link in New Window" context menu item. The approach of replacing the item's target/action rather than relying on the default WebKit path is correct and necessary to prevent the classifier from misidentifying context-menu actions as scripted popups.
Sources/AppDelegate.swift Adds an early-exit path for Cmd+W that closes browser popup windows before the existing settings/workspace panel logic runs. The logic is correct, but creates a redundant Cmd+W handler since BrowserPopupPanel.performKeyEquivalent handles the same key. Both paths call performClose(nil) on the same window.
Resources/Localizable.xcstrings Adds browser.popup.loadingTitle with English and Japanese translations for the initial popup window title. No issues.
GhosttyTabs.xcodeproj/project.pbxproj Correctly registers BrowserPopupWindowController.swift as a source file in the Xcode project. No issues.

Sequence Diagram

sequenceDiagram
    participant Page as Web Page (JS)
    participant WK as WebKit
    participant BUD as BrowserUIDelegate
    participant BP as BrowserPanel
    participant BPWC as BrowserPopupWindowController
    participant PUD as PopupUIDelegate
    participant PND as PopupNavigationDelegate

    Page->>WK: window.open(url)
    WK->>BUD: createWebViewWith(configuration, navigationAction)
    BUD->>BUD: classify: isScriptedPopup?
    alt scripted (navigationType == .other && !isUserNewTab)
        BUD->>BP: openPopup(configuration, windowFeatures)
        BP->>BPWC: init(configuration, windowFeatures, openerPanel)
        BPWC->>BPWC: create CmuxWebView + NSPanel
        BPWC->>BPWC: makeKeyAndOrderFront
        BPWC-->>BP: controller
        BP-->>BUD: controller.webView
        BUD-->>WK: popupWebView (live)
        WK->>PND: decidePolicyFor(navigationAction)
        PND->>PND: check external / insecure HTTP
        PND-->>WK: .allow
        WK->>BPWC: loads URL in popup webView
    else user-initiated (Cmd+click / middle-click)
        BUD->>BP: openInNewTab(url)
        BP-->>BUD: nil
        BUD-->>WK: nil (no new window)
    end

    Note over Page,BPWC: Nested popup from popup window
    Page->>WK: window.open() inside popup
    WK->>PUD: createWebViewWith(...)
    PUD->>BPWC: createNestedPopup(configuration, windowFeatures)
    BPWC->>BPWC: depth check (max=3)
    BPWC->>BPWC: init child BrowserPopupWindowController
    BPWC-->>PUD: child.webView
    PUD-->>WK: child webView

    Note over BPWC,BP: Cleanup on popup close
    BPWC->>BPWC: windowWillClose
    BPWC->>BPWC: closeAllChildPopups()
    BPWC->>BP: removePopupController(self)
Loading

Comments Outside Diff (1)

  1. Sources/AppDelegate.swift, line 77-84 (link)

    Redundant Cmd+W handling between AppDelegate and BrowserPopupPanel

    BrowserPopupPanel.performKeyEquivalent (in BrowserPopupWindowController.swift) already intercepts Cmd+W and calls performClose(nil) directly on the panel. This AppDelegate path also calls performClose(nil) on the same window, resulting in performClose being called twice in many scenarios.

    While performClose is idempotent in practice (a window that is already closing will not close again), this introduces subtle questions about double-firing windowShouldClose. A clear comment or consolidating the two handlers would reduce ambiguity. The comment "NSPanel may not be event.window when the key event originates from AppKit's event dispatch" suggests the AppDelegate handler was added as a fallback — if that's the case, it may be worth verifying whether the BrowserPopupPanel override alone is sufficient, and removing the AppDelegate check if so.

Last reviewed commit: 2cb2824

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 6 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/BrowserPopupWindowController.swift">

<violation number="1" location="Sources/Panels/BrowserPopupWindowController.swift:96">
P2: Convert the JavaScript `top` coordinate to AppKit's bottom-up coordinate system.</violation>
</file>

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

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 `@cmuxTests/CmuxWebViewKeyEquivalentTests.swift`:
- Around line 2350-2363: The test testExplicitCoordinatesClampToVisibleFrame
only checks origin clamping but not size clamping; update the call to
browserPopupContentRect so requestedWidth and requestedHeight exceed the
visibleFrame (e.g., set requestedWidth > visibleFrame.width and requestedHeight
> visibleFrame.height) and then change the assertions to expect rect.width and
rect.height to equal the visibleFrame's width and height (with accuracy), and
keep/assert the clamped origin values as appropriate; reference
browserPopupContentRect and testExplicitCoordinatesClampToVisibleFrame when
locating the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c57b43bc-5d8f-4447-a7b8-c3cfa1a1ea6b

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb2824 and 02d09d8.

📒 Files selected for processing (4)
  • Sources/AppDelegate.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/Panels/BrowserPopupWindowController.swift
  • cmuxTests/CmuxWebViewKeyEquivalentTests.swift

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 `@cmuxTests/CmuxWebViewKeyEquivalentTests.swift`:
- Around line 2302-2332: Add a test in BrowserPopupDecisionTests to cover the
plain .linkActivated case: implement a new test method (e.g.,
testLinkActivatedPlainClickDoesNotCreatePopup) that calls
browserNavigationShouldCreatePopup(navigationType: .linkActivated,
modifierFlags: [], buttonNumber: 0) and asserts false with XCTAssertFalse;
follow the style of the existing tests (testOtherNavigationPlainLeftClick,
testLinkActivatedCmdClickDoesNotCreatePopup) so the
.linkActivated-without-modifiers branch is pinned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c41e4ea8-0512-470c-bacd-92ca1d0862c5

📥 Commits

Reviewing files that changed from the base of the PR and between 02d09d8 and e191c79.

📒 Files selected for processing (1)
  • cmuxTests/CmuxWebViewKeyEquivalentTests.swift

@lawrencecchen lawrencecchen merged commit 6b138f7 into manaflow-ai:main Mar 16, 2026
11 of 14 checks passed
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
* feat: support window.open() popup windows (manaflow-ai#742)

Return a live WKWebView from createWebViewWith using WebKit's supplied
configuration, preserving popup browsing-context semantics (window.opener,
postMessage). This fixes OAuth/OIDC flows and any site relying on standard
popup patterns.

- Add BrowserPopupWindowController: NSPanel-based popup with self-retention,
  KVO title/URL, read-only URL label, nested popup depth limit (3),
  insecure-HTTP prompt parity, auth challenge parity, download delegate
- Classifier: scripted requests (window.open) create popups; user-initiated
  actions (Cmd+click, middle-click, context menu) open tabs
- Retarget context menu "Open Link in New Tab" to bypass createWebViewWith,
  wired in both main browser and popup web views
- Cmd+W fast path in AppDelegate for popup windows
- Opener panel owns popup lifecycle; close() tears down all child popups

* fix: Cmd+W closes only the popup, not the parent tab

Add BrowserPopupPanel (NSPanel subclass) that intercepts Cmd+W in
performKeyEquivalent before the swizzled cmux_performKeyEquivalent
can dispatch it to the main menu's "Close Tab" action.

Also refine the popup classifier to reuse browserNavigationShouldOpenInNewTab
for Cmd+click/middle-click detection, add download delegate wiring, and
wire onContextMenuOpenLinkInNewTab for popup web views.

* fix: tighten popup routing and window behavior

* test: cover oversized popup frame clamping

* test: cover plain link-activated popup routing

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
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: support window.open() popup windows

2 participants