Make WordPress Core

Changeset 55318


Ignore:
Timestamp:
02/13/2023 06:32:40 PM (20 months ago)
Author:
flixos90
Message:

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.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-template.php

    r54817 r55318  
    242242    $content = convert_smilies( $content );
    243243    $content = shortcode_unautop( $content );
    244     $content = wp_filter_content_tags( $content );
     244    $content = wp_filter_content_tags( $content, 'template' );
    245245    $content = do_shortcode( $content );
    246246    $content = str_replace( ']]>', ']]>', $content );
  • trunk/src/wp-includes/media.php

    r55251 r55318  
    54455445 */
    54465446function wp_get_loading_attr_default( $context ) {
    5447     // Only elements with 'the_content' or 'the_post_thumbnail' context have special handling.
    5448     if ( 'the_content' !== $context && 'the_post_thumbnail' !== $context ) {
     5447    // Skip lazy-loading for the overall block template, as it is handled more granularly.
     5448    if ( 'template' === $context ) {
     5449        return false;
     5450    }
     5451
     5452    // Do not lazy-load images in the header block template part, as they are likely above the fold.
     5453    $header_area = WP_TEMPLATE_PART_AREA_HEADER;
     5454    if ( "template_part_{$header_area}" === $context ) {
     5455        return false;
     5456    }
     5457
     5458    /*
     5459     * The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
     5460     * as they are likely above the fold.
     5461     */
     5462    if ( 'the_content' === $context || 'the_post_thumbnail' === $context ) {
     5463        // Only elements within the main query loop have special handling.
     5464        if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
     5465            return 'lazy';
     5466        }
     5467
     5468        // Increase the counter since this is a main query content element.
     5469        $content_media_count = wp_increase_content_media_count();
     5470
     5471        // If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
     5472        if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
     5473            return false;
     5474        }
     5475
     5476        // For elements after the threshold, lazy-load them as usual.
    54495477        return 'lazy';
    54505478    }
    54515479
    5452     // Only elements within the main query loop have special handling.
    5453     if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
    5454         return 'lazy';
    5455     }
    5456 
    5457     // Increase the counter since this is a main query content element.
    5458     $content_media_count = wp_increase_content_media_count();
    5459 
    5460     // If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
    5461     if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
    5462         return false;
    5463     }
    5464 
    5465     // For elements after the threshold, lazy-load them as usual.
     5480    // Lazy-load by default for any unknown context.
    54665481    return 'lazy';
    54675482}
  • trunk/tests/phpunit/tests/media.php

    r55278 r55318  
    35483548
    35493549    /**
     3550     * Tests that wp_get_loading_attr_default() returns the expected loading attribute value.
     3551     *
    35503552     * @ticket 53675
     3553     * @ticket 56930
     3554     *
     3555     * @covers ::wp_get_loading_attr_default
     3556     *
    35513557     * @dataProvider data_wp_get_loading_attr_default
    35523558     *
     
    35893595            $this->assertSame( 'lazy', wp_get_loading_attr_default( $context ) );
    35903596        }
     3597
     3598        // Exceptions: In the following contexts, images shouldn't be lazy-loaded by default.
     3599        $this->assertFalse( wp_get_loading_attr_default( 'template' ), 'Images run through the overall block template filter should not be lazy-loaded.' );
     3600        $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.' );
    35913601    }
    35923602
     
    37013711    }
    37023712
     3713    /**
     3714     * Tests that wp_filter_content_tags() does not add loading="lazy" to the first
     3715     * image in the loop when using a block theme.
     3716     *
     3717     * @ticket 56930
     3718     *
     3719     * @covers ::wp_filter_content_tags
     3720     * @covers ::wp_get_loading_attr_default
     3721     */
     3722    public function test_wp_filter_content_tags_does_not_lazy_load_first_image_in_block_theme() {
     3723        global $_wp_current_template_content, $wp_query, $wp_the_query, $post;
     3724
     3725        // Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test.
     3726        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3727        add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' );
     3728
     3729        $img1      = get_image_tag( self::$large_id, '', '', '', 'large' );
     3730        $img2      = get_image_tag( self::$large_id, '', '', '', 'medium' );
     3731        $lazy_img2 = wp_img_tag_add_loading_attr( $img2, 'the_content' );
     3732
     3733        // Only the second image should be lazy-loaded.
     3734        $post_content     = $img1 . $img2;
     3735        $expected_content = wpautop( $img1 . $lazy_img2 );
     3736
     3737        // Update the post to test with so that it has the above post content.
     3738        wp_update_post(
     3739            array(
     3740                'ID'                    => self::$post_ids['publish'],
     3741                'post_content'          => $post_content,
     3742                'post_content_filtered' => $post_content,
     3743            )
     3744        );
     3745
     3746        $wp_query     = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
     3747        $wp_the_query = $wp_query;
     3748        $post         = get_post( self::$post_ids['publish'] );
     3749        $this->reset_content_media_count();
     3750        $this->reset_omit_loading_attr_filter();
     3751
     3752        $_wp_current_template_content = '<!-- wp:post-content /-->';
     3753
     3754        $html = get_the_block_template_html();
     3755        $this->assertSame( '<div class="wp-site-blocks"><div class="entry-content wp-block-post-content is-layout-flow">' . $expected_content . '</div></div>', $html );
     3756    }
     3757
     3758    /**
     3759     * Tests that wp_filter_content_tags() does not add loading="lazy"
     3760     * to the featured image when using a block theme.
     3761     *
     3762     * @ticket 56930
     3763     *
     3764     * @covers ::wp_filter_content_tags
     3765     * @covers ::wp_get_loading_attr_default
     3766     */
     3767    public function test_wp_filter_content_tags_does_not_lazy_load_first_featured_image_in_block_theme() {
     3768        global $_wp_current_template_content, $wp_query, $wp_the_query, $post;
     3769
     3770        // Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test.
     3771        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3772        add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' );
     3773        add_filter(
     3774            'wp_get_attachment_image_attributes',
     3775            function( $attr ) {
     3776                unset( $attr['srcset'], $attr['sizes'], $attr['decoding'] );
     3777                return $attr;
     3778            }
     3779        );
     3780
     3781        $content_img      = get_image_tag( self::$large_id, '', '', '', 'large' );
     3782        $lazy_content_img = wp_img_tag_add_loading_attr( $content_img, 'the_content' );
     3783
     3784        // The featured image should not be lazy-loaded as it is the first image.
     3785        $featured_image_id = self::$large_id;
     3786        update_post_meta( self::$post_ids['publish'], '_thumbnail_id', $featured_image_id );
     3787        $expected_featured_image = '<figure class="wp-block-post-featured-image">' . get_the_post_thumbnail( self::$post_ids['publish'], 'post-thumbnail', array( 'loading' => false ) ) . '</figure>';
     3788
     3789        // The post content image should be lazy-loaded since the featured image appears above.
     3790        $post_content     = $content_img;
     3791        $expected_content = wpautop( $lazy_content_img );
     3792
     3793        // Update the post to test with so that it has the above post content.
     3794        wp_update_post(
     3795            array(
     3796                'ID'                    => self::$post_ids['publish'],
     3797                'post_content'          => $post_content,
     3798                'post_content_filtered' => $post_content,
     3799            )
     3800        );
     3801
     3802        $wp_query     = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
     3803        $wp_the_query = $wp_query;
     3804        $post         = get_post( self::$post_ids['publish'] );
     3805        $this->reset_content_media_count();
     3806        $this->reset_omit_loading_attr_filter();
     3807
     3808        $_wp_current_template_content = '<!-- wp:post-featured-image /--> <!-- wp:post-content /-->';
     3809
     3810        $html = get_the_block_template_html();
     3811        $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 );
     3812    }
     3813
     3814    /**
     3815     * Tests that wp_filter_content_tags() does not add loading="lazy" to images
     3816     * in a "Header" template part.
     3817     *
     3818     * @ticket 56930
     3819     *
     3820     * @covers ::wp_filter_content_tags
     3821     * @covers ::wp_get_loading_attr_default
     3822     */
     3823    public function test_wp_filter_content_tags_does_not_lazy_load_images_in_header() {
     3824        global $_wp_current_template_content;
     3825
     3826        // Do not add srcset, sizes, or decoding attributes as they are irrelevant for this test.
     3827        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     3828        add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' );
     3829
     3830        // Use a single image for each header and footer template parts.
     3831        $header_img = get_image_tag( self::$large_id, '', '', '', 'large' );
     3832        $footer_img = get_image_tag( self::$large_id, '', '', '', 'medium' );
     3833
     3834        // Create header and footer template parts.
     3835        $header_post_id = self::factory()->post->create(
     3836            array(
     3837                'post_type'    => 'wp_template_part',
     3838                'post_status'  => 'publish',
     3839                'post_name'    => 'header',
     3840                'post_content' => $header_img,
     3841            )
     3842        );
     3843        wp_set_post_terms( $header_post_id, WP_TEMPLATE_PART_AREA_HEADER, 'wp_template_part_area' );
     3844        wp_set_post_terms( $header_post_id, get_stylesheet(), 'wp_theme' );
     3845        $footer_post_id = self::factory()->post->create(
     3846            array(
     3847                'post_type'    => 'wp_template_part',
     3848                'post_status'  => 'publish',
     3849                'post_name'    => 'footer',
     3850                'post_content' => $footer_img,
     3851            )
     3852        );
     3853        wp_set_post_terms( $footer_post_id, WP_TEMPLATE_PART_AREA_FOOTER, 'wp_template_part_area' );
     3854        wp_set_post_terms( $footer_post_id, get_stylesheet(), 'wp_theme' );
     3855
     3856        $_wp_current_template_content = '<!-- wp:template-part {"slug":"header","theme":"' . get_stylesheet() . '","tagName":"header"} /--><!-- wp:template-part {"slug":"footer","theme":"' . get_stylesheet() . '","tagName":"footer"} /-->';
     3857
     3858        // Header image should not be lazy-loaded, footer image should be lazy-loaded.
     3859        $expected_template_content  = '<header class="wp-block-template-part">' . $header_img . '</header>';
     3860        $expected_template_content .= '<footer class="wp-block-template-part">' . wp_img_tag_add_loading_attr( $footer_img, 'force-lazy' ) . '</footer>';
     3861
     3862        $html = get_the_block_template_html();
     3863        $this->assertSame( '<div class="wp-site-blocks">' . $expected_template_content . '</div>', $html );
     3864    }
     3865
    37033866    private function reset_content_media_count() {
    37043867        // Get current value without increasing.
Note: See TracChangeset for help on using the changeset viewer.