Skip to content

feat(locator): add drop API for files and clipboard-like data#40283

Merged
pavelfeldman merged 1 commit intomicrosoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
pavelfeldman merged 1 commit intomicrosoft:mainfrom
pavelfeldman:feat-locator-drop

Conversation

@pavelfeldman
Copy link
Copy Markdown
Member

Summary

  • Adds locator.drop(payload, options?) that simulates an external OS/clipboard drop onto a target element, dispatching native dragenter/dragover/drop events with a real DataTransfer.
  • payload accepts { files?, data? } — files as paths or FilePayload buffers, data as a mime-type → string map.
  • Works cross-browser (Chromium, Firefox, WebKit) by constructing the DataTransfer in the page's main world so Firefox doesn't strip file content at the isolated-world boundary.
  • Throws an actionable error when the target's dragover handler does not call preventDefault().

Simulates an external OS/clipboard drag-and-drop of files and/or
mime-typed string data onto a locator. Dispatches native DragEvents
(dragenter, dragover, drop) with a DataTransfer constructed in the
page's main world, so it works cross-browser and carries real file
buffers — unlike the previous evaluateHandle + dispatchEvent workaround.
@github-actions
Copy link
Copy Markdown
Contributor

Test results for "MCP"

6096 passed, 976 skipped


Merge workflow run.

Comment thread docs/src/api/params.md
- `buffer` <[Buffer]> File content

## drop-payload
- `payload` <[Object]>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

add a name alias

@github-actions
Copy link
Copy Markdown
Contributor

Test results for "tests 1"

6 flaky ⚠️ [chromium-library] › library/video.spec.ts:647 › screencast › should capture full viewport `@chromium-ubuntu-22.04-arm-node20`
⚠️ [chromium-library] › library/video.spec.ts:719 › screencast › should work with video+trace `@chromium-ubuntu-22.04-node24`
⚠️ [chromium-library] › library/video.spec.ts:719 › screencast › should work with video+trace `@chromium-ubuntu-22.04-node20`
⚠️ [firefox-library] › library/inspector/cli-codegen-1.spec.ts:1080 › cli codegen › should not throw csp directive violation errors `@firefox-ubuntu-22.04-node20`
⚠️ [firefox-page] › page/page-wait-for-function.spec.ts:104 › should work with strict CSP policy `@firefox-ubuntu-22.04-node20`
⚠️ [playwright-test] › ui-mode-test-output.spec.ts:118 › should collapse repeated console messages for test `@ubuntu-latest-node22`

39242 passed, 847 skipped


Merge workflow run.

@pavelfeldman pavelfeldman merged commit db32e0c into microsoft:main Apr 17, 2026
38 checks passed

Dispatches the native `dragenter`, `dragover`, and `drop` events at the center of the
target element with a synthetic [DataTransfer] carrying the provided files and/or data
entries. Works cross-browser by constructing the [DataTransfer] in the page context.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
entries. Works cross-browser by constructing the [DataTransfer] in the page context.
entries.


**Details**

Dispatches the native `dragenter`, `dragover`, and `drop` events at the center of the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

at the center is wrong when position is passed. I'd recommend inside the target element instead.

}

async _drop(progress: Progress, inputFileItems: InputFilesItems, data: { mimeType: string, value: string }[], options: types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
const { filePayloads, localPaths } = inputFileItems;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why don't we reuse prepareFilesForUpload?

const disposeHandle = handle !== this;
try {
const result = await progress.race(handle.evaluate((node: Node, { payloads, data, point }) => {
if (!node.isConnected || node.nodeType !== 1 /* ELEMENT_NODE */)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we please extract this large evaluated function into InjectedScript.drop()?

Comment on lines +666 to +682
if (localPaths && !filePayloads) {
// Co-located server/browser: read files into buffers so File objects can be
// constructed in page context.
payloads = await Promise.all(localPaths.map(async p => ({
name: path.basename(p),
mimeType: mime.getType(p) || 'application/octet-stream',
buffer: (await fs.promises.readFile(p)).toString('base64'),
lastModifiedMs: (await fs.promises.stat(p)).mtimeMs,
})));
} else {
payloads = (filePayloads ?? []).map(p => ({
name: p.name,
mimeType: p.mimeType || 'application/octet-stream',
buffer: p.buffer,
lastModifiedMs: p.lastModifiedMs,
}));
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we instead force prepareFilesForUpload to read files into payloads right away?

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.

3 participants