Media: Enhance logic to determine LCP image in block themes and avoid lazy-loading it.

[52065] originally introduced the logic to guess the LCP image based on certain heuristics and to not lazy-load that image. However, with the introduction of block themes, that logic was not functioning correctly, resulting in all featured images to be lazy-loaded, regardless of whether it was the LCP image or not.

Together with an update to the `core/post-featured-image` block included in [55079], this changeset fixes the logic to correctly handle featured images in block themes as well.

Additionally, in combination with an update to the `core/template-part` block from [55246], this changeset includes an enhancement which uses the benefits of block template parts to avoid lazy-loading images in the `header` block template part, making the lazy-loading heuristics even more accurate for sites using a block theme.

Props flixos90, adamsilverstein, mamaduka, antonvlasenko, shahidul95, reduanmasud, costdev, mukesh27, ironprogrammer, manfcarlo, robinwpdeveloper, spacedmonkey.
Fixes #56930.


git-svn-id: https://develop.svn.wordpress.org/trunk@55318 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Felix Arntz 2023-02-13 18:32:40 +00:00
parent ea57bf06ea
commit 87c575a9fc
3 changed files with 195 additions and 17 deletions

View File

@ -241,7 +241,7 @@ function get_the_block_template_html() {
$content = wptexturize( $content );
$content = convert_smilies( $content );
$content = shortcode_unautop( $content );
$content = wp_filter_content_tags( $content );
$content = wp_filter_content_tags( $content, 'template' );
$content = do_shortcode( $content );
$content = str_replace( ']]>', ']]>', $content );

View File

@ -5444,25 +5444,40 @@ function wp_get_webp_info( $filename ) {
* that the `loading` attribute should be skipped.
*/
function wp_get_loading_attr_default( $context ) {
// Only elements with 'the_content' or 'the_post_thumbnail' context have special handling.
if ( 'the_content' !== $context && 'the_post_thumbnail' !== $context ) {
return 'lazy';
}
// Only elements within the main query loop have special handling.
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
return 'lazy';
}
// Increase the counter since this is a main query content element.
$content_media_count = wp_increase_content_media_count();
// If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
// Skip lazy-loading for the overall block template, as it is handled more granularly.
if ( 'template' === $context ) {
return false;
}
// For elements after the threshold, lazy-load them as usual.
// Do not lazy-load images in the header block template part, as they are likely above the fold.
$header_area = WP_TEMPLATE_PART_AREA_HEADER;
if ( "template_part_{$header_area}" === $context ) {
return false;
}
/*
* The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
* as they are likely above the fold.
*/
if ( 'the_content' === $context || 'the_post_thumbnail' === $context ) {
// Only elements within the main query loop have special handling.
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
return 'lazy';
}
// Increase the counter since this is a main query content element.
$content_media_count = wp_increase_content_media_count();
// If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
return false;
}
// For elements after the threshold, lazy-load them as usual.
return 'lazy';
}
// Lazy-load by default for any unknown context.
return 'lazy';
}

View File

