Opened 24 hours ago
Last modified 2 hours ago
#65367 new defect (bug)
REST API: expose per-attachment output format and progressive flags for client-side media processing
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | 7.1 | Priority: | normal |
| Severity: | normal | Version: | |
| Component: | REST API | Keywords: | has-patch has-unit-tests |
| Focuses: | Cc: |
Description
Client-side media processing encodes sub-sizes (and, when applicable, the threshold-scaled "scaled" copy) in the browser before uploading. To match server-side output, the browser needs two pieces of information that today are not exposed on the attachment response:
- The output MIME type a given file should be saved as, per the image_editor_output_format filter. The filter is filename- and MIME-aware (
( array $formats, string $filename, string $mime_type )), so a value computed in the REST index without a real filename is incorrect for any non-trivial site. - Whether progressive / interlaced encoding is requested for that MIME type, per the image_save_progressive filter.
Today these values are surfaced (in Gutenberg) on the REST API root index with empty filename and image_save_progressive evaluated against hard-coded MIME types. That is wrong: the filter contract requires the real filename and MIME of the file being saved, and plugins can return different decisions per file. The bypass-labelled Gutenberg PR #75793 moved this information to the per-attachment upload response in the Gutenberg plugin; this ticket backports the same change to Core.
Proposed change
Add two readonly fields to WP_REST_Attachments_Controller, in the edit
context, alongside the existing exif_orientation field:
"image_output_format": "image/webp", "image_save_progressive": false
image_output_formatreturns the resolved output MIME type whenimage_editor_output_formatmaps the source MIME to a different one (e.g. JPEG → WebP), ornullwhen no conversion is needed. The filter is called with the real attached filename and MIME type, so plugins can make per-file decisions.image_save_progressivereturns the boolean result of theimage_save_progressivefilter for the attachment's MIME type.- Both fields are evaluated only for image attachments (
wp_attachment_is_image()). prepare_item_for_response()and the upload / sideload code paths recompute the values after lifting the temporary__return_empty_array/__return_zeroguards that the controller installs around the initial response, so the values surfaced reflect the site's real configuration.
This deprecates the legacy generic surfacing of image_output_formats, jpeg_interlaced, png_interlaced, and gif_interlaced on the REST API root index (which used empty filenames and hard-coded MIME types). Those entries should be removed when the new per-attachment fields land.
Patch / PRs
- Gutenberg PR (merged 2026-04-23): https://github.com/WordPress/gutenberg/pull/75793
- Gutenberg issue: https://github.com/WordPress/gutenberg/issues/75784
- Core backport PR: (open after this ticket is filed; will be linked from PR description)
- Related ticket — sibling
image_qualityfield: https://core.trac.wordpress.org/ticket/65262 (Gutenberg #78420 / core #11856). - Context: client-side media processing was reverted and re-introduced for 7.1; the Gutenberg PR was originally labelled
No Core Sync Requiredbecause client-side media was Gutenberg-only at the time. The Core sync is now required.
Tests
New coverage in tests/phpunit/tests/rest-api/rest-attachments-controller.php:
test_image_output_format_schema- field is present, nullable string,readonly,edit-context.test_image_save_progressive_schema- field is present, boolean,readonly,edit-context.test_image_output_format_default- returnsnullwhen no filter remaps the source MIME.test_image_output_format_with_filter- JPEG→WebP filter is reflected in the response and uses the real attached filename.test_image_save_progressive_default- returnsfalsefor JPEG with no filter.test_image_save_progressive_with_filter- a MIME-awareimage_save_progressivefilter is honored.test_get_item_schema- property count updated to match the two new fields.- Existing
sideload/finalizetest coverage extended to assert the recomputed values after the controller's internal guards are removed.
Change History (3)
This ticket was mentioned in PR #12007 on WordPress/wordpress-develop by @adamsilverstein.
24 hours ago
#1
- Keywords has-patch has-unit-tests added; needs-patch needs-unit-tests removed
@adamsilverstein commented on PR #12007:
23 hours ago
#2
Added a follow-up commit that completes the #75793 migration in Core: it removes the now-redundant, file-less image_output_formats, jpeg_interlaced, png_interlaced, and gif_interlaced keys from WP_REST_Server::get_index() (and updates the generated qunit fixture).
Those values were exposed on the REST root index without per-file context, so image_editor_output_format ran with an empty filename and couldn't make per-file decisions. They're now superseded by the per-attachment image_output_format / image_save_progressive response fields added in this PR. image_sizes and image_size_threshold remain on the index since they don't need file context.
This also resolves the failing Gutenberg media processing test (the plugin asserts those keys are absent from the index). A temporary stopgap that disables those assertions until this merges is in WordPress/gutenberg#78788.
## Summary
Adds two readonly fields to
WP_REST_Attachments_Controller, in theeditcontext, alongside the existingexif_orientationfield:image_output_format— returns the resolved output MIME type whenimage_editor_output_formatmaps the source MIME to a different one (e.g. JPEG → WebP), ornullwhen no conversion is needed. The filter is invoked with the real attached filename and MIME type so plugins can make per-file decisions, mirroring the wayWP_Image_Editor::set_quality()resolves the output format.image_save_progressive— returns the boolean result of theimage_save_progressivefilter for the attachment's MIME type.Both fields are evaluated only for image attachments (
wp_attachment_is_image()).## Why
Client-side media processing encodes sub-sizes (and the threshold-scaled "scaled" copy) in the browser. To match server-side output, the browser needs to know the output MIME type and whether to use progressive encoding for the specific file being saved. Those decisions are filename- and MIME-aware (
image_editor_output_formatsignature is( array $formats, string $filename, string $mime_type )), so values computed at the REST API root index — without a real filename — are incorrect for any non-trivial site.Putting the resolved values on the per-attachment response gives the client real context to act on, mirroring the existing
exif_orientationpattern.## Trac ticket
https://core.trac.wordpress.org/ticket/65367
## Backport from Gutenberg
No Core Sync Requiredlabel; the Core sync is now required since client-side media processing has been re-introduced in Core).## Tests
New coverage in
tests/phpunit/tests/rest-api/rest-attachments-controller.php:test_image_output_format_and_progressive_schema— both fields are present, typed correctly,readonly,edit-context only.test_image_output_format_and_progressive_defaults_in_create_response— JPEG default response hasimage_output_format = nullandimage_save_progressive = false.test_image_output_format_with_custom_filter— a JPEG→WebPimage_editor_output_formatfilter is reflected in the response, and the filter sees the real attached filename and MIME type.test_image_save_progressive_with_custom_filter— a MIME-awareimage_save_progressivefilter is honored.test_image_output_format_skipped_for_non_image— non-image attachments do not surface the fields.Local results:
WP_Test_REST_Attachments_Controllerruns green (134 tests, 809 assertions, 2 skipped). PHPCS clean.## Follow-up
A follow-up could remove the now-redundant index-level
image_output_formats,jpeg_interlaced,png_interlaced,gif_interlacedfields fromWP_REST_Server::get_index(), but that needs coordinated updates to JS preloads insite-editor.phpandedit-form-blocks.phpand is left for a separate change.