Skip to content

fix(extension): open file chooser for hidden file inputs on upload - #2527

Open
yapxue wants to merge 2 commits into
jackwener:mainfrom
yapxue:fix/set-file-input-hidden-inputs
Open

yapxue wants to merge 2 commits into
jackwener:mainfrom
yapxue:fix/set-file-input-hidden-inputs

Conversation

@yapxue

@yapxue yapxue commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Problem

set-file-input fails whenever the target file input is hidden, e.g. on the Xiaohongshu creator center:

Image injection failed: Page.fileChooserOpened not received within 5s — the input may not have opened a file chooser

This breaks opencli xiaohongshu publish and opencli browser <session> upload for those sites.

Repro:

opencli browser xhs open "https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image"
opencli browser xhs eval "(()=>{const i=document.querySelector('.upload-input');const r=i.getBoundingClientRect();return JSON.stringify({w:r.width,h:r.height,display:getComputedStyle(i).display});})()"
# {"w":0,"h":0,"display":"block"}
opencli browser xhs upload ".upload-input" "/path/to/any.jpg"
# x Page.fileChooserOpened not received within 5s

Root cause

DOM.setFileInputFiles is rejected under chrome.debugger (crbug 928255), so the extension opens the chooser via Page.setInterceptFileChooserDialog + Runtime.evaluate .click() and uses the backendNodeId from Page.fileChooserOpened. The chooser only opens for a rendered input and when the click carries a user gesture. A 0x0 hidden input (hidden <input type=file> behind a styled button - a very common pattern) never opens a chooser, so the event never fires and the call times out.

Fix

Before clicking, if the input is not rendered (display:none / visibility:hidden / opacity:0 / zero rect), temporarily reveal it (fixed 1x1 box) and click with Runtime.evaluate userGesture: true; restore the original inline style in finally.

Tests

Added a cdp setFileInputFiles unit test covering the reveal -> click(userGesture) -> restore flow and the DOM.setFileInputFiles backendNodeId hand-off. I could not run vitest locally in this environment (no node_modules, slow network); please rely on CI.

DOM.setFileInputFiles is rejected under chrome.debugger (crbug 928255),
so the extension opens the file chooser via interception and uses the
backendNodeId from Page.fileChooserOpened. That only works when the
input is rendered and the click is treated as a user gesture.

Many sites hide a 0x0 <input type=file> behind a styled button and open
it programmatically (e.g. Xiaohongshu's creator center, which triggers
set-file-input on publish). Clicking such an input never opens a chooser,
so set-file-input always failed with:

  Page.fileChooserOpened not received within 5s

Temporarily make hidden inputs renderable and dispatch the click with
Runtime.evaluate userGesture:true, then restore the original inline
style. Add a cdp unit test covering the reveal/restore flow.
The setFileInputFiles cleanup calls chrome.debugger.onEvent.removeListener;
the mock only provided addListener, so the test threw.
@yapxue

yapxue commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

Pushed a fix for the failing extension test: the mock needed chrome.debugger.onEvent.removeListener (called by the cleanup path). The audit failure is unrelated to this change — it is a pre-existing js-yaml advisory (GHSA-2883-xcg3-v3hh) flagged by npm audit --omit=dev on the base tree.

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.

1 participant