Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure that first content image is not lazy-loaded in block themes either #3560

Closed
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/wp-includes/block-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
39 changes: 27 additions & 12 deletions src/wp-includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
// Skip lazy-loading for the overall block template, as it is handled more granularly.
if ( 'template' === $context ) {
return false;
}

// Only elements within the main query loop have special handling.
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
return 'lazy';
// 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;
}

// Increase the counter since this is a main query content element.
$content_media_count = wp_increase_content_media_count();
/*
* 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';
}

// 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;
// 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';
}

// For elements after the threshold, lazy-load them as usual.
// Lazy-load by default for any unknown context.
return 'lazy';
}

Expand Down
163 changes: 163 additions & 0 deletions tests/phpunit/tests/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -3547,7 +3547,13 @@ public function data_attachment_permalinks_based_on_parent_status() {
}

/**
* 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
Expand Down Expand Up @@ -3588,6 +3594,10 @@ public function test_wp_get_loading_attr_default( $context ) {
// 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() {
Expand Down Expand Up @@ -3700,6 +3710,159 @@ function() {
$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
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
*
* @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="is-layout-flow entry-content wp-block-post-content">' . $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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query: Should this have "first_featured_image", or just "featured_image"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"first_featured_image" is correct, as other featured images further down in the page should still be lazy-loaded.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to testing:

  • Doesn't have a featured image, and the first image is not lazy loaded.
  • Has a featured image, which is not lazy loaded.

Should we also cover this?

  • Has a featured image, and the first image afterwards is lazy loaded.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@costdev

Has a featured image, and the first image afterwards is lazy loaded.

Hmm, this is already being tested here I think. The final assertion tests that the featured image is not lazy-loaded, while the first image afterwards (an in-content image) is lazy-loaded.

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="is-layout-flow entry-content wp-block-post-content">' . $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
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
*
* @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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add another test to ensure that images in a footer template part are always lazy loaded? I'm thinking in case something is later changed that accidentally excludes footer images from lazy loading.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@costdev This is already being tested by this method, the final assertion checks the entire template which includes both a header and footer template part, each with an image. Only the one in the header template part is expected to not be lazy-loaded, while the one in the footer should be.

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 );
Expand Down