Repository navigation
fix(upload): support iframe --selector and hidden input[type=file] uploads - #725
Open
NianJiuZst wants to merge 5 commits into
Open
NianJiuZst wants to merge 5 commits into
NianJiuZst wants to merge 5 commits into
Conversation
…loads
Why:
Uploading files on iframe-hosted widgets that pair a visible trigger button
with a hidden `<input type="file">` (such as Layui's `display: none !important`
`.layui-upload-file`) failed across multiple layers:
1. `resolveBackendNode` only queried the top-level document via
`DOM.getDocument({ depth: 0 })`, returning `selector_not_found` for any
`--selector` inside same-origin `iframe`s or cross-origin OOPIFs.
2. `handleUpload` (`mode = "input"`) always routed through `withInputReady` +
`clickResolvedTarget` -> `resolveNodeGeometry`, failing with `element not
visible` (or `input_not_ready` on hidden/occluded windows) even when the
resolved target was itself an `<input type="file">` (out of sync with
`checkActionTarget`, which already exempted `tool.upload` from `!visible`).
3. Cross-origin OOPIF uploads did not enable the child `Page` domain before
`Page.setInterceptFileChooserDialog`, and `file-input-transaction.ts`
rejected same-target cross-frame `Page.fileChooserOpened` activations.
4. CLI/DSH upload target descriptions could mislead agents into hunting for
hidden `input[type=file]` refs instead of clicking the visible upload button
`@eN` ref or passing an `<input type="file">` selector directly.
What:
- Extend `resolveBackendNode` (and forward `getFrameGraph` in `waits.ts`'s
`probeCdp`) to search child frames in `getFrameGraph` order via isolated-world
deep node serialization when the main document misses or `strictSelector`
checks cross-frame uniqueness, while keeping `expect` preconditions
main-document-scoped.
- Update `CooperationController.run` to re-evaluate `mainDocumentAction` after
resolving selectors so child-frame selectors enforce `frameEpoch` safety while
main-document selectors remain unaffected by unrelated iframe navigations.
- Detect direct `<input type="file">` targets inside
`uploadThroughActivatedFileInput`, validate connectedness, enabled/non-inert
state, and `multiple`, and commit via `DOM.setFileInputFiles` without
clicking or requiring visual renderer frames.
- Enable `Page.enable` on OOPIF upload targets and accept single same-target
`Page.fileChooserOpened` events (covering detached dynamic file inputs and
same-origin cross-frame triggers).
- Clarify CLI, skill, and DSH plugin descriptions, update bilingual changelogs,
and add real-Chrome end-to-end coverage in `upload-iframe.browser.test.ts`
wired into CI.
Why: - Minimal CDP debugger mocks (e.g. tabs.test.ts) return an empty object for Page.getFrameTree, causing ChromiumCdp.getFrameGraph to throw after main-doc DOM.querySelector already reported nodeId=0, turning selector_not_found into cdp_failed. - packages/dsh-plugin-browserskill/skill/SKILL.md was 4449 LF bytes, which exceeded the 4500-byte entry budget by 32 bytes under CRLF line endings. What: - Extract listChildFrames() in resolveBackendNode to fall back cleanly to an empty child-frame list on non-abort/non-timeout/non-attachment-change frame tree errors while still propagating AbortError, CdpReadTimeoutError, and CdpAttachmentChangedError. - Trim DSH SKILL.md to 4398 LF bytes (4481 CRLF bytes <= 4500) and align deferred interaction-details.md / cooperation.md selector scope notes across both CLI and DSH skills.
…ling
Why:
- DSH SKILL.md was already 4495 CRLF bytes on main, leaving only 5 bytes under
the 4500-byte cap; raising maxEntryBytes to 4600 accommodates iframe selector
guidance cleanly across LF and CRLF checkouts without artificial wording cuts.
- resolveBackendNode outer catch converted AbortError during child-frame lookup
to cdp_failed instead of cancelled, and strictSelector still probed child
frames when the main document already had >1 match.
- A few wheel / untilVisible / MCP tool descriptions still said "main-document
CSS selector" even though those paths resolve through resolveBackendNode /
wait-for-element across accessible iframes.
What:
- Raise the DSH skill entry-point budget from 4500 to 4600 across skill
validators, build-skill-content, check-dsh-package, and skill tests, and
restore full wording in packages/dsh-plugin-browserskill/skill/SKILL.md.
- Return { code: "cancelled" } on AbortError in resolveBackendNode's outer
catch and short-circuit strictSelector child-frame traversal once
totalMatches > 1.
- Align remaining wheel, step/plan untilVisible, and MCP tool selector
descriptions across DSH plugin and bsk CLI.
…e-upload # Conflicts: # CHANGELOG.md # CHANGELOG.zh-CN.md
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / Why
Fixes file upload failures on
iframe-hosted upload widgets that pair a visible trigger button with a hidden<input type="file">(such as Layui'sdisplay: none !important.layui-upload-file), where users previously encounteredselector_not_found,element not visible, orinput_not_readyon hidden/occluded windows:--selectorstopped at the top-level document:resolveBackendNodeonly queriedDOM.getDocument({ depth: 0 })+DOM.querySelector/DOM.querySelectorAll, missing same-originiframes and cross-origin OOPIFs.<input type="file">targets required a visual click:handleUpload(mode = "input") unconditionally went throughwithInputReady+clickResolvedTarget->resolveNodeGeometry, failing withelement not visibleondisplay: nonefile inputs (out of sync withcheckActionTarget, which already exemptedtool.uploadfrom!facts.visible).Pagedomain beforePage.setInterceptFileChooserDialog, andfile-input-transaction.tsrejected same-target cross-framePage.fileChooserOpenedactivations.uploadparameter descriptions could nudge agents toward matching hiddeninput[type=file]refs rather than clicking the visible upload button@eNref or targeting<input type="file">directly via--selector.Changes / What
--selectorresolution (interaction.ts,waits.ts,cooperation/controller.ts):DOMfast path when non-strict selectors match in the top document; when unmatched or whenstrictSelectorverifies uniqueness, query child frames ingetFrameGraphorder via isolated-world deep node serialization (maxNodeDepth: 0,includeShadowTree: "none").getFrameGraphinwaits.ts'sprobeCdpsowait-for-element --selectoralso works acrossiframes / OOPIFs, while preserving main-document scope forexpectpreconditions (toolName === "precondition").mainDocumentActioninCooperationController.runafter resolving selectors so child-frame selectors enforceframeEpochinvalidation (document_changed,effect_state: "none"), while main-document selectors remain unaffected by unrelated child-frame navigations.<input type="file">assignment (file-input-transaction.ts,upload.ts):<input type="file">targets duringarm_input_probe, validate connectedness,!disabled/!inert, andmultiplevs file count, and commit directly viaDOM.setFileInputFiles— skippingwithInputReadyandclickResolvedTarget(works ondisplay: none !importantinputs and occluded/hidden background tabs).file-input-transaction.ts):Page.enableon child OOPIF targets (target.sessionId) beforePage.setInterceptFileChooserDialogsoPage.fileChooserOpenedfires reliably (including dynamically created detached<input type="file">pickers), and accept single same-targetPage.fileChooserOpenedevents across same-origin frames.upload.rs,files.md, DSH plugin tools,CHANGELOG*.md,ci.yml):.github/workflows/ci.yml.Verification
BSK_CLICK_CHROMEagainst Google Chrome):apps/extension/src/tools/__tests__/upload-iframe.browser.test.ts(same-originiframe& cross-origin OOPIF Layui-style button@refupload, hidden/background-tab direct--selectorupload todisplay: none !importantinput[type=file], detached dynamicinput[type=file]in OOPIF, same-origin cross-frame trigger,!multiple&disabledrejections, cross-framewait-for-element/click/ ambiguity checks, and cooperativeframeEpochinvalidation)apps/extension/src/tools/__tests__/soft-detach-click.browser.test.tsfile-transfer.test.ts,file-transfer-readiness.test.ts,target-policy.test.ts,action-checks.test.tspnpm lint,pnpm --filter @browser-skill/extension compile,pnpm protocol:check,cargo fmt --all -- --check,cargo clippy -p bsk --bin bsk --locked -- -D warnings,node scripts/check-skill-bundles.mjs,node scripts/check-crate-skill.mjs,node scripts/check-dsh-package.mjs,python3 scripts/check-changelog-sync.py