Commit b302da82e1 for woocommerce
commit b302da82e12e40b56dff74a569d992517ba7617c
Author: Tony Arcangelini <33258733+arcangelini@users.noreply.github.com>
Date: Fri Nov 28 16:49:32 2025 +0100
Email Editor: fix slow image rendering (#62118)
Co-authored-by: github-actions <github-actions@github.com>
diff --git a/packages/php/email-editor/changelog/62118-fix-email-editor-image-rendering-lag b/packages/php/email-editor/changelog/62118-fix-email-editor-image-rendering-lag
new file mode 100644
index 0000000000..b95a47303d
--- /dev/null
+++ b/packages/php/email-editor/changelog/62118-fix-email-editor-image-rendering-lag
@@ -0,0 +1,4 @@
+Significance: minor
+Type: fix
+
+Email Editor: retrieve image width in a more efficient manner.
\ No newline at end of file
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php
index 11111c1ddb..af09f8077b 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-image.php
@@ -98,20 +98,62 @@ class Image extends Abstract_Block_Renderer {
// Can't determine any width let's go with 100%.
if ( ! isset( $parsed_block['email_attrs']['width'] ) ) {
$parsed_block['attrs']['width'] = '100%';
+ return $parsed_block;
}
$max_width = Styles_Helper::parse_value( $parsed_block['email_attrs']['width'] );
+ $image_size = null;
+
if ( $image_url ) {
- $upload_dir = wp_upload_dir();
- $image_path = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $image_url );
- $image_size = wp_getimagesize( $image_path );
-
- $image_size = $image_size ? $image_size[0] : $max_width;
- $width = min( $image_size, $max_width );
- } else {
- $width = $max_width;
+ // Try to extract width from URL query parameter if it exists.
+ $parsed_url = wp_parse_url( $image_url );
+ if ( isset( $parsed_url['query'] ) ) {
+ parse_str( $parsed_url['query'], $query_params );
+ if ( isset( $query_params['w'] ) && is_numeric( $query_params['w'] ) && $query_params['w'] > 0 ) {
+ $image_size = (int) $query_params['w'];
+ }
+ }
+
+ // Next we check the attachment data if it has an ID.
+ if ( ! isset( $image_size ) ) {
+ $attachment_id = $parsed_block['attrs']['id'] ?? null;
+ if ( $attachment_id ) {
+ $size_slug = $parsed_block['attrs']['sizeSlug'] ?? 'large';
+
+ // Check the metadata first.
+ $metadata = wp_get_attachment_metadata( $attachment_id );
+ if ( $metadata ) {
+ if ( isset( $metadata['sizes'][ $size_slug ]['width'] ) ) {
+ $image_size = (int) $metadata['sizes'][ $size_slug ]['width'];
+ } elseif ( 'full' === $size_slug && isset( $metadata['width'] ) ) {
+ $image_size = (int) $metadata['width'];
+ }
+ }
+
+ // Try to get dimensions from wp_get_attachment_image_src if metadata didn't have it.
+ if ( ! isset( $image_size ) ) {
+ $image_src = wp_get_attachment_image_src( $attachment_id, $size_slug );
+ if ( $image_src && isset( $image_src[1] ) ) {
+ $image_size = (int) $image_src[1];
+ }
+ }
+ }
+ }
+
+ // Fallback to wp_getimagesize if we still don't have a size.
+ if ( ! isset( $image_size ) ) {
+ $upload_dir = wp_upload_dir();
+ $image_path = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $image_url );
+ $result = wp_getimagesize( $image_path );
+ if ( $result ) {
+ $image_size = (int) $result[0];
+ }
+ }
}
+ // Use the found image size or fall back to max_width.
+ $width = isset( $image_size ) ? min( $image_size, $max_width ) : $max_width;
+
$parsed_block['attrs']['width'] = "{$width}px";
return $parsed_block;
}
diff --git a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Image_Test.php b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Image_Test.php
index 3f63d2a550..9682a3e9bb 100644
--- a/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Image_Test.php
+++ b/packages/php/email-editor/tests/integration/Integrations/Core/Renderer/Blocks/Image_Test.php
@@ -268,4 +268,108 @@ class Image_Test extends \Email_Editor_Integration_Test_Case {
$this->assertStringNotContainsString( 'javascript:', $rendered );
$this->assertStringNotContainsString( 'alert("xss")', $rendered );
}
+
+ /**
+ * Test it extracts width from URL query parameter
+ */
+ public function testItExtractsWidthFromUrlQueryParameter(): void {
+ $image_content = '
+ <figure class="wp-block-image alignleft size-full is-style-default">
+ <img src="https://test.com/wp-content/uploads/2023/05/image.jpg?w=500" alt="" style="" srcset="https://test.com/wp-content/uploads/2023/05/image.jpg 1000w"/>
+ </figure>
+ ';
+ $parsed_image = $this->parsed_image;
+ unset( $parsed_image['attrs']['width'] ); // Remove width to test fallback logic.
+ $parsed_image['email_attrs']['width'] = '600px'; // Set max width.
+ $parsed_image['innerHTML'] = $image_content;
+
+ $rendered = $this->image_renderer->render( $image_content, $parsed_image, $this->rendering_context );
+
+ // Should use width from URL parameter (500px), which is less than max (600px).
+ $this->assertStringContainsString( 'width="500"', $rendered );
+ $this->assertStringContainsString( 'width:500px;', $rendered );
+ }
+
+ /**
+ * Test it respects max width when URL parameter is larger
+ */
+ public function testItRespectsMaxWidthWhenUrlParameterIsLarger(): void {
+ $image_content = '
+ <figure class="wp-block-image alignleft size-full is-style-default">
+ <img src="https://test.com/wp-content/uploads/2023/05/image.jpg?w=800" alt="" style="" srcset="https://test.com/wp-content/uploads/2023/05/image.jpg 1000w"/>
+ </figure>
+ ';
+ $parsed_image = $this->parsed_image;
+ unset( $parsed_image['attrs']['width'] ); // Remove width to test fallback logic.
+ $parsed_image['email_attrs']['width'] = '600px'; // Set max width.
+ $parsed_image['innerHTML'] = $image_content;
+
+ $rendered = $this->image_renderer->render( $image_content, $parsed_image, $this->rendering_context );
+
+ // Should use max width (600px) when URL parameter (800px) is larger.
+ $this->assertStringContainsString( 'width="600"', $rendered );
+ $this->assertStringContainsString( 'width:600px;', $rendered );
+ }
+
+ /**
+ * Test it falls back to 100% when no width information is available
+ */
+ public function testItFallsBackTo100PercentWhenNoWidthInfoAvailable(): void {
+ $image_content = '
+ <figure class="wp-block-image alignleft size-full is-style-default">
+ <img src="https://test.com/wp-content/uploads/2023/05/image.jpg" alt="" style="" srcset=""/>
+ </figure>
+ ';
+ $parsed_image = $this->parsed_image;
+ unset( $parsed_image['attrs']['width'] ); // Remove width to test fallback logic.
+ unset( $parsed_image['email_attrs']['width'] ); // Remove email_attrs width to trigger 100% fallback.
+ $parsed_image['innerHTML'] = $image_content;
+
+ $rendered = $this->image_renderer->render( $image_content, $parsed_image, $this->rendering_context );
+
+ // Should fall back to 100% width when no width information is available.
+ $this->assertStringContainsString( 'width:100%;', $rendered );
+ }
+
+ /**
+ * Test it ignores invalid URL width parameters
+ */
+ public function testItIgnoresInvalidUrlWidthParameters(): void {
+ $image_content = '
+ <figure class="wp-block-image alignleft size-full is-style-default">
+ <img src="https://test.com/wp-content/uploads/2023/05/image.jpg?w=invalid" alt="" style="" srcset=""/>
+ </figure>
+ ';
+ $parsed_image = $this->parsed_image;
+ unset( $parsed_image['attrs']['width'] ); // Remove width to test fallback logic.
+ $parsed_image['email_attrs']['width'] = '600px'; // Set max width.
+ $parsed_image['innerHTML'] = $image_content;
+
+ $rendered = $this->image_renderer->render( $image_content, $parsed_image, $this->rendering_context );
+
+ // Should fall back to max width when URL parameter is invalid.
+ $this->assertStringContainsString( 'width="600"', $rendered );
+ $this->assertStringContainsString( 'width:600px;', $rendered );
+ }
+
+ /**
+ * Test it ignores negative or zero width parameters
+ */
+ public function testItIgnoresNegativeOrZeroWidthParameters(): void {
+ $image_content = '
+ <figure class="wp-block-image alignleft size-full is-style-default">
+ <img src="https://test.com/wp-content/uploads/2023/05/image.jpg?w=0" alt="" style="" srcset=""/>
+ </figure>
+ ';
+ $parsed_image = $this->parsed_image;
+ unset( $parsed_image['attrs']['width'] ); // Remove width to test fallback logic.
+ $parsed_image['email_attrs']['width'] = '600px'; // Set max width.
+ $parsed_image['innerHTML'] = $image_content;
+
+ $rendered = $this->image_renderer->render( $image_content, $parsed_image, $this->rendering_context );
+
+ // Should fall back to max width when URL parameter is 0 or negative.
+ $this->assertStringContainsString( 'width="600"', $rendered );
+ $this->assertStringContainsString( 'width:600px;', $rendered );
+ }
}