Skip to content

feat: add Clipboard.onFilePaste for server-side file paste handling#24485

Queued
Artur- wants to merge 6 commits into
mainfrom
feature/paste-file
Queued

feat: add Clipboard.onFilePaste for server-side file paste handling#24485
Artur- wants to merge 6 commits into
mainfrom
feature/paste-file

Conversation

@Artur-

@Artur- Artur- commented May 30, 2026

Copy link
Copy Markdown
Member

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.

@github-actions

github-actions Bot commented May 30, 2026

Copy link
Copy Markdown

Test Results

 1 442 files  + 2   1 442 suites  +2   1h 25m 39s ⏱️ + 1m 29s
10 153 tests +13  10 085 ✅ +13  68 💤 ±0  0 ❌ ±0 
10 625 runs  +13  10 556 ✅ +13  69 💤 ±0  0 ❌ ±0 

Results for commit d6725ed. ± Comparison against base commit 3c9c485.

♻️ This comment has been updated with latest results.

@Artur- Artur- force-pushed the feature/paste-file branch from bec5ac9 to 1f7d328 Compare June 6, 2026 15:02
@mshabarov mshabarov self-requested a review June 10, 2026 12:54
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.
@Artur- Artur- force-pushed the feature/paste-file branch from 1f7d328 to e9fa853 Compare June 10, 2026 16:10
@Artur- Artur- marked this pull request as ready for review June 10, 2026 16:10

@mshabarov mshabarov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

First round of reviews, need to look at PasteFileHandler and tests.

Comment thread flow-server/src/main/java/com/vaadin/flow/component/clipboard/Clipboard.java Outdated
Comment thread flow-server/src/main/java/com/vaadin/flow/component/clipboard/Clipboard.java Outdated
Comment thread flow-server/src/main/java/com/vaadin/flow/component/clipboard/PasteComplete.java Outdated
totally-not-ai Bot added 2 commits June 10, 2026 19:29
- 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 mshabarov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Second, final round of review.

totally-not-ai Bot added 2 commits June 11, 2026 07:39
…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.
@Artur- Artur- requested a review from mshabarov June 11, 2026 07:51
@sonarqubecloud

Copy link
Copy Markdown

@mshabarov mshabarov added this pull request to the merge queue Jun 11, 2026
Any commits made after this event will not be merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants