Skip to content

Fix browser showOpenFilePicker support - #4122

Merged
austinywang merged 7 commits into
mainfrom
issue-4050-showopenfilepicker-undefined
May 15, 2026
Merged

austinywang merged 7 commits into
mainfrom
issue-4050-showopenfilepicker-undefined

Conversation

@austinywang

@austinywang austinywang commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a browser panel regression test that verifies window.showOpenFilePicker is installed in WKWebView pages
  • install a shared File System Access bridge from the browser WKWebView configuration path
  • back showOpenFilePicker() with WebKit's native file-input picker and lightweight file handle objects so selected files expose getFile()

Fixes #4050.

Testing

  • Not run locally per repo policy; CI will run on the PR.

Note

Medium Risk
Injects new JavaScript into every main-frame page load and changes file-picker behavior in the embedded browser, which could impact site compatibility or event-handling edge cases despite being scoped to a shim.

Overview
Restores window.showOpenFilePicker() support in the browser WKWebView by injecting a new BrowserFileSystemAccessBridge user script at document start (main frame only).

The bridge implements a minimal File System Access API shim backed by a hidden <input type="file">, returning lightweight FileSystemFileHandle-like objects with getFile() and ensuring the picker promise settles once (including an AbortError dismissal path with focus-return fallback) while cleaning up DOM inputs.

Adds regression tests to verify the API is installed in loaded pages and that the picker rejects/cleans up correctly when focus returns without a native cancel event.

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


Summary by cubic

Restore window.showOpenFilePicker in WKWebView by injecting a minimal File System Access bridge that uses the native file input picker. Ensures the promise settles once, avoids element-focus false cancels, and cleans up; fixes #4050.

  • Bug Fixes
    • Inject BrowserFileSystemAccessBridge via WKUserScript at document start (main frame) so showOpenFilePicker() is available on all pages.
    • Shim supports multiple and types.accept, returns lightweight FileSystemFileHandle with getFile(), and uses a window-focus fallback to resolve or reject with AbortError on dismissal; ignores element-focus events.
    • Add tests for API install, window-focus dismissal with AbortError, no rejection on element focus, and cleanup of hidden inputs.

Written for commit 82f05ca. Summary will update on new commits.

@vercel

vercel Bot commented May 13, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled May 15, 2026 3:19am
cmux-staging Building Building Preview, Comment May 15, 2026 3:19am

@coderabbitai

coderabbitai Bot commented May 13, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@austinywang has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b9406dc-bb87-4e74-8edf-4fdf0ae3e310

📥 Commits

Reviewing files that changed from the base of the PR and between c83f585 and 82f05ca.

📒 Files selected for processing (2)
  • Sources/Panels/BrowserPanel.swift
  • cmuxTests/BrowserPanelTests.swift
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4050-showopenfilepicker-undefined

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.

@greptile-apps

greptile-apps Bot commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores window.showOpenFilePicker() inside the browser WKWebView panel by injecting BrowserFileSystemAccessBridge as a document-start WKUserScript (main frame only). The shim wraps a hidden <input type="file"> and provides a robust single-settle promise with three settlement paths: change (file selected), cancel (macOS 14 native dismiss), and a window.focus-return fallback with a setTimeout(0) deferral so a selection-triggered change always wins.

  • BrowserPanel.swift: Adds BrowserFileSystemAccessBridge enum (222-line JS string) and wires it into configureWebViewConfiguration alongside the existing telemetry script injection.
  • BrowserPanelTests.swift: Adds BrowserPanelFileSystemAccessBridgeTests with three tests: API presence, focus-return rejection (using callAsyncJavaScript), and a guard confirming internal element focus does not prematurely reject the picker promise.

Confidence Score: 5/5

Safe to merge. The shim is narrowly scoped to main-frame pages, guards against double-installation, and the three settlement paths are mutually exclusive thanks to the settled flag.

The JavaScript bridge is logically sound, the single-settle invariant holds across all three settlement paths (change / cancel / focus-return), and the new tests exercise the non-obvious dismissal flows correctly using callAsyncJavaScript.

No files require special attention. BrowserPanel.swift continues to grow, but the addition here is self-contained.

Important Files Changed

Filename Overview
Sources/Panels/BrowserPanel.swift Adds BrowserFileSystemAccessBridge enum (222 lines of JS string + Swift wrapper) and injects it as a document-start WKUserScript. Logic is correct; the only concern is adding to an already 11 k-line file.
cmuxTests/BrowserPanelTests.swift Adds BrowserPanelFileSystemAccessBridgeTests with three coverage scenarios: API installation, focus-return rejection, and no-reject-on-element-focus. Uses callAsyncJavaScript correctly for promise-settling assertions.

Sequence Diagram

sequenceDiagram
    participant Page as "Web Page JS"
    participant Bridge as "BrowserFileSystemAccessBridge"
    participant Input as "hidden input[type=file]"
    participant OS as "OS File Picker"
    participant Win as "window focus"

    Page->>Bridge: "showOpenFilePicker(options)"
    Bridge->>Input: "createElement + configure"
    Bridge->>Input: "addEventListener(change, cancel)"
    Bridge->>Win: "addEventListener(focus)"
    Bridge->>Input: "appendChild + click()"
    Input->>OS: "open native picker"

    alt "User selects file(s)"
        OS-->>Input: "change event"
        Input->>Bridge: "handleChange()"
        Bridge-->>Page: "resolve [FileSystemFileHandle...]"
    else "User dismisses (cancel event)"
        OS-->>Input: "cancel event"
        Input->>Bridge: "handleCancel()"
        Bridge-->>Page: "reject AbortError"
    else "Window regains focus (fallback)"
        OS-->>Win: "focus event"
        Win->>Bridge: "handleWindowFocus()"
        Bridge->>Bridge: "setTimeout(0) — defer one turn"
        alt "change already settled"
            Bridge->>Bridge: "settled=true, no-op"
        else "no files — true dismissal"
            Bridge-->>Page: "reject AbortError"
        end
    end

    Bridge->>Input: "cleanup: remove listeners + DOM node"
Loading

Reviews (4): Last reviewed commit: "fix: avoid element-focus file picker dis..." | Re-trigger Greptile

Comment thread Sources/Panels/BrowserPanel.swift Outdated
Comment thread Sources/Panels/BrowserPanel.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

Greptile note checked: cmux targets macOS 14.0 or later (MACOSX_DEPLOYMENT_TARGET = 14.0, and public requirements say macOS 14+). The <input type="file"> cancel event concern is for Monterey and earlier, which this app does not support, so no fallback path is needed here.

Comment thread Sources/Panels/BrowserPanel.swift
Comment thread Sources/Panels/BrowserPanel.swift
Comment thread cmuxTests/BrowserPanelTests.swift
Comment thread cmuxTests/BrowserPanelTests.swift Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dde0ebf. Configure here.

Comment thread Sources/Panels/BrowserPanel.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

Greptile's latest pass reports this is safe to merge and that no files require special attention. I verified the remaining note is housekeeping around file placement; I'm leaving the bridge in BrowserPanel for this PR so the functional fix and regression coverage stay stable, and the extraction can be handled separately if we want that cleanup.

@austinywang
austinywang merged commit df72e51 into main May 15, 2026
24 checks passed
@austinywang austinywang mentioned this pull request May 19, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 82f05caf Deployed May 15, 2026 by vercel[bot]
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 Report: window.showOpenFilePicker is undefined

1 participant