@ -3547,7 +3547,13 @@ EOF;
}
/**
* Tests that wp_get_loading_attr_default() returns the expected loading attribute value.
*
* @ticket 53675
* @ticket 56930
*
* @covers ::wp_get_loading_attr_default
*
* @dataProvider data_wp_get_loading_attr_default
*
* @param string $context
@ -3588,6 +3594,10 @@ EOF;
// Yes, for all subsequent elements.
$this->assertSame( 'lazy', wp_get_loading_attr_default( $context ) );
}
// Exceptions: In the following contexts, images shouldn't be lazy-loaded by default.
$this->assertFalse( wp_get_loading_attr_default( 'template' ), 'Images run through the overall block template filter should not be lazy-loaded.' );
$this->assertFalse( wp_get_loading_attr_default( 'template_part_' . WP_TEMPLATE_PART_AREA_HEADER ), 'Images in the footer block template part should not be lazy-loaded.' );
}
public function data_wp_get_loading_attr_default() {
@ -3700,6 +3710,159 @@ EOF;
$this->assertSame( 3, $omit_threshold );
}
/**
* Tests that wp_filter_content_tags() does not add loading="lazy" to the first
* image in the loop when using a block theme.
*
* @ticket 56930
*
* @covers ::wp_filter_content_tags
* @covers ::wp_get_loading_attr_default
*/
public function test_wp_filter_content_tags_does_not_lazy_load_first_image_in_block_theme() {
global $_wp_current_template_content, $wp_query, $wp_the_query, $post;
// Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test.
add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' );
$img1 = get_image_tag( self::$large_id, '', '', '', 'large' );
$img2 = get_image_tag( self::$large_id, '', '', '', 'medium' );
$lazy_img2 = wp_img_tag_add_loading_attr( $img2, 'the_content' );
// Only the second image should be lazy-loaded.
$post_content = $img1 . $img2;
$expected_content = wpautop( $img1 . $lazy_img2 );
// Update the post to test with so that it has the above post content.
wp_update_post(
array(
'ID' => self::$post_ids['publish'],
'post_content' => $post_content,
'post_content_filtered' => $post_content,
)
);
$wp_query = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
$wp_the_query = $wp_query;
$post = get_post( self::$post_ids['publish'] );
$this->reset_content_media_count();
$this->reset_omit_loading_attr_filter();
$_wp_current_template_content = '<!-- wp:post-content /-->';
$html = get_the_block_template_html();
$this->assertSame( '<div class="wp-site-blocks"><div class="entry-content wp-block-post-content is-layout-flow">' . $expected_content . '</div></div>', $html );
}
/**
* Tests that wp_filter_content_tags() does not add loading="lazy"
* to the featured image when using a block theme.
*
* @ticket 56930
*
* @covers ::wp_filter_content_tags
* @covers ::wp_get_loading_attr_default
*/
public function test_wp_filter_content_tags_does_not_lazy_load_first_featured_image_in_block_theme() {
global $_wp_current_template_content, $wp_query, $wp_the_query, $post;
// Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test.
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_get_attachment_image_attributes',
function( $attr ) {
unset( $attr['srcset'], $attr['sizes'], $attr['decoding'] );
return $attr;
}
);
$content_img = get_image_tag( self::$large_id, '', '', '', 'large' );
$lazy_content_img = wp_img_tag_add_loading_attr( $content_img, 'the_content' );
// The featured image should not be lazy-loaded as it is the first image.
$featured_image_id = self::$large_id;
update_post_meta( self::$post_ids['publish'], '_thumbnail_id', $featured_image_id );
$expected_featured_image = '<figure class="wp-block-post-featured-image">' . get_the_post_thumbnail( self::$post_ids['publish'], 'post-thumbnail', array( 'loading' => false ) ) . '</figure>';
// The post content image should be lazy-loaded since the featured image appears above.
$post_content = $content_img;
$expected_content = wpautop( $lazy_content_img );
// Update the post to test with so that it has the above post content.
wp_update_post(
array(
'ID' => self::$post_ids['publish'],
'post_content' => $post_content,
'post_content_filtered' => $post_content,
)
);
$wp_query = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
$wp_the_query = $wp_query;
$post = get_post( self::$post_ids['publish'] );
$this->reset_content_media_count();
$this->reset_omit_loading_attr_filter();
$_wp_current_template_content = '<!-- wp:post-featured-image /--> <!-- wp:post-content /-->';
$html = get_the_block_template_html();
$this->assertSame( '<div class="wp-site-blocks">' . $expected_featured_image . ' <div class="entry-content wp-block-post-content is-layout-flow">' . $expected_content . '</div></div>', $html );
}
/**
* Tests that wp_filter_content_tags() does not add loading="lazy" to images
* in a "Header" template part.
*
* @ticket 56930
*
* @covers ::wp_filter_content_tags
* @covers ::wp_get_loading_attr_default
*/
public function test_wp_filter_content_tags_does_not_lazy_load_images_in_header() {
global $_wp_current_template_content;
// Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test.
add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' );
// Use a single image for each header and footer template parts.
$header_img = get_image_tag( self::$large_id, '', '', '', 'large' );
$footer_img = get_image_tag( self::$large_id, '', '', '', 'medium' );
// Create header and footer template parts.
$header_post_id = self::factory()->post->create(
array(
'post_type' => 'wp_template_part',
'post_status' => 'publish',
'post_name' => 'header',
'post_content' => $header_img,
)
);
wp_set_post_terms( $header_post_id, WP_TEMPLATE_PART_AREA_HEADER, 'wp_template_part_area' );
wp_set_post_terms( $header_post_id, get_stylesheet(), 'wp_theme' );
$footer_post_id = self::factory()->post->create(
array(
'post_type' => 'wp_template_part',
'post_status' => 'publish',
'post_name' => 'footer',
'post_content' => $footer_img,
)
);
wp_set_post_terms( $footer_post_id, WP_TEMPLATE_PART_AREA_FOOTER, 'wp_template_part_area' );
wp_set_post_terms( $footer_post_id, get_stylesheet(), 'wp_theme' );
$_wp_current_template_content = '<!-- wp:template-part {"slug":"header","theme":"' . get_stylesheet() . '","tagName":"header"} /--><!-- wp:template-part {"slug":"footer","theme":"' . get_stylesheet() . '","tagName":"footer"} /-->';
// Header image should not be lazy-loaded, footer image should be lazy-loaded.
$expected_template_content = '<header class="wp-block-template-part">' . $header_img . '</header>';
$expected_template_content .= '<footer class="wp-block-template-part">' . wp_img_tag_add_loading_attr( $footer_img, 'force-lazy' ) . '</footer>';
$html = get_the_block_template_html();
$this->assertSame( '<div class="wp-site-blocks">' . $expected_template_content . '</div>', $html );
}
private function reset_content_media_count() {
// Get current value without increasing.
$content_media_count = wp_increase_content_media_count( 0 );