Data.
- */
- public function data_provider_test_od_optimize_template_output_buffer(): array {
- return array(
- // Note: The Image Prioritizer plugin removes the loading attribute, and so then Auto Sizes does not then add sizes=auto.
- 'wrongly_lazy_responsive_img' => array(
- 'element_metrics' => array(
- 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]',
- 'isLCP' => false,
- 'intersectionRatio' => 1,
- ),
- 'buffer' => '',
- 'expected' => '',
- ),
-
- 'non_responsive_image' => array(
- 'element_metrics' => array(
- 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]',
- 'isLCP' => false,
- 'intersectionRatio' => 0,
- ),
- 'buffer' => '',
- 'expected' => '',
- ),
-
- 'auto_sizes_added' => array(
- 'element_metrics' => array(
- 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]',
- 'isLCP' => false,
- 'intersectionRatio' => 0,
- ),
- 'buffer' => '',
- 'expected' => '',
- ),
-
- 'auto_sizes_already_added' => array(
- 'element_metrics' => array(
- 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]',
- 'isLCP' => false,
- 'intersectionRatio' => 0,
- ),
- 'buffer' => '',
- 'expected' => '',
- ),
-
- // If Auto Sizes added the sizes=auto attribute but Image Prioritizer ended up removing it due to the image not being lazy-loaded, remove sizes=auto again.
- 'wrongly_auto_sized_responsive_img' => array(
- 'element_metrics' => array(
- 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]',
- 'isLCP' => false,
- 'intersectionRatio' => 1,
- ),
- 'buffer' => '',
- 'expected' => '',
- ),
- );
- }
-
- /**
- * Test auto_sizes_visit_tag().
- *
- * @covers ::auto_sizes_visit_tag
- *
- * @dataProvider data_provider_test_od_optimize_template_output_buffer
- * @phpstan-param array{ xpath: string, isLCP: bool, intersectionRatio: int } $element_metrics
- */
- public function test_od_optimize_template_output_buffer( array $element_metrics, string $buffer, string $expected ): void {
- $this->populate_url_metrics( array( $element_metrics ) );
-
- $html_start_doc = '...';
- $html_end_doc = '';
-
- $buffer = od_optimize_template_output_buffer( $html_start_doc . $buffer . $html_end_doc );
- $buffer = preg_replace( '#.+?]*>#s', '', $buffer );
- $buffer = preg_replace( '#.*$#s', '', $buffer );
-
- $this->assertEquals(
- $this->remove_initial_tabs( $expected ),
- $this->remove_initial_tabs( $buffer ),
- "Buffer snapshot:\n$buffer"
- );
- }
-}
diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php
index 1eb930dba0..71c304a98d 100644
--- a/plugins/dominant-color-images/hooks.php
+++ b/plugins/dominant-color-images/hooks.php
@@ -100,14 +100,18 @@ function dominant_color_img_tag_add_dominant_color( $filtered_image, string $con
return $filtered_image;
}
- // Only apply the dominant color to images that have a src attribute that
- // starts with a double quote, ensuring escaped JSON is also excluded.
- if ( ! str_contains( $filtered_image, ' src="' ) ) {
+ $processor = new WP_HTML_Tag_Processor( $filtered_image );
+ if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
+ return $filtered_image;
+ }
+
+ // Only apply the dominant color to images that have a src attribute.
+ if ( ! is_string( $processor->get_attribute( 'src' ) ) ) {
return $filtered_image;
}
// Ensure to not run the logic below in case relevant attributes are already present.
- if ( str_contains( $filtered_image, ' data-dominant-color="' ) || str_contains( $filtered_image, ' data-has-transparency="' ) ) {
+ if ( null !== $processor->get_attribute( 'data-dominant-color' ) || null !== $processor->get_attribute( 'data-has-transparency' ) ) {
return $filtered_image;
}
@@ -134,34 +138,23 @@ function dominant_color_img_tag_add_dominant_color( $filtered_image, string $con
return $filtered_image;
}
- $data = '';
- $extra_class = '';
-
if ( ! empty( $image_meta['dominant_color'] ) ) {
- $data .= sprintf( 'data-dominant-color="%s" ', esc_attr( $image_meta['dominant_color'] ) );
+ $processor->set_attribute( 'data-dominant-color', $image_meta['dominant_color'] );
- if ( str_contains( $filtered_image, ' style="' ) ) {
- $filtered_image = str_replace( ' style="', ' style="--dominant-color: #' . esc_attr( $image_meta['dominant_color'] ) . '; ', $filtered_image );
- } else {
- $filtered_image = str_replace( 'get_attribute( 'style' ) ) {
+ $style_attribute .= $processor->get_attribute( 'style' );
}
+ $processor->set_attribute( 'style', trim( $style_attribute ) );
}
if ( isset( $image_meta['has_transparency'] ) ) {
$transparency = $image_meta['has_transparency'] ? 'true' : 'false';
- $data .= sprintf( 'data-has-transparency="%s" ', $transparency );
- $extra_class = $image_meta['has_transparency'] ? 'has-transparency' : 'not-transparent';
- }
-
- if ( '' !== $data ) {
- $filtered_image = str_replace( 'set_attribute( 'data-has-transparency', $transparency );
+ $processor->add_class( $image_meta['has_transparency'] ? 'has-transparency' : 'not-transparent' );
}
- return $filtered_image;
+ return $processor->get_updated_html();
}
add_filter( 'wp_content_img_tag', 'dominant_color_img_tag_add_dominant_color', 20, 3 );
diff --git a/plugins/dominant-color-images/load.php b/plugins/dominant-color-images/load.php
index b505d94a8e..6f36b559eb 100644
--- a/plugins/dominant-color-images/load.php
+++ b/plugins/dominant-color-images/load.php
@@ -5,7 +5,7 @@
* Description: Displays placeholders based on an image's dominant color while the image is loading.
* Requires at least: 6.5
* Requires PHP: 7.2
- * Version: 1.1.1
+ * Version: 1.1.2
* Author: WordPress Performance Team
* Author URI: https://make.wordpress.org/performance/
* License: GPLv2 or later
@@ -25,7 +25,7 @@
return;
}
-define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.1.1' );
+define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.1.2' );
require_once __DIR__ . '/helper.php';
require_once __DIR__ . '/hooks.php';
diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt
index 88880c4c4d..2ac409272c 100644
--- a/plugins/dominant-color-images/readme.txt
+++ b/plugins/dominant-color-images/readme.txt
@@ -2,7 +2,7 @@
Contributors: wordpressdotorg
Tested up to: 6.6
-Stable tag: 1.1.1
+Stable tag: 1.1.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Tags: performance, images, dominant color
@@ -47,6 +47,17 @@ Contributions are always welcome! Learn more about how to get involved in the [C
== Changelog ==
+= 1.1.2 =
+
+**Enhancements**
+
+* Use more robust HTML Tag Processor for Image Placeholders. ([1477](https://github.com/WordPress/performance/pull/1477))
+
+**Bug Fixes**
+
+* Re-remove unneeded phpcs:ignore. ([1231](https://github.com/WordPress/performance/pull/1231))
+* Update PHPStan to 1.11.5. ([1318](https://github.com/WordPress/performance/pull/1318))
+
= 1.1.1 =
**Enhancements**
diff --git a/plugins/dominant-color-images/tests/test-dominant-color.php b/plugins/dominant-color-images/tests/test-dominant-color.php
index 9c17b55de1..7214f765cc 100644
--- a/plugins/dominant-color-images/tests/test-dominant-color.php
+++ b/plugins/dominant-color-images/tests/test-dominant-color.php
@@ -183,17 +183,13 @@ public function test_dominant_color_img_tag_add_dominant_color_requires_proper_q
*/
public function data_dominant_color_img_tag_add_dominant_color_requires_proper_quotes(): array {
return array(
- 'double quotes' => array(
+ 'double quotes' => array(
'image' => '',
'expected' => true,
),
- 'single quotes' => array(
+ 'single quotes' => array(
'image' => "",
- 'expected' => false,
- ),
- 'escaped double quotes' => array(
- 'image' => '',
- 'expected' => false,
+ 'expected' => true,
),
);
}
diff --git a/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php
index 287e955f31..926e448625 100644
--- a/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php
+++ b/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php
@@ -40,6 +40,20 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
$xpath = $processor->get_xpath();
+ /**
+ * Gets attribute value.
+ *
+ * @param string $attribute_name Attribute name.
+ * @return string|true|null Normalized attribute value.
+ */
+ $get_attribute_value = static function ( string $attribute_name ) use ( $processor ) {
+ $value = $processor->get_attribute( $attribute_name );
+ if ( is_string( $value ) ) {
+ $value = strtolower( trim( $value, " \t\f\r\n" ) );
+ }
+ return $value;
+ };
+
/*
* When the same LCP element is common/shared among all viewport groups, make sure that the element has
* fetchpriority=high, even though it won't really be needed because a preload link with fetchpriority=high
@@ -47,7 +61,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
*/
$common_lcp_element = $context->url_metrics_group_collection->get_common_lcp_element();
if ( ! is_null( $common_lcp_element ) && $xpath === $common_lcp_element['xpath'] ) {
- if ( 'high' === $processor->get_attribute( 'fetchpriority' ) ) {
+ if ( 'high' === $get_attribute_value( 'fetchpriority' ) ) {
$processor->set_meta_attribute( 'fetchpriority-already-added', true );
} else {
$processor->set_attribute( 'fetchpriority', 'high' );
@@ -81,7 +95,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
} else {
// Otherwise, make sure visible elements omit the loading attribute, and hidden elements include loading=lazy.
$is_visible = $element_max_intersection_ratio > 0.0;
- $loading = (string) $processor->get_attribute( 'loading' );
+ $loading = $get_attribute_value( 'loading' );
if ( $is_visible && 'lazy' === $loading ) {
$processor->remove_attribute( 'loading' );
} elseif ( ! $is_visible && 'lazy' !== $loading ) {
@@ -90,6 +104,23 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
}
// TODO: If an image is visible in one breakpoint but not another, add loading=lazy AND add a regular-priority preload link with media queries (unless LCP in which case it should already have a fetchpriority=high link) so that the image won't be eagerly-loaded for viewports on which it is not shown.
+ // Ensure that sizes=auto is set properly.
+ $sizes = $processor->get_attribute( 'sizes' );
+ if ( is_string( $sizes ) ) {
+ $is_lazy = 'lazy' === $get_attribute_value( 'loading' );
+ $has_auto = $this->sizes_attribute_includes_valid_auto( $sizes );
+
+ if ( $is_lazy && ! $has_auto ) {
+ $processor->set_attribute( 'sizes', "auto, $sizes" );
+ } elseif ( ! $is_lazy && $has_auto ) {
+ // Remove auto from the beginning of the list.
+ $processor->set_attribute(
+ 'sizes',
+ (string) preg_replace( '/^[ \t\f\r\n]*auto[ \t\f\r\n]*(,[ \t\f\r\n]*)?/i', '', $sizes )
+ );
+ }
+ }
+
// If this element is the LCP (for a breakpoint group), add a preload link for it.
foreach ( $context->url_metrics_group_collection->get_groups_by_lcp_element( $xpath ) as $group ) {
$link_attributes = array_merge(
@@ -110,8 +141,8 @@ static function ( string $value ): bool {
)
);
- $crossorigin = $processor->get_attribute( 'crossorigin' );
- if ( is_string( $crossorigin ) ) {
+ $crossorigin = $get_attribute_value( 'crossorigin' );
+ if ( null !== $crossorigin ) {
$link_attributes['crossorigin'] = 'use-credentials' === $crossorigin ? 'use-credentials' : 'anonymous';
}
@@ -126,4 +157,24 @@ static function ( string $value ): bool {
return true;
}
+
+ /**
+ * Checks whether the given 'sizes' attribute includes the 'auto' keyword as the first item in the list.
+ *
+ * Per the HTML spec, if present it must be the first entry.
+ *
+ * @since 0.1.4
+ *
+ * @param string $sizes_attr The 'sizes' attribute value.
+ * @return bool True if the 'auto' keyword is present, false otherwise.
+ */
+ private function sizes_attribute_includes_valid_auto( string $sizes_attr ): bool {
+ if ( function_exists( 'wp_sizes_attribute_includes_valid_auto' ) ) {
+ return wp_sizes_attribute_includes_valid_auto( $sizes_attr );
+ } elseif ( function_exists( 'auto_sizes_attribute_includes_valid_auto' ) ) {
+ return auto_sizes_attribute_includes_valid_auto( $sizes_attr );
+ } else {
+ return 'auto' === $sizes_attr || str_starts_with( $sizes_attr, 'auto,' );
+ }
+ }
}
diff --git a/plugins/image-prioritizer/load.php b/plugins/image-prioritizer/load.php
index 550a26c8b8..97b9983ee2 100644
--- a/plugins/image-prioritizer/load.php
+++ b/plugins/image-prioritizer/load.php
@@ -6,7 +6,7 @@
* Requires at least: 6.5
* Requires PHP: 7.2
* Requires Plugins: optimization-detective
- * Version: 0.1.3
+ * Version: 0.1.4
* Author: WordPress Performance Team
* Author URI: https://make.wordpress.org/performance/
* License: GPLv2 or later
@@ -66,7 +66,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi
}
)(
'image_prioritizer_pending_plugin',
- '0.1.3',
+ '0.1.4',
static function ( string $version ): void {
// Define the constant.
diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt
index 02838b6fc1..7e96509afb 100644
--- a/plugins/image-prioritizer/readme.txt
+++ b/plugins/image-prioritizer/readme.txt
@@ -2,7 +2,7 @@
Contributors: wordpressdotorg
Tested up to: 6.6
-Stable tag: 0.1.3
+Stable tag: 0.1.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Tags: performance, optimization, image, lcp, lazy-load
@@ -62,6 +62,12 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu
== Changelog ==
+= 0.1.4 =
+
+**Enhancements**
+
+* Move Auto Sizes logic from Enhanced Responsive Images to Image Prioritizer. ([1476](https://github.com/WordPress/performance/pull/1476))
+
= 0.1.3 =
**Bug Fixes**
diff --git a/plugins/image-prioritizer/tests/test-cases/common-lcp-image-and-lazy-loaded-image-outside-viewport-with-fully-populated-sample-data.php b/plugins/image-prioritizer/tests/test-cases/common-lcp-image-and-lazy-loaded-image-outside-viewport-with-fully-populated-sample-data.php
index 356ddfb0d6..31f6a9eba6 100644
--- a/plugins/image-prioritizer/tests/test-cases/common-lcp-image-and-lazy-loaded-image-outside-viewport-with-fully-populated-sample-data.php
+++ b/plugins/image-prioritizer/tests/test-cases/common-lcp-image-and-lazy-loaded-image-outside-viewport-with-fully-populated-sample-data.php
@@ -55,7 +55,7 @@
Now the following image is definitely outside the initial viewport.
-
+