Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(locator): add drop API for files and clipboard-like data by pavelfeldman · Pull Request #40283 · microsoft/playwright · GitHub
Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(locator): add drop API for files and clipboard-like data by pavelfeldman · Pull Request #40283 · microsoft/playwright · GitHub
Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(locator): add drop API for files and clipboard-like data by pavelfeldman · Pull Request #40283 · microsoft/playwright · GitHub
Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(locator): add drop API for files and clipboard-like data by pavelfeldman · Pull Request #40283 · microsoft/playwright · GitHub
Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(locator): add drop API for files and clipboard-like data by pavelfeldman · Pull Request #40283 · microsoft/playwright · GitHub
Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); feat(locator): add drop API for files and clipboard-like data by pavelfeldman · Pull Request #40283 · microsoft/playwright · GitHub
Skip to content

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

Merged
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft:mainfrom
pavelfeldman:feat-locator-drop
Apr 17, 2026
Merged

feat(locator): add drop API for files and clipboard-like data#40283
Pavel Feldman (pavelfeldman) merged 1 commit into
microsoft: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.

- `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
Pavel Feldman (pavelfeldman) merged commit db32e0c into microsoft:mainApr 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?

Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Yury Semikhatsky (yury-s) added a commit to yury-s/playwright-java that referenced this pull request May 6, 2026
Ports the following upstream client-side PRs:
- microsoft/playwright#39767 - feat: Add `location()` to `WebError`
- microsoft/playwright#39950 - feat: page version of toMatchAriaSnapshot
- microsoft/playwright#40083 - feat(locator): add `description` option to getByRole
- microsoft/playwright#40092 - feat(expect): support pseudo-element in toHaveCSS
- microsoft/playwright#40159 - feat(tracing): add tracing.startHar / tracing.stopHar
- microsoft/playwright#40196 - chore: remove deprecated `handle` option from exposeBinding
- microsoft/playwright#40215 - feat(api): Locator.highlight({ style })
- microsoft/playwright#40219 - feat(api): Page.hideHighlight, Locator.hideHighlight, highlight() returns AutoCloseable
- microsoft/playwright#40283 - feat(locator): add drop API for files and clipboard-like data
- microsoft/playwright#40315 - feat(browser): Browser.onContext / offContext
- microsoft/playwright#40341 - feat(browsercontext): mirror Page lifecycle events on BrowserContext
- microsoft/playwright#40377 - feat(route): expose WebSocket subprotocols on WebSocketRoute
- microsoft/playwright#40606 - feat(api): unify WebError and ConsoleMessage location
- microsoft/playwright#40651 - docs: alias drop-payload Object types
Protocol shape change for FrameExpectResult.received (now `{ value, ariaSnapshot }` instead
of a bare SerializedValue) — updated AssertionsBase to unwrap it.
Generator updates: added new types to relevant import lists; share topLevelTypes between
the assertion and non-assertion generate runs so PseudoElement gets emitted into options/.
Sign up for freeto 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

@pavelfeldman@yury-s@dgozman