Make WordPress Core

Opened 3 weeks ago

Last modified 5 days ago

#65661 new enhancement

Media: Enable client-side media uploads in the Media Library

Reported by: adamsilverstein Owned by:
Priority: normal Milestone: Awaiting Review
Component: Media Version:
Severity: normal Keywords: has-patch needs-testing has-unit-tests
Cc: Focuses:

Description

WordPress 7.1 introduced client-side media processing (wasm-vips): in a cross-origin isolated context, the block editor uploads the original image via the REST API and generates sub-sizes in the browser, which are then sideloaded and finalized. This offloads image processing from the server, works on hosts without Imagick/GD constraints, and speeds up uploads.

However, the pipeline currently only runs in the block editor. The Media Library grid (wp-admin/upload.php) — one of the primary places users upload media — still uploads via wp.Uploader/plupload to async-upload.php, with all image processing done server-side. Users uploading through the Media Library miss out on client-side processing entirely, and the two surfaces behave inconsistently.

Proposed change

Extend the client-side pipeline to the Media Library grid:

  1. Cross-origin isolation on the grid. Hook a new wp_set_up_media_library_cross_origin_isolation() on load-upload.php, reusing the existing wp_start_cross_origin_isolation_output_buffer() (Document-Isolation-Policy, Chromium 137+). Gated to grid mode and to users with upload_files. List mode is untouched: it has no pipeline integration, so it should not carry isolation side effects.
  1. Route grid uploads through the pipeline. A new media-library-upload admin script configures the @wordpress/upload-media store and intercepts plupload's FilesAdded event at a higher priority, routing each file through the pipeline (REST upload of the original, client-side thumbnails, sideload, finalize). The grid UI is preserved: the script mirrors wp-plupload's placeholder tiles, upload progress, queue reset, and error sidebar.
  1. Graceful degradation. When the browser is not cross-origin isolated (e.g. non-Chromium browsers, older Chromium) or lacks client-side support (no SharedArrayBuffer, low memory, data-saver mode), the script no-ops and the classic plupload flow keeps handling uploads unchanged. Degradation, never data loss.

