mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-06-28 14:20:15 +00:00
Media: Add decoding="async" to image attributes.
Dynamically add `decoding="async"` to image tags on the front end of a site to instruct browsers to download them in parallel. Modifies `wp_get_attachment_image()`, `get_avatar()` to include the attribute by default. Modifies `wp_filter_content_tags()` to add the attribute during the front-end render of the site. Introduces `wp_img_tag_add_decoding_attr()` to take an image tag and modify it to include the attribute. Introduces the filter `wp_img_tag_add_decoding_attr` used to define the default value for the attribute. Props adamsilverstein, ayeshrajans, costdev, flixos90, hellofromtonya, isaumya, michaelbourne, mihai2u, mitogh, sergiomdgomes, spacedmonkey, westonruter, peterwilsoncc. Fixes #53232. git-svn-id: https://develop.svn.wordpress.org/trunk@53480 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -1011,16 +1011,18 @@ function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon
|
||||
* @param string|array $attr {
|
||||
* Optional. Attributes for the image markup.
|
||||
*
|
||||
* @type string $src Image attachment URL.
|
||||
* @type string $class CSS class name or space-separated list of classes.
|
||||
* Default `attachment-$size_class size-$size_class`,
|
||||
* where `$size_class` is the image size being requested.
|
||||
* @type string $alt Image description for the alt attribute.
|
||||
* @type string $srcset The 'srcset' attribute value.
|
||||
* @type string $sizes The 'sizes' attribute value.
|
||||
* @type string|false $loading The 'loading' attribute value. Passing a value of false
|
||||
* will result in the attribute being omitted for the image.
|
||||
* Defaults to 'lazy', depending on wp_lazy_loading_enabled().
|
||||
* @type string $src Image attachment URL.
|
||||
* @type string $class CSS class name or space-separated list of classes.
|
||||
* Default `attachment-$size_class size-$size_class`,
|
||||
* where `$size_class` is the image size being requested.
|
||||
* @type string $alt Image description for the alt attribute.
|
||||
* @type string $srcset The 'srcset' attribute value.
|
||||
* @type string $sizes The 'sizes' attribute value.
|
||||
* @type string|false $loading The 'loading' attribute value. Passing a value of false
|
||||
* will result in the attribute being omitted for the image.
|
||||
* Defaults to 'lazy', depending on wp_lazy_loading_enabled().
|
||||
* @type string $decoding The 'decoding' attribute value. Possible values are
|
||||
* 'async' (default), 'sync', or 'auto'.
|
||||
* }
|
||||
* @return string HTML img element or empty string on failure.
|
||||
*/
|
||||
@@ -1040,9 +1042,10 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f
|
||||
}
|
||||
|
||||
$default_attr = array(
|
||||
'src' => $src,
|
||||
'class' => "attachment-$size_class size-$size_class",
|
||||
'alt' => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ),
|
||||
'src' => $src,
|
||||
'class' => "attachment-$size_class size-$size_class",
|
||||
'alt' => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ),
|
||||
'decoding' => 'async',
|
||||
);
|
||||
|
||||
// Add `loading` attribute.
|
||||
@@ -1843,6 +1846,11 @@ function wp_filter_content_tags( $content, $context = null ) {
|
||||
$filtered_image = wp_img_tag_add_loading_attr( $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.
|
||||
*
|
||||
@@ -1934,6 +1942,46 @@ function wp_img_tag_add_loading_attr( $image, $context ) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add `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
|
||||
*
|
||||
* @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 ) {
|
||||
/**
|
||||
* 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' (default), 'sync', or 'auto'.
|
||||
* @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.
|
||||
*/
|
||||
$value = apply_filters( 'wp_img_tag_add_decoding_attr', 'async', $image, $context );
|
||||
if ( in_array( $value, array( 'async', 'sync', 'auto' ), true ) ) {
|
||||
$image = str_replace( '<img ', '<img decoding="' . esc_attr( $value ) . '" ', $image );
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds `width` and `height` attributes to an `img` HTML tag.
|
||||
*
|
||||
|
||||
@@ -2764,6 +2764,7 @@ if ( ! function_exists( 'get_avatar' ) ) :
|
||||
'force_display' => false,
|
||||
'loading' => null,
|
||||
'extra_attr' => '',
|
||||
'decoding' => 'async',
|
||||
);
|
||||
|
||||
if ( wp_lazy_loading_enabled( 'img', 'get_avatar' ) ) {
|
||||
@@ -2851,6 +2852,13 @@ if ( ! function_exists( 'get_avatar' ) ) :
|
||||
$extra_attr .= "loading='{$loading}'";
|
||||
}
|
||||
|
||||
if ( in_array( $args['decoding'], array( 'async', 'sync', 'auto' ) ) && ! preg_match( '/\bdecoding\s*=/', $extra_attr ) ) {
|
||||
if ( ! empty( $extra_attr ) ) {
|
||||
$extra_attr .= ' ';
|
||||
}
|
||||
$extra_attr .= "decoding='{$args['decoding']}'";
|
||||
}
|
||||
|
||||
$avatar = sprintf(
|
||||
"<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>",
|
||||
esc_attr( $args['alt'] ),
|
||||
|
||||
@@ -169,7 +169,7 @@ class Tests_Avatar extends WP_UnitTestCase {
|
||||
|
||||
public function test_get_avatar() {
|
||||
$img = get_avatar( 1 );
|
||||
$this->assertSame( preg_match( "|^<img alt='[^']*' src='[^']*' srcset='[^']*' class='[^']*' height='[^']*' width='[^']*' loading='lazy'/>$|", $img ), 1 );
|
||||
$this->assertSame( preg_match( "|^<img alt='[^']*' src='[^']*' srcset='[^']*' class='[^']*' height='[^']*' width='[^']*' loading='lazy' decoding='async'/>$|", $img ), 1 );
|
||||
}
|
||||
|
||||
public function test_get_avatar_size() {
|
||||
|
||||
@@ -1474,7 +1474,7 @@ EOF;
|
||||
public function test_wp_get_attachment_image_defaults() {
|
||||
$image = image_downsize( self::$large_id, 'thumbnail' );
|
||||
$expected = sprintf(
|
||||
'<img width="%1$d" height="%2$d" src="%3$s" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" />',
|
||||
'<img width="%1$d" height="%2$d" src="%3$s" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" />',
|
||||
$image[1],
|
||||
$image[2],
|
||||
$image[0]
|
||||
@@ -1512,7 +1512,7 @@ EOF;
|
||||
|
||||
$image = image_downsize( self::$large_id, 'thumbnail' );
|
||||
$expected = sprintf(
|
||||
'<img width="%1$d" height="%2$d" src="%3$s" class="attachment-thumbnail size-thumbnail" alt="Some very clever alt text" loading="lazy" />',
|
||||
'<img width="%1$d" height="%2$d" src="%3$s" class="attachment-thumbnail size-thumbnail" alt="Some very clever alt text" decoding="async" loading="lazy" />',
|
||||
$image[1],
|
||||
$image[2],
|
||||
$image[0]
|
||||
@@ -2247,6 +2247,7 @@ EOF;
|
||||
$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' );
|
||||
@@ -2272,6 +2273,7 @@ EOF;
|
||||
public function test_wp_filter_content_tags_srcset_sizes_wrong() {
|
||||
$img = get_image_tag( self::$large_id, '', '', '', 'medium' );
|
||||
$img = wp_img_tag_add_loading_attr( $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 );
|
||||
@@ -2286,6 +2288,7 @@ EOF;
|
||||
// Generate HTML and add a dummy srcset attribute.
|
||||
$img = get_image_tag( self::$large_id, '', '', '', 'medium' );
|
||||
$img = wp_img_tag_add_loading_attr( $img, 'test' );
|
||||
$img = wp_img_tag_add_decoding_attr( $img, 'the_content' );
|
||||
$img = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . 'srcset="image2x.jpg 2x" />', $img );
|
||||
|
||||
// The content filter should return the image unchanged.
|
||||
@@ -2333,7 +2336,7 @@ EOF;
|
||||
*/
|
||||
public function test_wp_filter_content_tags_filter_with_identical_image_tags_custom_attributes() {
|
||||
$img = get_image_tag( self::$large_id, '', '', '', 'large' );
|
||||
$img = str_replace( '<img ', '<img srcset="custom" sizes="custom" loading="custom" ', $img );
|
||||
$img = str_replace( '<img ', '<img srcset="custom" sizes="custom" loading="custom" decoding="custom"', $img );
|
||||
$content = "$img\n$img";
|
||||
|
||||
add_filter(
|
||||
@@ -2358,6 +2361,7 @@ EOF;
|
||||
add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
|
||||
add_filter( 'wp_img_tag_add_width_and_height_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' );
|
||||
|
||||
add_filter(
|
||||
'wp_content_img_tag',
|
||||
@@ -2460,6 +2464,7 @@ EOF;
|
||||
$respimg_https,
|
||||
$respimg_relative
|
||||
);
|
||||
$expected = wp_img_tag_add_decoding_attr( $expected, 'the_content' );
|
||||
|
||||
$actual = wp_filter_content_tags( $unfiltered );
|
||||
|
||||
@@ -2602,7 +2607,7 @@ EOF;
|
||||
|
||||
$expected = '<img width="999" height="999" ' .
|
||||
'src="' . $uploads_url . 'test-image-testsize-999x999.jpg" ' .
|
||||
'class="attachment-testsize size-testsize" alt="" loading="lazy" ' .
|
||||
'class="attachment-testsize size-testsize" alt="" decoding="async" loading="lazy" ' .
|
||||
'srcset="' . $uploads_url . 'test-image-testsize-999x999.jpg 999w, ' . $uploads_url . $basename . '-150x150.jpg 150w" ' .
|
||||
'sizes="(max-width: 999px) 100vw, 999px" />';
|
||||
|
||||
@@ -2909,7 +2914,7 @@ EOF;
|
||||
%4$s';
|
||||
|
||||
$content_unfiltered = sprintf( $content, $img, $img_no_width_height, $img_no_width, $img_no_height );
|
||||
$content_filtered = sprintf( $content, $img, $respimg_no_width_height, $img_no_width, $img_no_height );
|
||||
$content_filtered = wp_img_tag_add_decoding_attr( sprintf( $content, $img, $respimg_no_width_height, $img_no_width, $img_no_height ), 'the_content' );
|
||||
|
||||
// Do not add loading, srcset, and sizes.
|
||||
add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
|
||||
@@ -2967,7 +2972,7 @@ EOF;
|
||||
%8$s';
|
||||
|
||||
$content_unfiltered = sprintf( $content, $img, $img_xhtml, $img_html5, $img_eager, $img_no_width_height, $iframe, $iframe_eager, $iframe_no_width_height );
|
||||
$content_filtered = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $img_eager, $img_no_width_height, $lazy_iframe, $iframe_eager, $iframe_no_width_height );
|
||||
$content_filtered = wp_img_tag_add_decoding_attr( sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $img_eager, $img_no_width_height, $lazy_iframe, $iframe_eager, $iframe_no_width_height ), 'the_content' );
|
||||
|
||||
// Do not add width, height, srcset, and sizes.
|
||||
add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
|
||||
@@ -2996,7 +3001,7 @@ EOF;
|
||||
%2$s';
|
||||
|
||||
$content_unfiltered = sprintf( $content, $img, $iframe );
|
||||
$content_filtered = sprintf( $content, $lazy_img, $lazy_iframe );
|
||||
$content_filtered = sprintf( $content, wp_img_tag_add_decoding_attr( $lazy_img, 'the_content' ), $lazy_iframe, 'the_content' );
|
||||
|
||||
// Do not add srcset and sizes while testing.
|
||||
add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
|
||||
@@ -3014,7 +3019,7 @@ EOF;
|
||||
* @ticket 50756
|
||||
*/
|
||||
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( get_image_tag( self::$large_id, '', '', '', 'medium' ), 'the_content' );
|
||||
$iframe = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
|
||||
|
||||
$content = '
|
||||
@@ -3478,7 +3483,7 @@ EOF;
|
||||
|
||||
// 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 = $img1 . $iframe1 . $lazy_img2 . $lazy_img3 . $lazy_iframe2;
|
||||
$content_expected = wp_img_tag_add_decoding_attr( $img1 . $iframe1 . $lazy_img2 . $lazy_img3 . $lazy_iframe2, 'the_content' );
|
||||
|
||||
$wp_query = new WP_Query( array( 'post__in' => array( self::$post_ids['publish'] ) ) );
|
||||
$wp_the_query = $wp_query;
|
||||
|
||||
@@ -32,7 +32,7 @@ class Tests_Media_GetAdjacentImageLink extends WP_Test_Adjacent_Image_Link_TestC
|
||||
'when has previous link' => array(
|
||||
'current_attachment_index' => 3,
|
||||
'expected_attachment_index' => 2,
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="http://example.org/wp-content/uploads/image2.jpg" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" /></a>',
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="' . WP_CONTENT_URL . '/uploads/image2.jpg" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" /></a>',
|
||||
),
|
||||
'with text when has previous link' => array(
|
||||
'current_attachment_index' => 3,
|
||||
@@ -43,7 +43,7 @@ class Tests_Media_GetAdjacentImageLink extends WP_Test_Adjacent_Image_Link_TestC
|
||||
'when has next link' => array(
|
||||
'current_attachment_index' => 4,
|
||||
'expected_attachment_index' => 5,
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="http://example.org/wp-content/uploads/image5.jpg" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" /></a>',
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="' . WP_CONTENT_URL . '/uploads/image5.jpg" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" /></a>',
|
||||
'args' => array( 'prev' => false ),
|
||||
),
|
||||
'with text when has next link' => array(
|
||||
|
||||
@@ -31,7 +31,7 @@ class Tests_Media_GetNextImageLink extends WP_Test_Adjacent_Image_Link_TestCase
|
||||
'when has next link' => array(
|
||||
'current_attachment_index' => 4,
|
||||
'expected_attachment_index' => 5,
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="http://example.org/wp-content/uploads/image5.jpg" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" /></a>',
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="' . WP_CONTENT_URL . '/uploads/image5.jpg" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" /></a>',
|
||||
),
|
||||
'with text when has next link' => array(
|
||||
'current_attachment_index' => 4,
|
||||
|
||||
@@ -31,7 +31,7 @@ class Tests_Media_GetPreviousImageLink extends WP_Test_Adjacent_Image_Link_TestC
|
||||
'when has previous link' => array(
|
||||
'current_attachment_index' => 3,
|
||||
'expected_attachment_index' => 2,
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="http://example.org/wp-content/uploads/image2.jpg" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" /></a>',
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="' . WP_CONTENT_URL . '/uploads/image2.jpg" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" /></a>',
|
||||
),
|
||||
'with text when has previous link' => array(
|
||||
'current_attachment_index' => 3,
|
||||
|
||||
@@ -30,7 +30,7 @@ class Tests_Media_NextImageLink extends WP_Test_Adjacent_Image_Link_TestCase {
|
||||
'when has next link' => array(
|
||||
'current_attachment_index' => 4,
|
||||
'expected_attachment_index' => 5,
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="http://example.org/wp-content/uploads/image5.jpg" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" /></a>',
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="' . WP_CONTENT_URL . '/uploads/image5.jpg" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" /></a>',
|
||||
),
|
||||
'with text when has next link' => array(
|
||||
'current_attachment_index' => 4,
|
||||
|
||||
@@ -30,7 +30,7 @@ class Tests_Media_PreviousImageLink extends WP_Test_Adjacent_Image_Link_TestCase
|
||||
'when has previous link' => array(
|
||||
'current_attachment_index' => 3,
|
||||
'expected_attachment_index' => 2,
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="http://example.org/wp-content/uploads/image2.jpg" class="attachment-thumbnail size-thumbnail" alt="" loading="lazy" /></a>',
|
||||
'expected' => '<a href=\'http://example.org/?attachment_id=%%ID%%\'><img width="1" height="1" src="' . WP_CONTENT_URL . '/uploads/image2.jpg" class="attachment-thumbnail size-thumbnail" alt="" decoding="async" loading="lazy" /></a>',
|
||||
),
|
||||
'with text when has previous link' => array(
|
||||
'current_attachment_index' => 3,
|
||||
|
||||
Reference in New Issue
Block a user