diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php index a341958a4e31f..607843e989b5a 100644 --- a/src/wp-includes/deprecated.php +++ b/src/wp-includes/deprecated.php @@ -5994,3 +5994,43 @@ function wp_update_https_detection_errors() { update_option( 'https_detection_errors', $support_errors ); } + +/** + * Adds `decoding` attribute to an `img` HTML tag. + * + * The `decoding` attribute allows developers to indicate whether the + * browser can decode the image off the main thread (`async`), on the + * main thread (`sync`) or as determined by the browser (`auto`). + * + * By default WordPress adds `decoding="async"` to images but developers + * can use the {@see 'wp_img_tag_add_decoding_attr'} filter to modify this + * to remove the attribute or set it to another accepted value. + * + * @since 6.1.0 + * @deprecated 6.4.0 Use wp_img_tag_add_loading_optimization_attrs() instead. + * @see wp_img_tag_add_loading_optimization_attrs() + * + * @param string $image The HTML `img` tag where the attribute should be added. + * @param string $context Additional context to pass to the filters. + * @return string Converted `img` tag with `decoding` attribute added. + */ +function wp_img_tag_add_decoding_attr( $image, $context ) { + _deprecated_function( __FUNCTION__, '6.4.0', 'wp_img_tag_add_loading_optimization_attrs()' ); + + /* + * Only apply the decoding attribute to images that have a src attribute that + * starts with a double quote, ensuring escaped JSON is also excluded. + */ + if ( ! str_contains( $image, ' src="' ) ) { + return $image; + } + + /** This action is documented in wp-includes/media.php */ + $value = apply_filters( 'wp_img_tag_add_decoding_attr', 'async', $image, $context ); + + if ( in_array( $value, array( 'async', 'sync', 'auto' ), true ) ) { + $image = str_replace( ' $src, - 'class' => "attachment-$size_class size-$size_class", - 'alt' => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ), - 'decoding' => 'async', + 'src' => $src, + 'class' => "attachment-$size_class size-$size_class", + 'alt' => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ), ); /** @@ -1892,11 +1891,6 @@ function wp_filter_content_tags( $content, $context = null ) { // Add loading optimization attributes if applicable. $filtered_image = wp_img_tag_add_loading_optimization_attrs( $filtered_image, $context ); - // Add 'decoding=async' attribute unless a 'decoding' attribute is already present. - if ( ! str_contains( $filtered_image, ' decoding=' ) ) { - $filtered_image = wp_img_tag_add_decoding_attr( $filtered_image, $context ); - } - /** * Filters an img tag within the content for a given context. * @@ -1957,6 +1951,7 @@ function wp_img_tag_add_loading_optimization_attrs( $image, $context ) { $height = preg_match( '/ height=["\']([0-9]+)["\']/', $image, $match_height ) ? (int) $match_height[1] : null; $loading_val = preg_match( '/ loading=["\']([A-Za-z]+)["\']/', $image, $match_loading ) ? $match_loading[1] : null; $fetchpriority_val = preg_match( '/ fetchpriority=["\']([A-Za-z]+)["\']/', $image, $match_fetchpriority ) ? $match_fetchpriority[1] : null; + $decoding_val = preg_match( '/ decoding=["\']([A-Za-z]+)["\']/', $image, $match_decoding ) ? $match_decoding[1] : null; /* * Get loading optimization attributes to use. @@ -1970,12 +1965,53 @@ function wp_img_tag_add_loading_optimization_attrs( $image, $context ) { 'height' => $height, 'loading' => $loading_val, 'fetchpriority' => $fetchpriority_val, + 'decoding' => $decoding_val, ), $context ); - // Images should have source and dimension attributes for the loading optimization attributes to be added. - if ( ! str_contains( $image, ' src="' ) || ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) { + // Images should have source for the loading optimization attributes to be added. + if ( ! str_contains( $image, ' src="' ) ) { + return $image; + } + + if ( empty( $decoding_val ) ) { + /** + * Filters the `decoding` attribute value to add to an image. Default `async`. + * + * Returning a falsey value will omit the attribute. + * + * @since 6.1.0 + * + * @param string|false|null $value The `decoding` attribute value. Returning a falsey value + * will result in the attribute being omitted for the image. + * Otherwise, it may be: 'async', 'sync', or 'auto'. Defaults to false. + * @param string $image The HTML `img` tag to be filtered. + * @param string $context Additional context about how the function was called + * or where the img tag is. + */ + $filtered_decoding_attr = apply_filters( + 'wp_img_tag_add_decoding_attr', + isset( $optimization_attrs['decoding'] ) ? $optimization_attrs['decoding'] : false, + $image, + $context + ); + + // Validate the values after filtering. + if ( isset( $optimization_attrs['decoding'] ) && ! $filtered_decoding_attr ) { + // Unset `decoding` attribute if `$filtered_decoding_attr` is set to `false`. + unset( $optimization_attrs['decoding'] ); + } elseif ( in_array( $filtered_decoding_attr, array( 'async', 'sync', 'auto' ), true ) ) { + $optimization_attrs['decoding'] = $filtered_decoding_attr; + } + + if ( ! empty( $optimization_attrs['decoding'] ) ) { + $image = str_replace( ' null, 'fetchpriority' => null, 'extra_attr' => '', - 'decoding' => 'async', ); if ( empty( $args ) ) { diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index ba88a83fded1a..087d5633521e8 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -1288,11 +1288,10 @@ function get_header_image_tag( $attr = array() ) { $attr = wp_parse_args( $attr, array( - 'src' => $header->url, - 'width' => $width, - 'height' => $height, - 'alt' => $alt, - 'decoding' => 'async', + 'src' => $header->url, + 'width' => $width, + 'height' => $height, + 'alt' => $alt, ) ); diff --git a/src/wp-includes/widgets/class-wp-widget-media-image.php b/src/wp-includes/widgets/class-wp-widget-media-image.php index 23822fb4257b6..2505f26b8860d 100644 --- a/src/wp-includes/widgets/class-wp-widget-media-image.php +++ b/src/wp-includes/widgets/class-wp-widget-media-image.php @@ -240,12 +240,11 @@ public function render_media( $instance ) { } $attr = array( - 'class' => $classes, - 'src' => $instance['url'], - 'alt' => $instance['alt'], - 'width' => $instance['width'], - 'height' => $instance['height'], - 'decoding' => 'async', + 'class' => $classes, + 'src' => $instance['url'], + 'alt' => $instance['alt'], + 'width' => $instance['width'], + 'height' => $instance['height'], ); $loading_optimization_attr = wp_get_loading_optimization_attributes( diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index f1cd87dd717f1..d70b54261aa6c 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -2263,16 +2263,17 @@ public function test_wp_filter_content_tags_srcset_sizes() { $respimg_xhtml, $respimg_html5 ); - $content_filtered = wp_img_tag_add_decoding_attr( $content_filtered, 'the_content' ); // Do not add width, height, and loading. add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); + add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); + remove_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); } /** @@ -2289,7 +2290,6 @@ public function test_wp_filter_content_tags_srcset_sizes() { public function test_wp_filter_content_tags_srcset_sizes_wrong() { $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); $img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' ); - $img = wp_img_tag_add_decoding_attr( $img, 'the_content' ); // Replace the src URL. $image_wrong_src = preg_replace( '|src="[^"]+"|', 'src="http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/foo.jpg"', $img ); @@ -2304,7 +2304,6 @@ public function test_wp_filter_content_tags_srcset_sizes_with_preexisting_srcset // Generate HTML and add a dummy srcset attribute. $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); $img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' ); - $img = wp_img_tag_add_decoding_attr( $img, 'the_content' ); $img = preg_replace( '|]+) />|', '', $img ); // The content filter should return the image unchanged. @@ -2480,7 +2479,6 @@ public function test_wp_filter_content_tags_schemes() { $respimg_https, $respimg_relative ); - $expected = wp_img_tag_add_decoding_attr( $expected, 'the_content' ); $actual = wp_filter_content_tags( $unfiltered ); @@ -2973,16 +2971,17 @@ public function test_wp_filter_content_tags_width_height() { $img_no_width, $img_no_height ); - $content_filtered = wp_img_tag_add_decoding_attr( $content_filtered, 'the_content' ); // Do not add loading, srcset, and sizes. add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); } /** @@ -3004,6 +3003,8 @@ public function test_wp_filter_content_tags_loading_lazy() { $iframe = ''; $iframe_no_width_height = ''; + add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); + $lazy_img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' ); $lazy_img_xhtml = wp_img_tag_add_loading_optimization_attrs( $img_xhtml, 'test' ); $lazy_img_html5 = wp_img_tag_add_loading_optimization_attrs( $img_html5, 'test' ); @@ -3054,7 +3055,6 @@ public function test_wp_filter_content_tags_loading_lazy() { $iframe_eager, $iframe_no_width_height ); - $content_filtered = wp_img_tag_add_decoding_attr( $content_filtered, 'the_content' ); // Do not add width, height, srcset, and sizes. add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); @@ -3064,6 +3064,7 @@ public function test_wp_filter_content_tags_loading_lazy() { remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); } /** @@ -3074,7 +3075,6 @@ public function test_wp_filter_content_tags_loading_lazy() { public function test_wp_filter_content_tags_loading_lazy_opted_in() { $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); $lazy_img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' ); - $lazy_img = wp_img_tag_add_decoding_attr( $lazy_img, 'the_content' ); $iframe = ''; $lazy_iframe = wp_iframe_tag_add_loading_attr( $iframe, 'test' ); @@ -3104,7 +3104,6 @@ public function test_wp_filter_content_tags_loading_lazy_opted_in() { */ public function test_wp_filter_content_tags_loading_lazy_opted_out() { $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); - $img = wp_img_tag_add_decoding_attr( $img, 'the_content' ); $iframe = ''; $content = ' @@ -3119,10 +3118,12 @@ public function test_wp_filter_content_tags_loading_lazy_opted_out() { // Disable globally for all tags. add_filter( 'wp_lazy_loading_enabled', '__return_false' ); + add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); $this->assertSame( $content, wp_filter_content_tags( $content ) ); remove_filter( 'wp_lazy_loading_enabled', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); } /** @@ -3186,6 +3187,8 @@ public function test_wp_img_tag_add_loading_attr_opt_out() { * Test that decoding="async" is not applied to img tags with single quotes. * * @ticket 56969 + * + * @expectedDeprecated wp_img_tag_add_decoding_attr */ public function test_wp_img_tag_add_decoding_attr_with_single_quotes() { $img = ""; @@ -3437,10 +3440,6 @@ public function data_wp_get_attachment_image_decoding_attr() { 'decoding' => false, 'expected' => 'no value', ), - 'null' => array( - 'decoding' => null, - 'expected' => 'no value', - ), 'zero' => array( 'decoding' => 0, 'expected' => 'no value', @@ -3719,6 +3718,7 @@ public function data_wp_get_loading_attr_default() { /** * @ticket 53675 * @ticket 58235 + * @ticket 58892 */ public function test_wp_omit_loading_attr_threshold_filter() { // Using a smaller image here. @@ -3738,15 +3738,21 @@ public function test_wp_omit_loading_attr_threshold_filter() { // Due to the filter, now the first five elements should not be lazy-loaded, i.e. return `false`. for ( $i = 0; $i < 5; $i++ ) { - $this->assertEmpty( + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'Expected second image to not be lazy-loaded.' ); } // For following elements, lazy-load them again. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ) ); } @@ -3761,6 +3767,7 @@ public function test_wp_omit_loading_attr_threshold_filter() { * @covers ::wp_get_loading_optimization_attributes */ public function test_wp_filter_content_tags_with_loading_optimization_attrs() { + add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); $img1 = get_image_tag( self::$large_id, '', '', '', 'large' ); $iframe1 = ''; $img2 = get_image_tag( self::$large_id, '', '', '', 'medium' ); @@ -3777,7 +3784,6 @@ public function test_wp_filter_content_tags_with_loading_optimization_attrs() { // Following the threshold of 2, the first two content media elements should not be lazy-loaded. $content_unfiltered = $img1 . $iframe1 . $img2 . $img3 . $iframe2; $content_expected = $prio_img1 . $iframe1 . $lazy_img2 . $lazy_img3 . $lazy_iframe2; - $content_expected = wp_img_tag_add_decoding_attr( $content_expected, 'the_content' ); $query = $this->get_new_wp_query_for_published_post(); $this->set_main_query( $query ); @@ -3789,6 +3795,7 @@ public function test_wp_filter_content_tags_with_loading_optimization_attrs() { $content_filtered = wp_filter_content_tags( $content_unfiltered, 'the_content' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); } + remove_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); // After filtering, the first image should not be lazy-loaded while the other ones should be. $this->assertSame( $content_expected, $content_filtered ); @@ -4128,6 +4135,7 @@ public function test_wp_filter_content_tags_does_not_lazy_load_images_in_header( /** * @ticket 58089 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_filter_content_tags * @covers ::wp_get_loading_optimization_attributes @@ -4143,6 +4151,7 @@ public function test_wp_filter_content_tags_does_not_apply_loading_optimization_ array( 'loading' => false, 'fetchpriority' => false, + 'decoding' => false, ) ); @@ -4181,7 +4190,7 @@ static function () use ( &$image_within_content ) { $this->assertSame( $expected_image, $image_within_content, 'Image with wp_get_attachment_image context within post content should not receive loading optimization attributes' ); // Ensure that parsed content has the image with fetchpriority as it is the first large image. - $expected_content = wpautop( str_replace( 'assertSame( $expected_content, $content, 'Post content with programmatically injected image is missing loading optimization attributes' ); } @@ -4261,6 +4270,7 @@ public function data_special_contexts_for_the_content_wp_get_loading_attr_defaul * @ticket 53675 * @ticket 56930 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @@ -4272,18 +4282,27 @@ public function test_wp_get_loading_optimization_attributes( $context ) { $attr = $this->get_width_height_for_high_priority(); // Return 'lazy' by default. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'test' ) ); - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'wp_get_attachment_image' ) ); // Return 'lazy' if not in the loop or the main query. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); @@ -4293,8 +4312,11 @@ public function test_wp_get_loading_optimization_attributes( $context ) { the_post(); // Return 'lazy' if in the loop but not in the main query. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); @@ -4302,29 +4324,44 @@ public function test_wp_get_loading_optimization_attributes( $context ) { $this->set_main_query( $query ); // First three element are not lazy loaded. However, first image is loaded with fetchpriority high. - $this->assertSame( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), "Expected first image to not be lazy-loaded. First large image get's high fetchpriority." ); - $this->assertEmpty( + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'Expected second image to not be lazy-loaded.' ); - $this->assertEmpty( + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'Expected third image to not be lazy-loaded.' ); // Return 'lazy' if in the loop and in the main query for any subsequent elements. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); // Yes, for all subsequent elements. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); } @@ -4344,8 +4381,11 @@ public function test_wp_get_loading_optimization_attributes( $context ) { public function test_wp_get_loading_optimization_attributes_with_arbitrary_contexts_in_main_loop( $context ) { $attr = $this->get_width_height_for_high_priority(); - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'The "loading" attribute should be "lazy" when not in the loop or the main query.' ); @@ -4358,8 +4398,11 @@ public function test_wp_get_loading_optimization_attributes_with_arbitrary_conte while ( have_posts() ) { the_post(); - $this->assertSame( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'The "fetchpriority" attribute should be "high" while in the loop and the main query.' ); @@ -4388,8 +4431,11 @@ public function test_wp_get_loading_optimization_attributes_with_arbitrary_conte // Set as main query. $this->set_main_query( $query ); - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'The "loading" attribute should be "lazy" before the main query loop.' ); @@ -4397,8 +4443,11 @@ public function test_wp_get_loading_optimization_attributes_with_arbitrary_conte while ( have_posts() ) { the_post(); - $this->assertSame( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'The "fetchpriority" attribute should be "high" while in the loop and the main query.' ); @@ -4467,8 +4516,11 @@ public function test_wp_get_loading_optimization_attributes_header_contexts( $co add_filter( 'wp_loading_optimization_force_header_contexts', '__return_empty_array' ); - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'Images in the header context should get lazy-loaded after the wp_loading_optimization_force_header_contexts filter.' ); @@ -4502,8 +4554,11 @@ function ( $context ) { } ); - $this->assertSame( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'something_completely_arbitrary' ) ); } @@ -4513,6 +4568,7 @@ function ( $context ) { * * @ticket 58211 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @@ -4530,8 +4586,11 @@ public function test_wp_get_loading_optimization_attributes_before_loop_if_not_m $attr = $this->get_width_height_for_high_priority(); // Lazy if not main query. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); } @@ -4541,6 +4600,7 @@ public function test_wp_get_loading_optimization_attributes_before_loop_if_not_m * * @ticket 58211 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @@ -4557,8 +4617,11 @@ public function test_wp_get_loading_optimization_attributes_before_loop_in_main_ $attr = $this->get_width_height_for_high_priority(); // Lazy if header not called. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); } @@ -4585,8 +4648,11 @@ public function test_wp_get_loading_optimization_attributes_before_loop_if_main_ $attr = $this->get_width_height_for_high_priority(); // First image is loaded with high fetchpriority. - $this->assertSame( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ), 'Expected first image to not be lazy-loaded. First large image is loaded with high fetchpriority.' ); @@ -4597,6 +4663,7 @@ public function test_wp_get_loading_optimization_attributes_before_loop_if_main_ * * @ticket 58211 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @@ -4617,8 +4684,11 @@ public function test_wp_get_loading_optimization_attributes_after_loop( $context } $attr = $this->get_width_height_for_high_priority(); - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); } @@ -4628,6 +4698,7 @@ public function test_wp_get_loading_optimization_attributes_after_loop( $context * * @ticket 58211 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @@ -4648,8 +4719,11 @@ public function test_wp_get_loading_optimization_attributes_no_loop( $context ) $attr = $this->get_width_height_for_high_priority(); // Load lazy if the there is no loop and footer was called. - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); } @@ -4659,6 +4733,7 @@ public function test_wp_get_loading_optimization_attributes_no_loop( $context ) * * @ticket 58089 * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @@ -4668,8 +4743,11 @@ public function test_wp_get_loading_optimization_attributes_no_loop( $context ) */ public function test_wp_get_loading_optimization_attributes_should_return_lazy_for_special_contexts_outside_of_the_content( $context ) { $attr = $this->get_width_height_for_high_priority(); - $this->assertSame( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, $context ) ); } @@ -4703,6 +4781,54 @@ function ( $content ) use ( &$result, $context ) { $this->assertSame( array(), $result ); } + /** + * Tests to cover the decoding attribute within wp_get_loading_optimization_attributes(). + * + * @ticket 58892 + * + * @covers ::wp_get_loading_optimization_attributes + */ + public function test_wp_get_loading_optimization_attributes_decoding_attribute() { + + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + ), + wp_get_loading_optimization_attributes( 'img', array(), 'the_content' ), + 'Expected decoding attribute to be async.' + ); + + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'auto', + ), + wp_get_loading_optimization_attributes( 'img', array( 'decoding' => 'auto' ), 'the_content' ), + 'Expected decoding attribute to be auto.' + ); + + $result = null; + add_filter( + 'the_content', + static function ( $content ) use ( &$result ) { + $result = wp_get_loading_optimization_attributes( 'img', array(), 'something_completely_arbitrary' ); + return $content; + } + ); + apply_filters( 'the_content', '' ); + + $this->assertSameSetsWithIndex( + array(), + $result, + 'Expected decoding attribute to be empty for img on arbitrary context, while running the_content.' + ); + + $this->assertSameSetsWithIndex( + array(), + wp_get_loading_optimization_attributes( 'iframe', array(), 'the_content' ), + 'Expected decoding attribute to be empty for iframe.' + ); + } + /** * @ticket 44427 * @ticket 50367 @@ -4809,6 +4935,7 @@ public function test_the_excerpt_does_not_affect_omit_lazy_loading_logic() { 'post-thumbnail', array( 'loading' => false, + 'decoding' => 'async', 'fetchpriority' => 'high', ) ); @@ -5015,20 +5142,27 @@ public function data_wp_get_loading_optimization_attributes_min_required_attrs() 'width' => 100, 'height' => 100, ), - array( 'loading' => 'lazy' ), - 'Expected default `loading="lazy"`.', + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), + 'Expected default `decoding="async"` and `loading="lazy"`.', ), 'img_without_height' => array( 'img', array( 'width' => 100 ), - array(), - 'Expected blank array as height is required.', + array( + 'decoding' => 'async', + ), + 'Only `decoding` is set as height is required for `loading` attribute.', ), 'img_without_width' => array( 'img', array( 'height' => 100 ), - array(), - 'Expected blank array as width is required.', + array( + 'decoding' => 'async', + ), + 'Only `decoding` is set as width is required for `loading` attribute.', ), ); } @@ -5061,8 +5195,11 @@ public function data_wp_get_loading_optimization_attributes_check_allowed_tags() return array( 'img' => array( 'img', - array( 'loading' => 'lazy' ), - 'Expected `loading="lazy"` for the img.', + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), + 'Expected `decoding="async"` and `loading="lazy"` and `decoding="async"` for the img.', ), 'iframe' => array( 'iframe', @@ -5105,8 +5242,11 @@ public function test_wp_get_loading_optimization_attributes_header_block_templat $attr = $this->get_width_height_for_high_priority(); // Skip logic if context is `template`. - $this->assertSame( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'template_part_' . WP_TEMPLATE_PART_AREA_HEADER ), 'Images in the header block template part should not be lazy-loaded and first large image is set high fetchpriority.' ); @@ -5114,6 +5254,7 @@ public function test_wp_get_loading_optimization_attributes_header_block_templat /** * @ticket 58235 + * @ticket 58892 * * @covers ::wp_get_loading_optimization_attributes * @expectedIncorrectUsage wp_get_loading_optimization_attributes @@ -5125,6 +5266,7 @@ public function test_wp_get_loading_optimization_attributes_incorrect_loading_at $this->assertEqualSetsWithIndex( array( + 'decoding' => 'async', 'loading' => 'lazy', 'fetchpriority' => 'high', ), @@ -5143,8 +5285,9 @@ public function test_wp_get_loading_optimization_attributes_if_loading_attr_pres $attr['loading'] = 'eager'; // Check fetchpriority high logic if loading attribute is present. - $this->assertSame( + $this->assertSameSetsWithIndex( array( + 'decoding' => 'async', 'fetchpriority' => 'high', ), wp_get_loading_optimization_attributes( 'img', $attr, 'test' ), @@ -5166,7 +5309,9 @@ public function test_wp_get_loading_optimization_attributes_low_res_image() { // fetchpriority not set as image is of lower resolution. $this->assertSame( - array(), + array( + 'decoding' => 'async', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'test' ), 'loading optimization attr array should be empty.' ); @@ -5182,7 +5327,7 @@ public function test_wp_get_loading_optimization_attributes_in_shortcodes( $setu $setup(); // The first image processed in a shortcode should have fetchpriority set to high. - $this->assertSame( + $this->assertSameSetsWithIndex( $expected, wp_get_loading_optimization_attributes( 'img', $attr, 'do_shortcode' ), $message @@ -5200,6 +5345,7 @@ public function data_wp_get_loading_optimization_attributes_in_shortcodes() { $this->set_main_query( $wp_query ); }, 'expected' => array( + 'decoding' => 'async', 'fetchpriority' => 'high', ), 'message' => 'Fetch priority not applied to during shortcode rendering.', @@ -5217,9 +5363,10 @@ public function data_wp_get_loading_optimization_attributes_in_shortcodes() { wp_increase_content_media_count( 3 ); }, 'expected' => array( - 'loading' => 'lazy', + 'decoding' => 'async', + 'loading' => 'lazy', ), - 'message' => 'Lazy-loading not applied to during shortcode rendering.', + 'message' => 'Lazy-loading or decoding not applied to during shortcode rendering.', ), 'shortcode_image_outside_of_the_loop_are_loaded_lazy' => array( 'setup' => function () { @@ -5227,9 +5374,10 @@ public function data_wp_get_loading_optimization_attributes_in_shortcodes() { return; }, 'expected' => array( - 'loading' => 'lazy', + 'decoding' => 'async', + 'loading' => 'lazy', ), - 'message' => 'Lazy-loading not applied to shortcodes outside the loop.', + 'message' => 'Lazy-loading or decoding not applied to shortcodes outside the loop.', ), ); } @@ -5326,7 +5474,10 @@ public function data_wp_maybe_add_fetchpriority_high_attr() { 'high', ), 'image with loading=lazy' => array( - array( 'loading' => 'lazy' ), + array( + 'loading' => 'lazy', + 'decoding' => 'async', + ), 'img', $this->get_width_height_for_high_priority(), false, @@ -5463,7 +5614,7 @@ static function ( $loading_attrs ) { $attr = $this->get_width_height_for_high_priority(); - $this->assertSame( + $this->assertSameSetsWithIndex( array( 'fetchpriority' => 'high' ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'The filter did not return early fetchpriority attribute' @@ -5472,8 +5623,11 @@ static function ( $loading_attrs ) { // Clean up the filter. add_filter( 'pre_wp_get_loading_optimization_attributes', '__return_false' ); - $this->assertSameSets( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'The filter did not return the default attributes.' ); @@ -5481,7 +5635,7 @@ static function ( $loading_attrs ) { // Return no loading attributes. add_filter( 'pre_wp_get_loading_optimization_attributes', '__return_empty_array' ); - $this->assertSameSets( + $this->assertSameSetsWithIndex( array(), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'The filter did not clean up all attributes.' @@ -5503,7 +5657,7 @@ static function ( $loading_attrs ) { 1 ); - $this->assertSameSets( + $this->assertSameSetsWithIndex( array( 'custom_attr' => 'custom_value' ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'The filter did not return custom attributes.' @@ -5518,8 +5672,11 @@ static function ( $loading_attrs ) { public function test_wp_get_loading_optimization_attributes_filter() { $attr = $this->get_width_height_for_high_priority(); - $this->assertSameSets( - array( 'loading' => 'lazy' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'loading' => 'lazy', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'Before the filter it will not return the loading attribute.' ); @@ -5536,8 +5693,11 @@ static function ( $loading_attrs ) { 1 ); - $this->assertSameSets( - array( 'fetchpriority' => 'high' ), + $this->assertSameSetsWithIndex( + array( + 'decoding' => 'async', + 'fetchpriority' => 'high', + ), wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ), 'After the filter it will not return the fetchpriority attribute.' ); diff --git a/tests/phpunit/tests/media/wpImageTagAddDecodingAttr.php b/tests/phpunit/tests/media/wpImageTagAddDecodingAttr.php index 7011d816da1ab..15e1859084a03 100644 --- a/tests/phpunit/tests/media/wpImageTagAddDecodingAttr.php +++ b/tests/phpunit/tests/media/wpImageTagAddDecodingAttr.php @@ -19,6 +19,8 @@ class Tests_Media_Wp_Img_Tag_Add_Decoding_Attr extends WP_UnitTestCase { * @param string $context Additional context to pass to the filters. * @param string $decoding The value for the 'decoding' attribute. 'no value' for default. * @param string $expected The expected `img` tag. + * + * @expectedDeprecated wp_img_tag_add_decoding_attr */ public function test_should_add_decoding_attr( $image, $context, $decoding, $expected ) { // Falsey values are allowed in the filter, cannot use `null` or `false` here. @@ -80,6 +82,8 @@ public function data_should_add_decoding_attr() { * @param string $context Additional context to pass to the filters. * @param mixed $decoding The value for the 'decoding' attribute. 'no value' for default. * @param string $expected The expected `img` tag. + * + * @expectedDeprecated wp_img_tag_add_decoding_attr */ public function test_should_not_add_decoding_attr( $image, $context, $decoding, $expected ) { // Falsey values are allowed in the filter, cannot use `null` or `false` here.