The implementation was developed and validated in the client-side-media-everywhere plugin (PR #49), where the equivalent end-to-end suite passes on Chromium, Firefox, and WebKit against core trunk.

Scope notes

  • In scope: upload.php grid mode — both drag-and-drop and "Add New", which flow through the single grid wp.Uploader instance.
  • Out of scope (follow-ups): the list-mode "Add New" uploader (media-new.php, a separate uploader path) and a beforeunload warning while uploads are in flight.

Testing instructions

  1. Use Chrome 137+ on a secure origin (HTTPS or localhost).
  2. Go to Media > Library in grid mode and upload a JPEG via drag-and-drop or "Add New".
  3. In DevTools > Network, observe a POST to /wp/v2/media, one or more POSTs to /wp/v2/media/<id>/sideload, one POST to /wp/v2/media/<id>/finalize, and no request to async-upload.php.
  4. Confirm the grid tile shows progress and resolves to the finished attachment, and that sub-sizes exist in the attachment metadata.
  5. In Firefox/Safari (or list mode), confirm uploads still work via the classic async-upload.php path.

The PR includes 17 new PHPUnit tests and a Playwright E2E spec covering the isolation header, the REST upload flow, and the error path.

PR: https://github.com/WordPress/wordpress-develop/pull/12585

Change History (8)

This ticket was mentioned in PR #12585 on WordPress/wordpress-develop by @adamsilverstein.


3 weeks ago
#1

  • Keywords has-unit-tests added

## What

WordPress 7.1's client-side media pipeline (wasm-vips) only runs in the block editor: it swaps the editor's mediaUpload setting and never touches the Media Library grid, which uploads via wp.Uploader/plupload to async-upload.php. This PR extends the pipeline to the Media Library grid at upload.php, so grid uploads are processed in the browser (REST upload of the original, client-side thumbnails, sideload, finalize) instead of server-side.

This ports client-side-media-everywhere PR #49 (see issue #44) into core, adapted to core's DIP-only isolation model.

## How

Commit 1 - Extend cross-origin isolation to the grid. Hooks a new wp_set_up_media_library_cross_origin_isolation() on load-upload.php, reusing core's existing wp_start_cross_origin_isolation_output_buffer() (Document-Isolation-Policy, Chromium 137+). It is gated to grid mode (resolved the same way upload.php resolves it later in the request, via a new wp_get_media_library_mode()) and to users with upload_files. Unlike the plugin, no COEP/COOP fallback path is needed: core's isolation is DIP-only across all screens.

Commit 2 - Route grid uploads through the pipeline. A new media-library-upload script (vanilla IIFE in src/js/_enqueues/admin/, matching the surrounding admin scripts) configures the @wordpress/upload-media store via MediaUploadProvider (useSubRegistry: false), wraps wp.Uploader.prototype.init to intercept FilesAdded at a higher plupload priority, and routes each file through the store while mirroring wp-plupload's placeholder tiles, progress, queue reset, and error sidebar so the grid UI works unchanged. mediaSideload/mediaFinalize are thin apiFetch wrappers rather than the private @wordpress/media-utils APIs. The script is enqueued from the upload.php grid branch via wp_enqueue_media_library_upload(), with pipeline settings (wp_get_media_library_upload_settings(): max upload size, allowed mime types, registered sub-sizes, big-image threshold, strip-meta and bit-depth filters) passed as an inline script. When the browser is not cross-origin isolated or lacks client-side support, the script no-ops and classic plupload keeps handling uploads - degradation, never data loss.

Commit 3 - E2E coverage. New spec asserting the DIP header on upload.php grid (and its absence in list mode), the happy-path upload (create + sideload + finalize via REST, zero async-upload.php requests), and the disallowed-file-type error path. Playwright's Chromium ships without Document-Isolation-Policy support, so the upload assertions skip when the context is not isolated; the header assertions always run.

## Testing

  • 17 new PHPUnit tests (wpMediaLibraryCrossOriginIsolation.php, wpEnqueueMediaLibraryUpload.php) pass locally, as does the existing wpCrossOriginIsolation.php suite.
  • PHPCS: no new issues on the touched files. JSHint passes on the new script.
  • The equivalent implementation passed the plugin's E2E suite on chromium, firefox, and webkit against core trunk (7.1-alpha), including full-pipeline runs on browsers where isolation is available.

## Scope

In: upload.php grid mode - drag-and-drop and "Add New", both of which flow through the one grid wp.Uploader.

Out: list-mode "Add New" / media-new.php (separate uploader path) and a beforeunload guard for in-progress uploads; both are follow-up candidates.

#2 @adamsilverstein
3 weeks ago

  • Type defect (bug)enhancement

#3 @sanayasir
3 weeks ago

Test Report

I tested this ticket on my local WordPress setup and successfully reproduced the behavior described in the ticket.

Before applying the patch:

Uploading images from Media → Library (Grid View) continued to use the existing Media Library upload flow.
The Grid View was not integrated with the client-side media processing pipeline available in the Block Editor, resulting in inconsistent upload behavior between the two interfaces.

Expected after applying the patch:

Grid View uploads should use the client-side media processing pipeline via the REST API while preserving the existing upload UI.
Unsupported browsers and List View should continue using the classic upload flow.

Result:
I was able to reproduce the issue on my local WordPress setup, and the observed behavior matches the ticket description.

#4 @adamsilverstein
2 weeks ago

  • Summary Media: Enable client-side media uploads in the Media Library gridMedia: Enable client-side media uploads in the Media Library

#5 follow-up: @adamsilverstein
2 weeks ago

Thanks for testing @sanayasir! I have updated the PR to also cover the media library list view screen, can you give it another test.

Also if possible, please try as many different image formats as possible including heic, webp, avif and animated gif or png and report the results.

#6 in reply to: ↑ 5 @sanayasir
12 days ago

Yes, sure! @adamsilverstein I’ll test the updated PR, including the Media Library List View and the different image formats, and report the results.

Last edited 12 days ago by sanayasir (previous) (diff)

#7 @sanayasir
11 days ago

## Patch Testing Feedback

I successfully reproduced and tested the reported issue using the proposed changes.

The Media Library Grid upload flow was tested using Chrome 137+ on a secure origin. When uploading an image through both drag-and-drop and the Add New interface, the upload was successfully routed through the client-side media processing pipeline. The expected REST API requests to /wp/v2/media, /wp/v2/media/<id>/sideload, and /wp/v2/media/<id>/finalize were observed, and no request to async-upload.php was made.

The uploaded media appeared correctly in the Media Library Grid, upload progress was displayed as expected, and the generated image sub-sizes were successfully created.

I also verified the fallback behavior in an unsupported environment and confirmed that uploads continue to work using the existing async-upload.php flow. Media Library List View also continues to use the existing upload flow without introducing unwanted side effects.

Overall, the patch works as expected and successfully extends client-side media processing to the Media Library Grid while maintaining graceful degradation for unsupported environments.

@adamsilverstein commented on PR #12585:


5 days ago
#8

Note: we should make sure we bring over the improved HEIC upload error messages from https://github.com/WordPress/gutenberg/pull/81130 in this PR.

Note: See TracTickets for help on using tickets.