feat: add Clipboard.onFilePaste for server-side file paste handling#24485
Queued
Artur- wants to merge 6 commits into
Queued
feat: add Clipboard.onFilePaste for server-side file paste handling#24485Artur- wants to merge 6 commits into
Artur- wants to merge 6 commits into
Conversation
bec5ac9 to
1f7d328
Compare
Pastes whose clipboard carries files (screenshots, files copied from a file manager) flow through the supplied UploadHandler — one fetch POST per file with X-Filename + raw body, matching vaadin-upload's wire format. The client-side helper lives in flow-client/Clipboard.ts (window.Vaadin.Flow.clipboard.uploadPastedFiles) so the Java side only emits a one-line filter expression. Each fetch carries X-Paste-Id (monotonic per browser tab) and X-Paste-File-Count headers so server-side handlers can correlate the parallel uploads of one paste. PasteFileHandler offers two flavours that consume those headers: inMemory(consumer) delivers each file as a PasteFile (with newPaste() flagging the first file of each paste), and session() builds a three-step onStart / onFile / onComplete listener that knows when the whole paste has finished delivering. Sessions are per paste id and run independently — a newer paste does not cancel the still-in-flight uploads of an older paste, so two pastes in sequence (or interleaved) both complete on their own timelines. onFilePaste is independent of onPaste; both fire on the same gesture when both are registered.
1f7d328 to
e9fa853
Compare
mshabarov
requested changes
Jun 10, 2026
mshabarov
left a comment
Contributor
There was a problem hiding this comment.
First round of reviews, need to look at PasteFileHandler and tests.
- Drop the mandatory @Push: once a paste's uploads settle the client dispatches a 'vaadin-paste-upload-finished' event with a server-side listener, so Flow performs a round trip that flushes the UI updates the upload callbacks queued via UI.access. The upload response is written only after that access task has applied its changes (uploads run outside the session lock, so access drains synchronously), so the changes are always picked up by the round trip. This mirrors how a regular Upload completes without push. - Install the paste listener via Element.addJsInitializer instead of a dummy paste DOM filter that always returned false. - Use a per-registration UUID for the upload attribute name instead of a shared static counter, matching the StreamResource approach. - Rename PasteFileHandler.inMemory/session to perFile/batch (and SessionBuilder to BatchBuilder) for clearer intent. - Document on PasteComplete that server-side upload errors are observed via a custom UploadHandler/TransferProgressListener, not onComplete. Updates unit tests for the new wiring and removes @Push from the IT view.
mshabarov
requested changes
Jun 11, 2026
mshabarov
left a comment
Contributor
There was a problem hiding this comment.
Second, final round of review.
…e batches Addresses review feedback on PasteFileHandler: - The batch-tracking map previously lived in the handler instance, so a handler shared across components/UIs could accumulate batches and a malformed or missing X-Paste-File-Count header (expected == 0) left a batch that never completed. State now lives on the owning component via ComponentUtil data, so it dies with the component and a shared handler cannot leak. - A newer paste supersedes older unfinished batches (sweep by paste id) and late uploads of a superseded paste are dropped, so a stalled or malformed batch cannot linger. Trade-off: overlapping pastes no longer complete on independent timelines (the latest paste wins). - Document the resource characteristics flagged in review: each file is buffered fully into memory, and PasteFile.fileName() is untrusted and must be sanitized before any filesystem use. Updates the overlapping-pastes unit test to assert the supersede behavior.
Per review discussion: scoping batch state to the owning component already fixes the shared-handler leak, so independent per-paste completion does not need to be sacrificed. Reverts the "newer supersedes older" behavior — pastes again complete on their own timelines and no file of a live paste is dropped. The malformed/never-completing-batch concern is instead bounded by a cap on the number of unfinished pastes tracked per component (oldest open batch is evicted once the cap is reached). The cap is far above any real overlap, so it only trims abandoned or malformed batches. Adds a test for the eviction bound and restores the overlapping-pastes test to assert independent completion.
|
mshabarov
approved these changes
Jun 11, 2026
Any commits made after this event will not be merged.
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.



Pastes whose clipboard carries files (screenshots, files copied from a
file manager) flow through the supplied UploadHandler — one fetch POST
per file with X-Filename + raw body, matching vaadin-upload's wire
format. The client-side helper lives in flow-client/Clipboard.ts
(window.Vaadin.Flow.clipboard.uploadPastedFiles) so the Java side only
emits a one-line filter expression. Each fetch carries X-Paste-Id
(monotonic per browser tab) and X-Paste-File-Count headers so
server-side handlers can correlate the parallel uploads of one paste.
PasteFileHandler offers two flavours that consume those headers:
inMemory(consumer) delivers each file as a PasteFile (with newPaste()
flagging the first file of each paste), and session() builds a
three-step onStart / onFile / onComplete listener that knows when the
whole paste has finished delivering. Sessions are per paste id and run
independently — a newer paste does not cancel the still-in-flight
uploads of an older paste, so two pastes in sequence (or interleaved)
both complete on their own timelines.
onFilePaste is independent of onPaste; both fire on the same gesture
when both are registered.