diff --git a/lib/media/load.php b/lib/media/load.php index d53b460e17af1f..99b06a6273383d 100644 --- a/lib/media/load.php +++ b/lib/media/load.php @@ -199,17 +199,22 @@ function gutenberg_media_processing_filter_rest_index( WP_REST_Response $respons add_filter( 'rest_index', 'gutenberg_media_processing_filter_rest_index' ); /** - * Sets a global JS variable to indicate that HEIC canvas-based upload support is available. + * Sets a global JS variable to indicate that client-side media processing is enabled. * - * This flag is set whenever the media processing feature is enabled, - * regardless of whether the browser supports full VIPS-based processing. - * Browsers like Safari can use createImageBitmap() to decode HEIC images - * and convert them to JPEG for server-side sub-size generation. + * The flag gates both processing modes: the full VIPS/WASM pipeline (browsers + * that pass feature detection) and the HEIC canvas fallback used by browsers + * such as Safari that can decode HEIC via createImageBitmap() but lack + * SharedArrayBuffer support. The browser-capability check happens client-side. */ -function gutenberg_set_heic_upload_support_flag() { - wp_add_inline_script( 'wp-block-editor', 'window.__heicUploadSupport = true', 'before' ); +function gutenberg_set_client_side_media_processing_flag() { + // Re-check the filter at action time, since other plugins (loaded after Gutenberg) + // may have added a filter to disable client-side media processing. + if ( ! gutenberg_is_client_side_media_processing_enabled() ) { + return; + } + wp_add_inline_script( 'wp-block-editor', 'window.__clientSideMediaProcessing = true', 'before' ); } -add_action( 'admin_init', 'gutenberg_set_heic_upload_support_flag' ); +add_action( 'admin_init', 'gutenberg_set_client_side_media_processing_flag' ); /** * Deletes the source-format companion file when its attachment is deleted. @@ -258,17 +263,6 @@ function gutenberg_delete_heic_companion_file( int $post_id ): bool { // Everything below requires cross-origin isolation (Document-Isolation-Policy) // and SharedArrayBuffer support, which is only available in Chromium 137+. -/** - * Sets a global JS variable to indicate that client-side media processing is enabled. - */ -function gutenberg_set_client_side_media_processing_flag() { - if ( ! gutenberg_is_client_side_media_processing_enabled() ) { - return; - } - wp_add_inline_script( 'wp-block-editor', 'window.__clientSideMediaProcessing = true', 'before' ); -} -add_action( 'admin_init', 'gutenberg_set_client_side_media_processing_flag' ); - /** * Filters the list of rewrite rules formatted for output to an .htaccess file. * diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 34c6710e3e722c..3d0e9199054ace 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -6,6 +6,10 @@ - Inspector controls in the standard block-supports panels (Typography, Dimensions, Border, Color, Background, Filters) now reflect the value a block inherits from Global Styles when no local override is set. Inherited controls show that value at rest (as a placeholder, preselected option, or resolved value) and mark the label with a dotted underline; setting a local override reveals a reset affordance that clears the override back to the inherited value ([#77894](https://github.com/WordPress/gutenberg/pull/77894)). +### Bug Fixes + +- Gate the HEIC canvas conversion fallback on `window.__clientSideMediaProcessing` instead of the redundant `window.__heicUploadSupport` flag, fixing client-side HEIC conversion in Safari on core WordPress installs ([#80452](https://github.com/WordPress/gutenberg/pull/80452)). + ## 16.0.0 (2026-07-14) ### Internal diff --git a/packages/block-editor/src/components/provider/index.js b/packages/block-editor/src/components/provider/index.js index 379aad102606a9..410f83d5495c2f 100644 --- a/packages/block-editor/src/components/provider/index.js +++ b/packages/block-editor/src/components/provider/index.js @@ -102,7 +102,7 @@ function shouldEnableClientSideMediaProcessing() { * * Returns true when: * 1. Full client-side processing is NOT available (otherwise it handles HEIC already) - * 2. The server has set the __heicUploadSupport flag + * 2. The server has set the __clientSideMediaProcessing flag * 3. The browser supports createImageBitmap + OffscreenCanvas (e.g. Safari) * * @return {boolean} Whether HEIC-only canvas processing should be enabled. @@ -118,7 +118,7 @@ function shouldEnableHeicCanvasProcessing() { return false; } - if ( ! window.__heicUploadSupport ) { + if ( ! window.__clientSideMediaProcessing ) { isHeicCanvasEnabledCache = false; return false; } diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index d7375f064b4ae2..be8ae8d0638e28 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Internal + +- Image: Check only `window.__clientSideMediaProcessing` for sideloading status, following the removal of the redundant `window.__heicUploadSupport` flag ([#80452](https://github.com/WordPress/gutenberg/pull/80452)). + ## 10.2.0 (2026-07-14) ### Enhancements diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index ff6cd3eddc3416..bf97311b99b0f5 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -353,11 +353,7 @@ export function ImageEdit( { const isSideloading = useSelect( ( select ) => { - if ( - ( ! window.__clientSideMediaProcessing && - ! window.__heicUploadSupport ) || - ! id - ) { + if ( ! window.__clientSideMediaProcessing || ! id ) { return false; } return select( uploadStore ).isUploadingById( id ); diff --git a/packages/e2e-tests/plugins/disable-cross-origin-isolation.php b/packages/e2e-tests/plugins/disable-cross-origin-isolation.php new file mode 100644 index 00000000000000..f697a369183fab --- /dev/null +++ b/packages/e2e-tests/plugins/disable-cross-origin-isolation.php @@ -0,0 +1,18 @@ + { test.beforeAll( async ( { requestUtils } ) => { - // Disable full client-side media processing while leaving the - // HEIC canvas-conversion mode active (`window.__heicUploadSupport` - // is set regardless). This mirrors Safari, where full client-side - // processing is unsupported but HEIC files are still converted to - // JPEG via createImageBitmap + OffscreenCanvas before upload. + // Disable cross-origin isolation while leaving client-side media + // processing enabled: without SharedArrayBuffer the full VIPS + // pipeline fails feature detection, so the editor falls back to the + // HEIC canvas-conversion mode. This mirrors Safari, where full + // client-side processing is unsupported but HEIC files are still + // converted to JPEG via createImageBitmap + OffscreenCanvas before + // upload. await requestUtils.activatePlugin( - 'gutenberg-test-plugin-disable-client-side-media-processing' + 'gutenberg-test-plugin-disable-cross-origin-isolation' ); } ); @@ -31,7 +33,7 @@ test.describe( 'Upload progress snackbar (HEIC-only canvas mode) (@webkit)', () test.afterAll( async ( { requestUtils } ) => { await requestUtils.deactivatePlugin( - 'gutenberg-test-plugin-disable-client-side-media-processing' + 'gutenberg-test-plugin-disable-cross-origin-isolation' ); } ); @@ -59,6 +61,19 @@ test.describe( 'Upload progress snackbar (HEIC-only canvas mode) (@webkit)', () const imageBlock = editor.canvas.locator( 'role=document[name="Block: Image"i]' ); + + // The canvas conversion mode sideloads the converted JPEG. A plain + // server-side upload never hits the sideload endpoint, so waiting for + // it proves the HEIC canvas path actually ran: the server also + // content-sniffs the fake HEIC's JPEG bytes and renames the file, so + // the src assertion below cannot tell the two paths apart on its own. + const sideloadRequest = page.waitForRequest( + ( request ) => + request.method() === 'POST' && + request.url().includes( 'sideload' ), + { timeout: 60_000 } + ); + await imageBlock .locator( 'data-testid=form-file-upload-input' ) .setInputFiles( { @@ -67,6 +82,8 @@ test.describe( 'Upload progress snackbar (HEIC-only canvas mode) (@webkit)', () buffer, } ); + await sideloadRequest; + const snackbarList = page.locator( '.components-snackbar-list' ); // A single file must use the single-file message throughout. The