From b5bc2b169e8db3c4094f6dd8c530a9b17709cfed Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Mon, 13 Jul 2026 14:35:36 -0700 Subject: [PATCH 1/4] REST API: Expose image_strip_meta and image_max_bit_depth in the index. On the client-side media processing path no server-side WP_Image_Editor is instantiated, so the `image_strip_meta` and `image_max_bit_depth` filters never influence generated images. Expose their filtered values on the REST API index (alongside `image_size_threshold`) so the client encoder can honor them: metadata stripping can be disabled and the output bit depth capped for client-generated sub-sizes. Since the server never decodes the image on this path, `image_max_bit_depth` is applied with 16 (the maximum depth the client encoder can produce) as both the value and the current depth, so plugins lowering the cap still take effect. See https://github.com/WordPress/gutenberg/issues/80216 and https://github.com/WordPress/gutenberg/pull/80218. See #64804. --- .../rest-api/class-wp-rest-server.php | 14 ++++ tests/phpunit/tests/rest-api/rest-server.php | 66 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php index af73b80103546..5b537fa996005 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -1378,6 +1378,20 @@ public function get_index( $request ) { /** This filter is documented in wp-admin/includes/image.php */ $available['image_size_threshold'] = (int) apply_filters( 'big_image_size_threshold', 2560, array( 0, 0 ), '', 0 ); + + /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ + $available['image_strip_meta'] = (bool) apply_filters( 'image_strip_meta', true ); + + /* + * On the server, this filter receives the decoded image's actual bit depth. + * The client path never decodes the image on the server, so the filter is + * applied with 16 (the maximum depth the client encoder can produce) as + * both the value and the current depth. The client caps its output bit + * depth at the filtered value, so a plugin lowering it (e.g. to 8) takes + * effect on client-generated images too. + */ + /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ + $available['image_max_bit_depth'] = (int) apply_filters( 'image_max_bit_depth', 16, 16 ); } $response = new WP_REST_Response( $available ); diff --git a/tests/phpunit/tests/rest-api/rest-server.php b/tests/phpunit/tests/rest-api/rest-server.php index f61d2fc745d99..e0e12f6dcf0a0 100644 --- a/tests/phpunit/tests/rest-api/rest-server.php +++ b/tests/phpunit/tests/rest-api/rest-server.php @@ -1207,6 +1207,72 @@ public function test_get_index() { $this->assertArrayHasKey( 'site_icon_url', $data ); } + /** + * @ticket 64804 + * + * @covers WP_REST_Server::get_index + */ + public function test_get_index_should_include_media_processing_settings() { + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + + $server = new WP_REST_Server(); + $request = new WP_REST_Request( 'GET', '/' ); + $index = $server->dispatch( $request ); + $data = $index->get_data(); + + $this->assertArrayHasKey( 'image_sizes', $data ); + $this->assertArrayHasKey( 'image_size_threshold', $data ); + $this->assertArrayHasKey( 'image_strip_meta', $data ); + $this->assertTrue( $data['image_strip_meta'] ); + $this->assertArrayHasKey( 'image_max_bit_depth', $data ); + $this->assertSame( 16, $data['image_max_bit_depth'] ); + } + + /** + * @ticket 64804 + * + * @covers WP_REST_Server::get_index + */ + public function test_get_index_should_not_include_media_processing_settings_without_caps() { + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + + $server = new WP_REST_Server(); + $request = new WP_REST_Request( 'GET', '/' ); + $index = $server->dispatch( $request ); + $data = $index->get_data(); + + $this->assertArrayNotHasKey( 'image_sizes', $data ); + $this->assertArrayNotHasKey( 'image_size_threshold', $data ); + $this->assertArrayNotHasKey( 'image_strip_meta', $data ); + $this->assertArrayNotHasKey( 'image_max_bit_depth', $data ); + } + + /** + * @ticket 64804 + * + * @covers WP_REST_Server::get_index + */ + public function test_get_index_should_honor_media_processing_filters() { + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + add_filter( 'image_strip_meta', '__return_false' ); + add_filter( + 'image_max_bit_depth', + static function ( $max_depth ) { + return min( 8, $max_depth ); + } + ); + + $server = new WP_REST_Server(); + $request = new WP_REST_Request( 'GET', '/' ); + $index = $server->dispatch( $request ); + $data = $index->get_data(); + + $this->assertFalse( $data['image_strip_meta'] ); + $this->assertSame( 8, $data['image_max_bit_depth'] ); + } + /** * @ticket 57902 * From f50738cc8545bf8fcf77b4bf287b6c8420c37b02 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Mon, 13 Jul 2026 15:02:15 -0700 Subject: [PATCH 2/4] Update QUnit REST schema fixture with new index fields CI regenerates wp-api-generated.js from the live REST index and fails when it differs from the committed copy. Add the new image_strip_meta and image_max_bit_depth fields exposed in WP_REST_Server::get_index(). --- tests/qunit/fixtures/wp-api-generated.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 14a26f8301e3b..2591b830a1d5e 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -12919,6 +12919,8 @@ mockedApiResponse.Schema = { } }, "image_size_threshold": 2560, + "image_strip_meta": true, + "image_max_bit_depth": 16, "site_logo": 0, "site_icon": 0, "site_icon_url": "" From 68b3d8e7e9a4a615412f2a5d832b995129629c57 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 13 Jul 2026 23:39:18 -0700 Subject: [PATCH 3/4] Fix PHPStan errors in tests --- tests/phpunit/tests/rest-api/rest-server.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-server.php b/tests/phpunit/tests/rest-api/rest-server.php index e0e12f6dcf0a0..036c835657e0a 100644 --- a/tests/phpunit/tests/rest-api/rest-server.php +++ b/tests/phpunit/tests/rest-api/rest-server.php @@ -1212,14 +1212,17 @@ public function test_get_index() { * * @covers WP_REST_Server::get_index */ - public function test_get_index_should_include_media_processing_settings() { - wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + public function test_get_index_should_include_media_processing_settings(): void { + $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); + $this->assertIsInt( $user_id ); + wp_set_current_user( $user_id ); add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); $server = new WP_REST_Server(); $request = new WP_REST_Request( 'GET', '/' ); $index = $server->dispatch( $request ); $data = $index->get_data(); + $this->assertIsArray( $data ); $this->assertArrayHasKey( 'image_sizes', $data ); $this->assertArrayHasKey( 'image_size_threshold', $data ); @@ -1234,13 +1237,14 @@ public function test_get_index_should_include_media_processing_settings() { * * @covers WP_REST_Server::get_index */ - public function test_get_index_should_not_include_media_processing_settings_without_caps() { + public function test_get_index_should_not_include_media_processing_settings_without_caps(): void { add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); $server = new WP_REST_Server(); $request = new WP_REST_Request( 'GET', '/' ); $index = $server->dispatch( $request ); $data = $index->get_data(); + $this->assertIsArray( $data ); $this->assertArrayNotHasKey( 'image_sizes', $data ); $this->assertArrayNotHasKey( 'image_size_threshold', $data ); @@ -1253,8 +1257,10 @@ public function test_get_index_should_not_include_media_processing_settings_with * * @covers WP_REST_Server::get_index */ - public function test_get_index_should_honor_media_processing_filters() { - wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + public function test_get_index_should_honor_media_processing_filters(): void { + $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); + $this->assertIsInt( $user_id ); + wp_set_current_user( $user_id ); add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); add_filter( 'image_strip_meta', '__return_false' ); add_filter( From 7e5e76a1c9b64963adaa685893d1086d469a2520 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 13 Jul 2026 23:39:49 -0700 Subject: [PATCH 4/4] Use arrow function for filter --- tests/phpunit/tests/rest-api/rest-server.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-server.php b/tests/phpunit/tests/rest-api/rest-server.php index 036c835657e0a..b8e8cc6f5adcb 100644 --- a/tests/phpunit/tests/rest-api/rest-server.php +++ b/tests/phpunit/tests/rest-api/rest-server.php @@ -1265,9 +1265,7 @@ public function test_get_index_should_honor_media_processing_filters(): void { add_filter( 'image_strip_meta', '__return_false' ); add_filter( 'image_max_bit_depth', - static function ( $max_depth ) { - return min( 8, $max_depth ); - } + static fn ( int $max_depth ) => min( 8, $max_depth ) ); $server = new WP_REST_Server();