Make WordPress Core

Changeset 56037


Ignore:
Timestamp:
06/26/2023 04:15:12 PM (11 months ago)
Author:
flixos90
Message:

Media: Automatically add fetchpriority="high" to hero image to improve load time performance.

This changeset adds support for the fetchpriority attribute, which is typically added to a single image in each HTML response with a value of "high". This enhances load time performance (also Largest Contentful Paint, or LCP) by telling the browser to prioritize this image for downloading even before the layout of the page has been computed. In lab tests, this has shown to improve LCP performance by ~10% on average.

Specifically, fetchpriority="high" is added to the first image that satisfies all of the following conditions:

  • The image is not lazy-loaded, i.e. does not have loading="lazy".
  • The image does not already have a (conflicting) fetchpriority attribute.
  • The size of of the image (i.e. width * height) is greater than 50,000 squarepixels.

While these heuristics are based on several field analyses, there will always be room for optimization. Sites can customize the squarepixel threshold using a new filter wp_min_priority_img_pixels which should return an integer for the value.

Since the logic for adding fetchpriority="high" is heavily intertwined with the logic for adding loading="lazy", yet the features should work decoupled from each other, the majority of code changes in this changeset is refactoring of the existing lazy-loading logic to be reusable. For this purpose, a new function wp_get_loading_optimization_attributes() has been introduced which returns an associative array of performance-relevant attributes for a given HTML element. This function replaces wp_get_loading_attr_default(), which has been deprecated. As another result of that change, a new function wp_img_tag_add_loading_optimization_attrs() replaces the more specific wp_img_tag_add_loading_attr(), which has been deprecated as well.

See https://make.wordpress.org/core/2023/05/02/proposal-for-enhancing-lcp-image-performance-with-fetchpriority/ for the original proposal and additional context.

Props thekt12, joemcgill, spacedmonkey, mukesh27, costdev, 10upsimon.
Fixes #58235.

Location:
trunk
Files:
4 edited

Legend:

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

    r56020 r56037  
    46594659    wp_lazyload_comment_meta( $comment_ids );
    46604660}
     4661
     4662/**
     4663 * Gets the default value to use for a `loading` attribute on an element.
     4664 *
     4665 * This function should only be called for a tag and context if lazy-loading is generally enabled.
     4666 *
     4667 * The function usually returns 'lazy', but uses certain heuristics to guess whether the current element is likely to
     4668 * appear above the fold, in which case it returns a boolean `false`, which will lead to the `loading` attribute being
     4669 * omitted on the element. The purpose of this refinement is to avoid lazy-loading elements that are within the initial
     4670 * viewport, which can have a negative performance impact.
     4671 *
     4672 * Under the hood, the function uses {@see wp_increase_content_media_count()} every time it is called for an element
     4673 * within the main content. If the element is the very first content element, the `loading` attribute will be omitted.
     4674 * This default threshold of 3 content elements to omit the `loading` attribute for can be customized using the
     4675 * {@see 'wp_omit_loading_attr_threshold'} filter.
     4676 *
     4677 * @since 5.9.0
     4678 * @deprecated 6.3.0 Use wp_get_loading_optimization_attributes() instead.
     4679 * @see wp_get_loading_optimization_attributes()
     4680 *
     4681 * @global WP_Query $wp_query WordPress Query object.
     4682 *
     4683 * @param string $context Context for the element for which the `loading` attribute value is requested.
     4684 * @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
     4685 *                     that the `loading` attribute should be skipped.
     4686 */
     4687function wp_get_loading_attr_default( $context ) {
     4688    _deprecated_function( __FUNCTION__, '6.3.0', 'wp_get_loading_optimization_attributes' );
     4689    global $wp_query;
     4690
     4691    // Skip lazy-loading for the overall block template, as it is handled more granularly.
     4692    if ( 'template' === $context ) {
     4693        return false;
     4694    }
     4695
     4696    /*
     4697     * Do not lazy-load images in the header block template part, as they are likely above the fold.
     4698     * For classic themes, this is handled in the condition below using the 'get_header' action.
     4699     */
     4700    $header_area = WP_TEMPLATE_PART_AREA_HEADER;
     4701    if ( "template_part_{$header_area}" === $context ) {
     4702        return false;
     4703    }
     4704
     4705    // Special handling for programmatically created image tags.
     4706    if ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) {
     4707        /*
     4708         * Skip programmatically created images within post content as they need to be handled together with the other
     4709         * images within the post content.
     4710         * Without this clause, they would already be counted below which skews the number and can result in the first
     4711         * post content image being lazy-loaded only because there are images elsewhere in the post content.
     4712         */
     4713        if ( doing_filter( 'the_content' ) ) {
     4714            return false;
     4715        }
     4716
     4717        // Conditionally skip lazy-loading on images before the loop.
     4718        if (
     4719            // Only apply for main query but before the loop.
     4720            $wp_query->before_loop && $wp_query->is_main_query()
     4721            /*
     4722             * Any image before the loop, but after the header has started should not be lazy-loaded,
     4723             * except when the footer has already started which can happen when the current template
     4724             * does not include any loop.
     4725             */
     4726            && did_action( 'get_header' ) && ! did_action( 'get_footer' )
     4727        ) {
     4728            return false;
     4729        }
     4730    }
     4731
     4732    /*
     4733     * The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
     4734     * as they are likely above the fold.
     4735     */
     4736    if ( 'the_content' === $context || 'the_post_thumbnail' === $context ) {
     4737        // Only elements within the main query loop have special handling.
     4738        if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
     4739            return 'lazy';
     4740        }
     4741
     4742        // Increase the counter since this is a main query content element.
     4743        $content_media_count = wp_increase_content_media_count();
     4744
     4745        // If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
     4746        if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
     4747            return false;
     4748        }
     4749
     4750        // For elements after the threshold, lazy-load them as usual.
     4751        return 'lazy';
     4752    }
     4753
     4754    // Lazy-load by default for any unknown context.
     4755    return 'lazy';
     4756}
     4757
     4758/**
     4759 * Adds `loading` attribute to an `img` HTML tag.
     4760 *
     4761 * @since 5.5.0
     4762 * @deprecated 6.3.0 Use wp_img_tag_add_loading_optimization_attrs() instead.
     4763 * @see wp_img_tag_add_loading_optimization_attrs()
     4764 *
     4765 * @param string $image   The HTML `img` tag where the attribute should be added.
     4766 * @param string $context Additional context to pass to the filters.
     4767 * @return string Converted `img` tag with `loading` attribute added.
     4768 */
     4769function wp_img_tag_add_loading_attr( $image, $context ) {
     4770    _deprecated_function( __FUNCTION__, '6.3.0', 'wp_img_tag_add_loading_optimization_attrs' );
     4771    /*
     4772     * Get loading attribute value to use. This must occur before the conditional check below so that even images that
     4773     * are ineligible for being lazy-loaded are considered.
     4774     */
     4775    $value = wp_get_loading_attr_default( $context );
     4776
     4777    // Images should have source and dimension attributes for the `loading` attribute to be added.
     4778    if ( ! str_contains( $image, ' src="' ) || ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) {
     4779        return $image;
     4780    }
     4781
     4782    /**
     4783     * Filters the `loading` attribute value to add to an image. Default `lazy`.
     4784     *
     4785     * Returning `false` or an empty string will not add the attribute.
     4786     * Returning `true` will add the default value.
     4787     *
     4788     * @since 5.5.0
     4789     *
     4790     * @param string|bool $value   The `loading` attribute value. Returning a falsey value will result in
     4791     *                             the attribute being omitted for the image.
     4792     * @param string      $image   The HTML `img` tag to be filtered.
     4793     * @param string      $context Additional context about how the function was called or where the img tag is.
     4794     */
     4795    $value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context );
     4796
     4797    if ( $value ) {
     4798        if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
     4799            $value = 'lazy';
     4800        }
     4801
     4802        return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image );
     4803    }
     4804
     4805    return $image;
     4806}
  • trunk/src/wp-includes/media.php

    r56031 r56037  
    10591059         */
    10601060        $context = apply_filters( 'wp_get_attachment_image_context', 'wp_get_attachment_image' );
    1061 
    1062         // Add `loading` attribute.
    1063         if ( wp_lazy_loading_enabled( 'img', $context ) ) {
    1064             $default_attr['loading'] = wp_get_loading_attr_default( $context );
    1065         }
    1066 
    1067         $attr = wp_parse_args( $attr, $default_attr );
     1061        $attr    = wp_parse_args( $attr, $default_attr );
     1062
     1063        $loading_attr              = $attr;
     1064        $loading_attr['width']     = $width;
     1065        $loading_attr['height']    = $height;
     1066        $loading_optimization_attr = wp_get_loading_optimization_attributes(
     1067            'img',
     1068            $loading_attr,
     1069            $context
     1070        );
     1071
     1072        // Add loading optimization attributes if not available.
     1073        $attr = array_merge( $attr, $loading_optimization_attr );
    10681074
    10691075        // Omit the `decoding` attribute if the value is invalid according to the spec.
     
    10741080        // If the default value of `lazy` for the `loading` attribute is overridden
    10751081        // to omit the attribute for this image, ensure it is not included.
    1076         if ( array_key_exists( 'loading', $attr ) && ! $attr['loading'] ) {
     1082        if ( isset( $attr['loading'] ) && ! $attr['loading'] ) {
    10771083            unset( $attr['loading'] );
     1084        }
     1085
     1086        // If the `fetchpriority` attribute is overridden and set to false or an empty string.
     1087        if ( isset( $attr['fetchpriority'] ) && ! $attr['fetchpriority'] ) {
     1088            unset( $attr['fetchpriority'] );
    10781089        }
    10791090
     
    17811792 * @see wp_img_tag_add_width_and_height_attr()
    17821793 * @see wp_img_tag_add_srcset_and_sizes_attr()
    1783  * @see wp_img_tag_add_loading_attr()
     1794 * @see wp_img_tag_add_loading_optimization_attrs()
    17841795 * @see wp_iframe_tag_add_loading_attr()
    17851796 *
     
    17941805    }
    17951806
    1796     $add_img_loading_attr    = wp_lazy_loading_enabled( 'img', $context );
    17971807    $add_iframe_loading_attr = wp_lazy_loading_enabled( 'iframe', $context );
    17981808
     
    18581868            }
    18591869
    1860             // Add 'loading' attribute if applicable.
    1861             if ( $add_img_loading_attr && ! str_contains( $filtered_image, ' loading=' ) ) {
    1862                 $filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context );
    1863             }
     1870            // Add loading optimization attributes if applicable.
     1871            $filtered_image = wp_img_tag_add_loading_optimization_attrs( $filtered_image, $context );
    18641872
    18651873            // Add 'decoding=async' attribute unless a 'decoding' attribute is already present.
     
    19151923
    19161924/**
    1917  * Adds `loading` attribute to an `img` HTML tag.
    1918  *
    1919  * @since 5.5.0
     1925 * Adds optimization attributes to an `img` HTML tag.
     1926 *
     1927 * @since 6.3.0
    19201928 *
    19211929 * @param string $image   The HTML `img` tag where the attribute should be added.
    19221930 * @param string $context Additional context to pass to the filters.
    1923  * @return string Converted `img` tag with `loading` attribute added.
    1924  */
    1925 function wp_img_tag_add_loading_attr( $image, $context ) {
    1926     // Get loading attribute value to use. This must occur before the conditional check below so that even images that
    1927     // are ineligible for being lazy-loaded are considered.
    1928     $value = wp_get_loading_attr_default( $context );
    1929 
    1930     // Images should have source and dimension attributes for the `loading` attribute to be added.
     1931 * @return string Converted `img` tag with optimization attributes added.
     1932 */
     1933function wp_img_tag_add_loading_optimization_attrs( $image, $context ) {
     1934    $width             = preg_match( '/ width=["\']([0-9]+)["\']/', $image, $match_width ) ? (int) $match_width[1] : null;
     1935    $height            = preg_match( '/ height=["\']([0-9]+)["\']/', $image, $match_height ) ? (int) $match_height[1] : null;
     1936    $loading_val       = preg_match( '/ loading=["\']([A-Za-z]+)["\']/', $image, $match_loading ) ? $match_loading[1] : null;
     1937    $fetchpriority_val = preg_match( '/ fetchpriority=["\']([A-Za-z]+)["\']/', $image, $match_fetchpriority ) ? $match_fetchpriority[1] : null;
     1938
     1939    /*
     1940     * Get loading optimization attributes to use.
     1941     * This must occur before the conditional check below so that even images
     1942     * that are ineligible for being lazy-loaded are considered.
     1943     */
     1944    $optimization_attrs = wp_get_loading_optimization_attributes(
     1945        'img',
     1946        array(
     1947            'width'         => $width,
     1948            'height'        => $height,
     1949            'loading'       => $loading_val,
     1950            'fetchpriority' => $fetchpriority_val,
     1951        ),
     1952        $context
     1953    );
     1954
     1955    // Images should have source and dimension attributes for the loading optimization attributes to be added.
    19311956    if ( ! str_contains( $image, ' src="' ) || ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) {
    19321957        return $image;
    19331958    }
    19341959
    1935     /**
    1936      * Filters the `loading` attribute value to add to an image. Default `lazy`.
    1937      *
    1938      * Returning `false` or an empty string will not add the attribute.
    1939      * Returning `true` will add the default value.
    1940      *
    1941      * @since 5.5.0
    1942      *
    1943      * @param string|bool $value   The `loading` attribute value. Returning a falsey value will result in
    1944      *                             the attribute being omitted for the image.
    1945      * @param string      $image   The HTML `img` tag to be filtered.
    1946      * @param string      $context Additional context about how the function was called or where the img tag is.
    1947      */
    1948     $value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context );
    1949 
    1950     if ( $value ) {
    1951         if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
    1952             $value = 'lazy';
    1953         }
    1954 
    1955         return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image );
     1960    // Retained for backward compatibility.
     1961    $loading_attrs_enabled = wp_lazy_loading_enabled( 'img', $context );
     1962
     1963    if ( empty( $loading_val ) && $loading_attrs_enabled ) {
     1964        /**
     1965         * Filters the `loading` attribute value to add to an image. Default `lazy`.
     1966         * This filter is added in for backward compatibility.
     1967         *
     1968         * Returning `false` or an empty string will not add the attribute.
     1969         * Returning `true` will add the default value.
     1970         * `true` and `false` usage supported for backward compatibility.
     1971         *
     1972         * @since 5.5.0
     1973         *
     1974         * @param string|bool $loading Current value for `loading` attribute for the image.
     1975         * @param string      $image   The HTML `img` tag to be filtered.
     1976         * @param string      $context Additional context about how the function was called or where the img tag is.
     1977         */
     1978        $filtered_loading_attr = apply_filters(
     1979            'wp_img_tag_add_loading_attr',
     1980            isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false,
     1981            $image,
     1982            $context
     1983        );
     1984
     1985        // Validate the values after filtering.
     1986        if ( isset( $optimization_attrs['loading'] ) && ! $filtered_loading_attr ) {
     1987            // Unset `loading` attributes if `$filtered_loading_attr` is set to `false`.
     1988            unset( $optimization_attrs['loading'] );
     1989        } elseif ( in_array( $filtered_loading_attr, array( 'lazy', 'eager' ), true ) ) {
     1990            /*
     1991             * If the filter changed the loading attribute to "lazy" when a fetchpriority attribute
     1992             * with value "high" is already present, trigger a warning since those two attribute
     1993             * values should be mutually exclusive.
     1994             *
     1995             * The same warning is present in `wp_get_loading_optimization_attributes()`, and here it
     1996             * is only intended for the specific scenario where the above filtered caused the problem.
     1997             */
     1998            if ( isset( $optimization_attrs['fetchpriority'] ) && 'high' === $optimization_attrs['fetchpriority'] &&
     1999                ( isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false ) !== $filtered_loading_attr &&
     2000                'lazy' === $filtered_loading_attr
     2001            ) {
     2002                _doing_it_wrong(
     2003                    __FUNCTION__,
     2004                    __( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
     2005                    '6.3.0'
     2006                );
     2007            }
     2008
     2009            // The filtered value will still be respected.
     2010            $optimization_attrs['loading'] = $filtered_loading_attr;
     2011        }
     2012
     2013        if ( ! empty( $optimization_attrs['loading'] ) ) {
     2014            $image = str_replace( '<img', '<img loading="' . esc_attr( $optimization_attrs['loading'] ) . '"', $image );
     2015        }
     2016    }
     2017
     2018    if ( empty( $fetchpriority_val ) && ! empty( $optimization_attrs['fetchpriority'] ) ) {
     2019        $image = str_replace( '<img', '<img fetchpriority="' . esc_attr( $optimization_attrs['fetchpriority'] ) . '"', $image );
    19562020    }
    19572021
     
    21042168    // Get loading attribute value to use. This must occur before the conditional check below so that even iframes that
    21052169    // are ineligible for being lazy-loaded are considered.
    2106     $value = wp_get_loading_attr_default( $context );
     2170    $optimization_attrs = wp_get_loading_optimization_attributes(
     2171        'iframe',
     2172        array(
     2173            /*
     2174             * The concrete values for width and height are not important here for now
     2175             * since fetchpriority is not yet supported for iframes.
     2176             * TODO: Use WP_HTML_Tag_Processor to extract actual values once support is
     2177             * added.
     2178             */
     2179            'width'   => str_contains( $iframe, ' width="' ) ? 100 : null,
     2180            'height'  => str_contains( $iframe, ' height="' ) ? 100 : null,
     2181            // This function is never called when a 'loading' attribute is already present.
     2182            'loading' => null,
     2183        ),
     2184        $context
     2185    );
    21072186
    21082187    // Iframes should have source and dimension attributes for the `loading` attribute to be added.
     
    21102189        return $iframe;
    21112190    }
     2191
     2192    $value = isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false;
    21122193
    21132194    /**
     
    54705551
    54715552/**
    5472  * Gets the default value to use for a `loading` attribute on an element.
    5473  *
    5474  * This function should only be called for a tag and context if lazy-loading is generally enabled.
    5475  *
    5476  * The function usually returns 'lazy', but uses certain heuristics to guess whether the current element is likely to
    5477  * appear above the fold, in which case it returns a boolean `false`, which will lead to the `loading` attribute being
    5478  * omitted on the element. The purpose of this refinement is to avoid lazy-loading elements that are within the initial
    5479  * viewport, which can have a negative performance impact.
    5480  *
    5481  * Under the hood, the function uses {@see wp_increase_content_media_count()} every time it is called for an element
    5482  * within the main content. If the element is the very first content element, the `loading` attribute will be omitted.
    5483  * This default threshold of 3 content elements to omit the `loading` attribute for can be customized using the
    5484  * {@see 'wp_omit_loading_attr_threshold'} filter.
    5485  *
    5486  * @since 5.9.0
     5553 * Gets loading optimization attributes.
     5554 *
     5555 * This function returns an array of attributes that should be merged into the given attributes array to optimize
     5556 * loading performance. Potential attributes returned by this function are:
     5557 * - `loading` attribute with a value of "lazy"
     5558 * - `fetchpriority` attribute with a value of "high"
     5559 *
     5560 * If any of these attributes are already present in the given attributes, they will not be modified. Note that no
     5561 * element should have both `loading="lazy"` and `fetchpriority="high"`, so the function will trigger a warning in case
     5562 * both attributes are present with those values.
     5563 *
     5564 * @since 6.3.0
    54875565 *
    54885566 * @global WP_Query $wp_query WordPress Query object.
    54895567 *
    5490  * @param string $context Context for the element for which the `loading` attribute value is requested.
    5491  * @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
    5492  *                     that the `loading` attribute should be skipped.
    5493  */
    5494 function wp_get_loading_attr_default( $context ) {
     5568 * @param string $tag_name The tag name.
     5569 * @param array  $attr     Array of the attributes for the tag.
     5570 * @param string $context  Context for the element for which the loading optimization attribute is requested.
     5571 * @return array Loading optimization attributes.
     5572 */
     5573function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
    54955574    global $wp_query;
    54965575
    5497     // Skip lazy-loading for the overall block template, as it is handled more granularly.
     5576    /*
     5577     * Closure for postprocessing logic.
     5578     * It is here to avoid duplicate logic in many places below, without having
     5579     * to introduce a very specific private global function.
     5580     */
     5581    $postprocess = static function( $loading_attributes, $with_fetchpriority = false ) use ( $tag_name, $attr, $context ) {
     5582        // Potentially add `fetchpriority="high"`.
     5583        if ( $with_fetchpriority ) {
     5584            $loading_attributes = wp_maybe_add_fetchpriority_high_attr( $loading_attributes, $tag_name, $attr );
     5585        }
     5586        // Potentially strip `loading="lazy"` if the feature is disabled.
     5587        if ( isset( $loading_attributes['loading'] ) && ! wp_lazy_loading_enabled( $tag_name, $context ) ) {
     5588            unset( $loading_attributes['loading'] );
     5589        }
     5590        return $loading_attributes;
     5591    };
     5592
     5593    $loading_attrs = array();
     5594
     5595    /*
     5596     * Skip lazy-loading for the overall block template, as it is handled more granularly.
     5597     * The skip is also applicable for `fetchpriority`.
     5598     */
    54985599    if ( 'template' === $context ) {
    5499         return false;
    5500     }
    5501 
    5502     // Do not lazy-load images in the header block template part, as they are likely above the fold.
    5503     // For classic themes, this is handled in the condition below using the 'get_header' action.
     5600        return $loading_attrs;
     5601    }
     5602
     5603    // For now this function only supports images and iframes.
     5604    if ( 'img' !== $tag_name && 'iframe' !== $tag_name ) {
     5605        return $loading_attrs;
     5606    }
     5607
     5608    // For any resources, width and height must be provided, to avoid layout shifts.
     5609    if ( ! isset( $attr['width'], $attr['height'] ) ) {
     5610        return $loading_attrs;
     5611    }
     5612
     5613    if ( isset( $attr['loading'] ) ) {
     5614        /*
     5615         * While any `loading` value could be set in `$loading_attrs`, for
     5616         * consistency we only do it for `loading="lazy"` since that is the
     5617         * only possible value that WordPress core would apply on its own.
     5618         */
     5619        if ( 'lazy' === $attr['loading'] ) {
     5620            $loading_attrs['loading'] = 'lazy';
     5621            if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
     5622                _doing_it_wrong(
     5623                    __FUNCTION__,
     5624                    __( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
     5625                    '6.3.0'
     5626                );
     5627            }
     5628        }
     5629
     5630        return $postprocess( $loading_attrs, true );
     5631    }
     5632
     5633    // An image with `fetchpriority="high"` cannot be assigned `loading="lazy"` at the same time.
     5634    if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
     5635        return $postprocess( $loading_attrs, true );
     5636    }
     5637
     5638    /*
     5639     * Do not lazy-load images in the header block template part, as they are likely above the fold.
     5640     * For classic themes, this is handled in the condition below using the 'get_header' action.
     5641     */
    55045642    $header_area = WP_TEMPLATE_PART_AREA_HEADER;
    55055643    if ( "template_part_{$header_area}" === $context ) {
    5506         return false;
     5644        return $postprocess( $loading_attrs, true );
    55075645    }
    55085646
    55095647    // Special handling for programmatically created image tags.
    5510     if ( ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) ) {
     5648    if ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) {
    55115649        /*
    55125650         * Skip programmatically created images within post content as they need to be handled together with the other
     
    55165654         */
    55175655        if ( doing_filter( 'the_content' ) ) {
    5518             return false;
     5656            return $postprocess( $loading_attrs, true );
    55195657        }
    55205658
     
    55305668            && did_action( 'get_header' ) && ! did_action( 'get_footer' )
    55315669        ) {
    5532             return false;
     5670            return $postprocess( $loading_attrs, true );
    55335671        }
    55345672    }
     
    55415679        // Only elements within the main query loop have special handling.
    55425680        if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
    5543             return 'lazy';
     5681            $loading_attrs['loading'] = 'lazy';
     5682            return $postprocess( $loading_attrs, false );
    55445683        }
    55455684
     
    55475686        $content_media_count = wp_increase_content_media_count();
    55485687
    5549         // If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
     5688        // If the count so far is below the threshold, `loading` attribute is omitted.
    55505689        if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
    5551             return false;
    5552         }
    5553 
    5554         // For elements after the threshold, lazy-load them as usual.
    5555         return 'lazy';
     5690            // The first largest image will still get `fetchpriority='high'`.
     5691            return $postprocess( $loading_attrs, true );
     5692        }
    55565693    }
    55575694
    55585695    // Lazy-load by default for any unknown context.
    5559     return 'lazy';
     5696    $loading_attrs['loading'] = 'lazy';
     5697    return $postprocess( $loading_attrs, false );
    55605698}
    55615699
     
    56105748    return $content_media_count;
    56115749}
     5750
     5751/**
     5752 * Determines whether to add `fetchpriority='high'` to loading attributes.
     5753 *
     5754 * @since 6.3.0
     5755 * @access private
     5756 *
     5757 * @param array  $loading_attrs Array of the loading optimization attributes for the element.
     5758 * @param string $tag_name      The tag name.
     5759 * @param array  $attr          Array of the attributes for the element.
     5760 * @return array Updated loading optimization attributes for the element.
     5761 */
     5762function wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr ) {
     5763    // For now, adding `fetchpriority="high"` is only supported for images.
     5764    if ( 'img' !== $tag_name ) {
     5765        return $loading_attrs;
     5766    }
     5767
     5768    if ( isset( $attr['fetchpriority'] ) ) {
     5769        /*
     5770         * While any `fetchpriority` value could be set in `$loading_attrs`,
     5771         * for consistency we only do it for `fetchpriority="high"` since that
     5772         * is the only possible value that WordPress core would apply on its
     5773         * own.
     5774         */
     5775        if ( 'high' === $attr['fetchpriority'] ) {
     5776            $loading_attrs['fetchpriority'] = 'high';
     5777            wp_high_priority_element_flag( false );
     5778        }
     5779        return $loading_attrs;
     5780    }
     5781
     5782    // Lazy-loading and `fetchpriority="high"` are mutually exclusive.
     5783    if ( isset( $loading_attrs['loading'] ) && 'lazy' === $loading_attrs['loading'] ) {
     5784        return $loading_attrs;
     5785    }
     5786
     5787    if ( ! wp_high_priority_element_flag() ) {
     5788        return $loading_attrs;
     5789    }
     5790
     5791    /**
     5792     * Filters the minimum square-pixels threshold for an image to be eligible as the high-priority image.
     5793     *
     5794     * @since 6.3.0
     5795     *
     5796     * @param int $threshold Minimum square-pixels threshold. Default 50000.
     5797     */
     5798    $wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
     5799    if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
     5800        $loading_attrs['fetchpriority'] = 'high';
     5801        wp_high_priority_element_flag( false );
     5802    }
     5803    return $loading_attrs;
     5804}
     5805
     5806/**
     5807 * Accesses a flag that indicates if an element is a possible candidate for `fetchpriority='high'`.
     5808 *
     5809 * @since 6.3.0
     5810 * @access private
     5811 *
     5812 * @param bool $value Optional. Used to change the static variable. Default null.
     5813 * @return bool Returns true if high-priority element was marked already, otherwise false.
     5814 */
     5815function wp_high_priority_element_flag( $value = null ) {
     5816    static $high_priority_element = true;
     5817
     5818    if ( is_bool( $value ) ) {
     5819        $high_priority_element = $value;
     5820    }
     5821    return $high_priority_element;
     5822}
  • trunk/src/wp-includes/pluggable.php

    r55990 r56037  
    28162816            'force_display' => false,
    28172817            'loading'       => null,
     2818            'fetchpriority' => null,
    28182819            'extra_attr'    => '',
    28192820            'decoding'      => 'async',
    28202821        );
    28212822
    2822         if ( wp_lazy_loading_enabled( 'img', 'get_avatar' ) ) {
    2823             $defaults['loading'] = wp_get_loading_attr_default( 'get_avatar' );
    2824         }
    2825 
    28262823        if ( empty( $args ) ) {
    28272824            $args = array();
     
    28402837            $args['width'] = $args['size'];
    28412838        }
     2839
     2840        // Update args with loading optimized attributes.
     2841        $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' );
     2842
     2843        $args = array_merge( $args, $loading_optimization_attr );
    28422844
    28432845        if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
     
    28932895        }
    28942896
    2895         // Add `loading` and `decoding` attributes.
     2897        // Add `loading`, `fetchpriority` and `decoding` attributes.
    28962898        $extra_attr = $args['extra_attr'];
    28972899
     
    29142916
    29152917            $extra_attr .= "decoding='{$args['decoding']}'";
     2918        }
     2919
     2920        // Add support for `fetchpriority`.
     2921        if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true )
     2922            && ! preg_match( '/\bfetchpriority\s*=/', $extra_attr )
     2923        ) {
     2924            if ( ! empty( $extra_attr ) ) {
     2925                $extra_attr .= ' ';
     2926            }
     2927
     2928            $extra_attr .= "fetchpriority='{$args['fetchpriority']}'";
    29162929        }
    29172930
  • trunk/tests/phpunit/tests/media.php

    r55956 r56037  
    7777
    7878    /**
    79      * Ensures that the static content media count and related filter are reset between tests.
     79     * Ensures that the static content media count, fetchpriority element flag and related filter are reset between tests.
    8080     */
    8181    public function set_up() {
     
    8484        $this->reset_content_media_count();
    8585        $this->reset_omit_loading_attr_filter();
     86        $this->reset_high_priority_element_flag();
    8687    }
    8788
     
    22902291    public function test_wp_filter_content_tags_srcset_sizes_wrong() {
    22912292        $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
    2292         $img = wp_img_tag_add_loading_attr( $img, 'test' );
     2293        $img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
    22932294        $img = wp_img_tag_add_decoding_attr( $img, 'the_content' );
    22942295
     
    23052306        // Generate HTML and add a dummy srcset attribute.
    23062307        $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
    2307         $img = wp_img_tag_add_loading_attr( $img, 'test' );
     2308        $img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
    23082309        $img = wp_img_tag_add_decoding_attr( $img, 'the_content' );
    23092310        $img = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . 'srcset="image2x.jpg 2x" />', $img );
     
    24502451        // Build HTML for the editor.
    24512452        $img          = get_image_tag( self::$large_id, '', '', '', 'medium' );
    2452         $img          = wp_img_tag_add_loading_attr( $img, 'test' );
     2453        $img          = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
    24532454        $img_https    = str_replace( 'http://', 'https://', $img );
    24542455        $img_relative = str_replace( 'http://', '//', $img );
     
    29912992     * @ticket 50367
    29922993     * @ticket 50756
     2994     * @ticket 58235
    29932995     * @requires function imagejpeg
    29942996     */
     
    30053007        $iframe_no_width_height = '<iframe src="https://www.example.com"></iframe>';
    30063008
    3007         $lazy_img       = wp_img_tag_add_loading_attr( $img, 'test' );
    3008         $lazy_img_xhtml = wp_img_tag_add_loading_attr( $img_xhtml, 'test' );
    3009         $lazy_img_html5 = wp_img_tag_add_loading_attr( $img_html5, 'test' );
     3009        $lazy_img       = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
     3010        $lazy_img_xhtml = wp_img_tag_add_loading_optimization_attrs( $img_xhtml, 'test' );
     3011        $lazy_img_html5 = wp_img_tag_add_loading_optimization_attrs( $img_html5, 'test' );
    30103012        $lazy_iframe    = wp_iframe_tag_add_loading_attr( $iframe, 'test' );
    30113013
    30123014        // The following should not be modified because there already is a 'loading' attribute.
    3013         $img_eager    = str_replace( ' />', ' loading="eager" />', $img );
     3015        $img_eager    = str_replace( ' />', ' loading="eager" fetchpriority="high" />', $img );
    30143016        $iframe_eager = str_replace( '">', '" loading="eager">', $iframe );
    30153017
     
    30703072     * @ticket 44427
    30713073     * @ticket 50756
     3074     * @ticket 58235
    30723075     */
    30733076    public function test_wp_filter_content_tags_loading_lazy_opted_in() {
    30743077        $img         = get_image_tag( self::$large_id, '', '', '', 'medium' );
    3075         $lazy_img    = wp_img_tag_add_loading_attr( $img, 'test' );
     3078        $lazy_img    = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
    30763079        $lazy_img    = wp_img_tag_add_decoding_attr( $lazy_img, 'the_content' );
    30773080        $iframe      = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
     
    31283131     * @ticket 44427
    31293132     * @ticket 50367
     3133     *
     3134     * @expectedDeprecated wp_img_tag_add_loading_attr
     3135     * @expectedDeprecated wp_get_loading_attr_default
    31303136     */
    31313137    public function test_wp_img_tag_add_loading_attr() {
     
    31393145     * @ticket 44427
    31403146     * @ticket 50367
     3147     *
     3148     * @expectedDeprecated wp_img_tag_add_loading_attr
     3149     * @expectedDeprecated wp_get_loading_attr_default
    31413150     */
    31423151    public function test_wp_img_tag_add_loading_attr_without_src() {
     
    31503159     * @ticket 44427
    31513160     * @ticket 50367
     3161     *
     3162     * @expectedDeprecated wp_img_tag_add_loading_attr
     3163     * @expectedDeprecated wp_get_loading_attr_default
    31523164     */
    31533165    public function test_wp_img_tag_add_loading_attr_with_single_quotes() {
     
    32873299        // There should not be any loading attribute in this case.
    32883300        $this->assertStringNotContainsString( ' loading=', $img );
     3301    }
     3302
     3303    /**
     3304     * @ticket 58235
     3305     *
     3306     * @covers ::wp_get_attachment_image
     3307     * @covers ::wp_get_loading_optimization_attributes
     3308     */
     3309    public function test_wp_get_attachment_image_fetchpriority_not_present_by_default() {
     3310        $img = wp_get_attachment_image( self::$large_id );
     3311
     3312        $this->assertStringNotContainsString( ' fetchpriority="high"', $img );
     3313    }
     3314
     3315    /**
     3316     * @ticket 58235
     3317     *
     3318     * @covers ::wp_get_attachment_image
     3319     * @covers ::wp_get_loading_optimization_attributes
     3320     */
     3321    public function test_wp_get_attachment_image_fetchpriority_high_when_not_lazy_loaded() {
     3322        $img = wp_get_attachment_image( self::$large_id, 'large', false, array( 'loading' => false ) );
     3323
     3324        $this->assertStringContainsString( ' fetchpriority="high"', $img );
     3325    }
     3326
     3327    /**
     3328     * @ticket 58235
     3329     *
     3330     * @dataProvider data_provider_fetchpriority_values
     3331     *
     3332     * @covers ::wp_get_attachment_image
     3333     * @covers ::wp_get_loading_optimization_attributes
     3334     */
     3335    public function test_wp_get_attachment_image_fetchpriority_original_value_respected( $value ) {
     3336        $img = wp_get_attachment_image(
     3337            self::$large_id,
     3338            'large',
     3339            false,
     3340            array(
     3341                'loading'       => false,
     3342                'fetchpriority' => $value,
     3343            )
     3344        );
     3345
     3346        $this->assertStringContainsString( ' fetchpriority="' . $value . '"', $img );
     3347    }
     3348
     3349    /**
     3350     * Data provider.
     3351     *
     3352     * @return array[]
     3353     */
     3354    public function data_provider_fetchpriority_values() {
     3355        return self::text_array_to_dataprovider( array( 'high', 'low', 'auto' ) );
     3356    }
     3357
     3358    /**
     3359     * @ticket 58235
     3360     *
     3361     * @covers ::wp_get_attachment_image
     3362     * @covers ::wp_get_loading_optimization_attributes
     3363     */
     3364    public function test_wp_get_attachment_image_fetchpriority_stripped_when_false() {
     3365        $img = wp_get_attachment_image(
     3366            self::$large_id,
     3367            'large',
     3368            false,
     3369            array(
     3370                'loading'       => false,
     3371                'fetchpriority' => false,
     3372            )
     3373        );
     3374
     3375        $this->assertStringNotContainsString( ' fetchpriority=', $img );
     3376    }
     3377
     3378    /**
     3379     * @ticket 58235
     3380     *
     3381     * @covers ::wp_get_attachment_image
     3382     * @covers ::wp_get_loading_optimization_attributes
     3383     */
     3384    public function test_wp_get_attachment_image_fetchpriority_high_prevents_lazy_loading() {
     3385        $img = wp_get_attachment_image( self::$large_id, 'large', false, array( 'fetchpriority' => 'high' ) );
     3386
     3387        $this->assertStringNotContainsString( ' loading="lazy"', $img );
    32893388    }
    32903389
     
    35653664     * @covers ::wp_get_loading_attr_default
    35663665     *
     3666     * @expectedDeprecated wp_get_loading_attr_default
     3667     *
    35673668     * @dataProvider data_wp_get_loading_attr_default
    35683669     *
     
    35883689            $this->set_main_query( $query );
    35893690
    3590             // For contexts other than for the main content, still return 'lazy' even in the loop
    3591             // and in the main query, and do not increase the content media count.
     3691            /*
     3692             * For contexts other than for the main content, still return 'lazy' even in the loop
     3693             * and in the main query, and do not increase the content media count.
     3694             */
    35923695            $this->assertSame( 'lazy', wp_get_loading_attr_default( 'wp_get_attachment_image' ) );
    35933696
     
    36183721    /**
    36193722     * @ticket 53675
     3723     * @ticket 58235
    36203724     */
    36213725    public function test_wp_omit_loading_attr_threshold_filter() {
     3726        // Using a smaller image here.
     3727        $attr = array(
     3728            'width'  => 100,
     3729            'height' => 100,
     3730        );
     3731
    36223732        $query = $this->get_new_wp_query_for_published_post();
    36233733        $this->set_main_query( $query );
     
    36313741            // Due to the filter, now the first five elements should not be lazy-loaded, i.e. return `false`.
    36323742            for ( $i = 0; $i < 5; $i++ ) {
    3633                 $this->assertFalse( wp_get_loading_attr_default( 'the_content' ) );
     3743                $this->assertEmpty(
     3744                    wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
     3745                    'Expected second image to not be lazy-loaded.'
     3746                );
    36343747            }
    36353748
    36363749            // For following elements, lazy-load them again.
    3637             $this->assertSame( 'lazy', wp_get_loading_attr_default( 'the_content' ) );
     3750            $this->assertSame(
     3751                array( 'loading' => 'lazy' ),
     3752                wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' )
     3753            );
    36383754        }
    36393755    }
     
    36413757    /**
    36423758     * @ticket 53675
    3643      */
    3644     public function test_wp_filter_content_tags_with_wp_get_loading_attr_default() {
     3759     * @ticket 58235
     3760     *
     3761     * @covers ::wp_filter_content_tags
     3762     * @covers ::wp_img_tag_add_loading_optimization_attrs
     3763     * @covers ::wp_get_loading_optimization_attributes
     3764     */
     3765    public function test_wp_filter_content_tags_with_loading_optimization_attrs() {
    36453766        $img1         = get_image_tag( self::$large_id, '', '', '', 'large' );
    36463767        $iframe1      = '<iframe src="https://www.example.com" width="640" height="360"></iframe>';
     
    36483769        $img3         = get_image_tag( self::$large_id, '', '', '', 'thumbnail' );
    36493770        $iframe2      = '<iframe src="https://wordpress.org" width="640" height="360"></iframe>';
    3650         $lazy_img2    = wp_img_tag_add_loading_attr( $img2, 'the_content' );
    3651         $lazy_img3    = wp_img_tag_add_loading_attr( $img3, 'the_content' );
     3771        $prio_img1    = str_replace( ' src=', ' fetchpriority="high" src=', $img1 );
     3772        $lazy_img2    = wp_img_tag_add_loading_optimization_attrs( $img2, 'the_content' );
     3773        $lazy_img3    = wp_img_tag_add_loading_optimization_attrs( $img3, 'the_content' );
    36523774        $lazy_iframe2 = wp_iframe_tag_add_loading_attr( $iframe2, 'the_content' );
    36533775
     
    36573779        // Following the threshold of 2, the first two content media elements should not be lazy-loaded.
    36583780        $content_unfiltered = $img1 . $iframe1 . $img2 . $img3 . $iframe2;
    3659         $content_expected   = $img1 . $iframe1 . $lazy_img2 . $lazy_img3 . $lazy_iframe2;
     3781        $content_expected   = $prio_img1 . $iframe1 . $lazy_img2 . $lazy_img3 . $lazy_iframe2;
    36603782        $content_expected   = wp_img_tag_add_decoding_attr( $content_expected, 'the_content' );
    36613783
     
    37063828     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
    37073829     *
     3830     * @expectedDeprecated wp_get_loading_attr_default
     3831     *
    37083832     * @param string $context Context for the element for which the `loading` attribute value is requested.
    37093833     */
     
    37283852     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
    37293853     *
     3854     * @expectedDeprecated wp_get_loading_attr_default
     3855     *
    37303856     * @param string $context Context for the element for which the `loading` attribute value is requested.
    37313857     */
     
    37493875     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
    37503876     *
     3877     * @expectedDeprecated wp_get_loading_attr_default
     3878     *
    37513879     * @param string $context Context for the element for which the `loading` attribute value is requested.
    37523880     */
     
    37693897     *
    37703898     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
     3899     *
     3900     * @expectedDeprecated wp_get_loading_attr_default
    37713901     *
    37723902     * @param string $context Context for the element for which the `loading` attribute value is requested.
     
    37953925     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
    37963926     *
     3927     * @expectedDeprecated wp_get_loading_attr_default
     3928     *
    37973929     * @param string $context Context for the element for which the `loading` attribute value is requested.
    37983930     */
     
    38293961     * @ticket 56930
    38303962     * @ticket 58548
     3963     * @ticket 58235
    38313964     *
    38323965     * @covers ::wp_filter_content_tags
    3833      * @covers ::wp_get_loading_attr_default
     3966     * @covers ::wp_img_tag_add_loading_optimization_attrs
     3967     * @covers ::wp_get_loading_optimization_attributes
    38343968     */
    38353969    public function test_wp_filter_content_tags_does_not_lazy_load_first_image_in_block_theme() {
     
    38433977        $img1      = get_image_tag( self::$large_id, '', '', '', 'large' );
    38443978        $img2      = get_image_tag( self::$large_id, '', '', '', 'medium' );
    3845         $lazy_img2 = wp_img_tag_add_loading_attr( $img2, 'the_content' );
     3979        $prio_img1 = str_replace( ' src=', ' fetchpriority="high" src=', $img1 );
     3980        $lazy_img2 = wp_img_tag_add_loading_optimization_attrs( $img2, 'the_content' );
    38463981
    38473982        // Only the second image should be lazy-loaded.
    38483983        $post_content     = $img1 . $img2;
    3849         $expected_content = wpautop( $img1 . $lazy_img2 );
     3984        $expected_content = wpautop( $prio_img1 . $lazy_img2 );
    38503985
    38513986        // Update the post to test with so that it has the above post content.
     
    38744009     * @ticket 56930
    38754010     * @ticket 58548
     4011     * @ticket 58235
    38764012     *
    38774013     * @covers ::wp_filter_content_tags
    3878      * @covers ::wp_get_loading_attr_default
     4014     * @covers ::wp_img_tag_add_loading_optimization_attrs
     4015     * @covers ::wp_get_loading_optimization_attributes
    38794016     */
    38804017    public function test_wp_filter_content_tags_does_not_lazy_load_first_featured_image_in_block_theme() {
     
    38944031
    38954032        $content_img      = get_image_tag( self::$large_id, '', '', '', 'large' );
    3896         $lazy_content_img = wp_img_tag_add_loading_attr( $content_img, 'the_content' );
     4033        $lazy_content_img = wp_img_tag_add_loading_optimization_attrs( $content_img, 'the_content' );
    38974034
    38984035        // The featured image should not be lazy-loaded as it is the first image.
    38994036        $featured_image_id = self::$large_id;
    39004037        update_post_meta( self::$post_ids['publish'], '_thumbnail_id', $featured_image_id );
    3901         $expected_featured_image = '<figure class="wp-block-post-featured-image">' . get_the_post_thumbnail( self::$post_ids['publish'], 'post-thumbnail', array( 'loading' => false ) ) . '</figure>';
     4038        $expected_featured_image = '<figure class="wp-block-post-featured-image">' . get_the_post_thumbnail(
     4039            self::$post_ids['publish'],
     4040            'post-thumbnail',
     4041            array(
     4042                'loading'       => false,
     4043                'fetchpriority' => 'high',
     4044            )
     4045        ) . '</figure>';
     4046
     4047        // Reset high priority flag as the forced `fetchpriority="high"` above already modified it.
     4048        $this->reset_high_priority_element_flag();
    39024049
    39034050        // The post content image should be lazy-loaded since the featured image appears above.
     
    39134060            )
    39144061        );
    3915 
    39164062        $wp_query     = new WP_Query( array( 'p' => self::$post_ids['publish'] ) );
    39174063        $wp_the_query = $wp_query;
     
    39294075     *
    39304076     * @ticket 56930
     4077     * @ticket 58235
    39314078     *
    39324079     * @covers ::wp_filter_content_tags
    3933      * @covers ::wp_get_loading_attr_default
     4080     * @covers ::wp_img_tag_add_loading_optimization_attrs
     4081     * @covers ::wp_get_loading_optimization_attributes
    39344082     */
    39354083    public function test_wp_filter_content_tags_does_not_lazy_load_images_in_header() {
     
    39424090        // Use a single image for each header and footer template parts.
    39434091        $header_img = get_image_tag( self::$large_id, '', '', '', 'large' );
     4092        // Since header_img is qualified candidate for LCP, fetchpriority high is applied to it.
     4093        $header_img = str_replace( '<img', '<img fetchpriority="high"', $header_img );
     4094
    39444095        $footer_img = get_image_tag( self::$large_id, '', '', '', 'medium' );
    39454096
     
    39704121        // Header image should not be lazy-loaded, footer image should be lazy-loaded.
    39714122        $expected_template_content  = '<header class="wp-block-template-part">' . $header_img . '</header>';
    3972         $expected_template_content .= '<footer class="wp-block-template-part">' . wp_img_tag_add_loading_attr( $footer_img, 'force-lazy' ) . '</footer>';
     4123        $expected_template_content .= '<footer class="wp-block-template-part">' . wp_img_tag_add_loading_optimization_attrs( $footer_img, 'force-lazy' ) . '</footer>';
    39734124
    39744125        $html = get_the_block_template_html();
     
    39784129    /**
    39794130     * @ticket 58089
     4131     * @ticket 58235
    39804132     *
    39814133     * @covers ::wp_filter_content_tags
    3982      * @covers ::wp_get_loading_attr_default
     4134     * @covers ::wp_get_loading_optimization_attributes
    39834135     */
    39844136    public function test_wp_filter_content_tags_does_not_lazy_load_special_images_within_the_content() {
     
    39864138
    39874139        // Force no lazy-loading on the image tag expected in the content.
    3988         $expected_content = wpautop( wp_get_attachment_image( self::$large_id, 'large', false, array( 'loading' => false ) ) );
     4140        $expected_content = wpautop(
     4141            wp_get_attachment_image(
     4142                self::$large_id,
     4143                'large',
     4144                false,
     4145                array(
     4146                    'loading'       => false,
     4147                    'fetchpriority' => 'high',
     4148                )
     4149            )
     4150        );
     4151
     4152        // Reset high priority flag as the forced `fetchpriority="high"` above already modified it.
     4153        $this->reset_high_priority_element_flag();
    39894154
    39904155        // Overwrite post content with an image.
     
    40244189     * @covers ::wp_get_loading_attr_default
    40254190     *
     4191     * @expectedDeprecated wp_get_loading_attr_default
     4192     *
    40264193     * @dataProvider data_special_contexts_for_the_content
    40274194     *
     
    40384205     *
    40394206     * @covers ::wp_get_loading_attr_default
     4207     *
     4208     * @expectedDeprecated wp_get_loading_attr_default
    40404209     *
    40414210     * @dataProvider data_special_contexts_for_the_content
     
    40684237            'wp_get_attachment_image' => array( 'context' => 'wp_get_attachment_image' ),
    40694238        );
     4239    }
     4240
     4241    /**
     4242     * Tests that wp_get_loading_attr_default() returns the expected loading attribute value.
     4243     *
     4244     * @ticket 53675
     4245     * @ticket 56930
     4246     * @ticket 58235
     4247     *
     4248     * @covers ::wp_get_loading_optimization_attributes
     4249     *
     4250     * @dataProvider data_wp_get_loading_attr_default
     4251     *
     4252     * @param string $context
     4253     */
     4254    public function test_wp_get_loading_optimization_attributes( $context ) {
     4255        $attr = $this->get_width_height_for_high_priority();
     4256
     4257        // Return 'lazy' by default.
     4258        $this->assertSame(
     4259            array( 'loading' => 'lazy' ),
     4260            wp_get_loading_optimization_attributes( 'img', $attr, 'test' )
     4261        );
     4262        $this->assertSame(
     4263            array( 'loading' => 'lazy' ),
     4264            wp_get_loading_optimization_attributes( 'img', $attr, 'wp_get_attachment_image' )
     4265        );
     4266
     4267        // Return 'lazy' if not in the loop or the main query.
     4268        $this->assertSame(
     4269            array( 'loading' => 'lazy' ),
     4270            wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4271        );
     4272
     4273        $query = $this->get_new_wp_query_for_published_post();
     4274
     4275        while ( have_posts() ) {
     4276            the_post();
     4277
     4278            // Return 'lazy' if in the loop but not in the main query.
     4279            $this->assertSame(
     4280                array( 'loading' => 'lazy' ),
     4281                wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4282            );
     4283
     4284            // Set as main query.
     4285            $this->set_main_query( $query );
     4286
     4287            /*
     4288             * For contexts other than for the main content, still return 'lazy' even in the loop
     4289             * and in the main query, and do not increase the content media count.
     4290             */
     4291            $this->assertSame(
     4292                array( 'loading' => 'lazy' ),
     4293                wp_get_loading_optimization_attributes( 'img', $attr, 'wp_get_attachment_image' )
     4294            );
     4295
     4296            // First three element are not lazy loaded. However, first image is loaded with fetchpriority high.
     4297            $this->assertSame(
     4298                array( 'fetchpriority' => 'high' ),
     4299                wp_get_loading_optimization_attributes( 'img', $attr, $context ),
     4300                "Expected first image to not be lazy-loaded. First large image get's high fetchpriority."
     4301            );
     4302            $this->assertEmpty(
     4303                wp_get_loading_optimization_attributes( 'img', $attr, $context ),
     4304                'Expected second image to not be lazy-loaded.'
     4305            );
     4306            $this->assertEmpty(
     4307                wp_get_loading_optimization_attributes( 'img', $attr, $context ),
     4308                'Expected third image to not be lazy-loaded.'
     4309            );
     4310
     4311            // Return 'lazy' if in the loop and in the main query for any subsequent elements.
     4312            $this->assertSame(
     4313                array( 'loading' => 'lazy' ),
     4314                wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4315            );
     4316
     4317            // Yes, for all subsequent elements.
     4318            $this->assertSame(
     4319                array( 'loading' => 'lazy' ),
     4320                wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4321            );
     4322        }
     4323    }
     4324
     4325    /**
     4326     * Tests that wp_get_loading_optimization_attributes() returns the expected loading attribute value before loop but after get_header if not main query.
     4327     *
     4328     * @ticket 58211
     4329     * @ticket 58235
     4330     *
     4331     * @covers ::wp_get_loading_optimization_attributes
     4332     *
     4333     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
     4334     *
     4335     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4336     */
     4337    public function test_wp_get_loading_optimization_attributes_before_loop_if_not_main_query( $context ) {
     4338        global $wp_query;
     4339
     4340        $wp_query = $this->get_new_wp_query_for_published_post();
     4341
     4342        do_action( 'get_header' );
     4343
     4344        $attr = $this->get_width_height_for_high_priority();
     4345
     4346        // Lazy if not main query.
     4347        $this->assertSame(
     4348            array( 'loading' => 'lazy' ),
     4349            wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4350        );
     4351    }
     4352
     4353    /**
     4354     * Tests that wp_get_loading_optimization_attributes() returns the expected loading attribute value before loop but after get_header in main query but header was not called.
     4355     *
     4356     * @ticket 58211
     4357     * @ticket 58235
     4358     *
     4359     * @covers ::wp_get_loading_optimization_attributes
     4360     *
     4361     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
     4362     *
     4363     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4364     */
     4365    public function test_wp_get_loading_optimization_attributes_before_loop_in_main_query_but_header_not_called( $context ) {
     4366        global $wp_query;
     4367
     4368        $wp_query = $this->get_new_wp_query_for_published_post();
     4369        $this->set_main_query( $wp_query );
     4370
     4371        $attr = $this->get_width_height_for_high_priority();
     4372
     4373        // Lazy if header not called.
     4374        $this->assertSame(
     4375            array( 'loading' => 'lazy' ),
     4376            wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4377        );
     4378    }
     4379
     4380    /**
     4381     * Tests that wp_get_loading_optimization_attributes() returns the expected loading attribute value before loop but after get_header for main query.
     4382     *
     4383     * @ticket 58211
     4384     * @ticket 58235
     4385     *
     4386     * @covers ::wp_get_loading_optimization_attributes
     4387     *
     4388     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
     4389     *
     4390     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4391     */
     4392    public function test_wp_get_loading_optimization_attributes_before_loop_if_main_query( $context ) {
     4393        global $wp_query;
     4394
     4395        $wp_query = $this->get_new_wp_query_for_published_post();
     4396        $this->set_main_query( $wp_query );
     4397        do_action( 'get_header' );
     4398
     4399        $attr = $this->get_width_height_for_high_priority();
     4400
     4401        // First image is loaded with high fetchpriority.
     4402        $this->assertSame(
     4403            array( 'fetchpriority' => 'high' ),
     4404            wp_get_loading_optimization_attributes( 'img', $attr, $context ),
     4405            'Expected first image to not be lazy-loaded. First large image is loaded with high fetchpriority.'
     4406        );
     4407    }
     4408
     4409    /**
     4410     * Tests that wp_get_loading_optimization_attributes() returns the expected loading attribute value after get_header and after loop.
     4411     *
     4412     * @ticket 58211
     4413     * @ticket 58235
     4414     *
     4415     * @covers ::wp_get_loading_optimization_attributes
     4416     *
     4417     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
     4418     *
     4419     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4420     */
     4421    public function test_wp_get_loading_optimization_attributes_after_loop( $context ) {
     4422        global $wp_query;
     4423
     4424        $wp_query = $this->get_new_wp_query_for_published_post();
     4425        $this->set_main_query( $wp_query );
     4426
     4427        do_action( 'get_header' );
     4428
     4429        while ( have_posts() ) {
     4430            the_post();
     4431        }
     4432
     4433        $attr = $this->get_width_height_for_high_priority();
     4434        $this->assertSame(
     4435            array( 'loading' => 'lazy' ),
     4436            wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4437        );
     4438    }
     4439
     4440    /**
     4441     * Tests that wp_get_loading_optimization_attributes() returns the expected loading attribute if no loop.
     4442     *
     4443     * @ticket 58211
     4444     * @ticket 58235
     4445     *
     4446     * @covers ::wp_get_loading_optimization_attributes
     4447     *
     4448     * @dataProvider data_wp_get_loading_attr_default_before_and_no_loop
     4449     *
     4450     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4451     */
     4452    public function test_wp_get_loading_optimization_attributes_no_loop( $context ) {
     4453        global $wp_query;
     4454
     4455        $wp_query = $this->get_new_wp_query_for_published_post();
     4456        $this->set_main_query( $wp_query );
     4457
     4458        // Ensure header and footer is called.
     4459        do_action( 'get_header' );
     4460        do_action( 'get_footer' );
     4461
     4462        $attr = $this->get_width_height_for_high_priority();
     4463
     4464        // Load lazy if the there is no loop and footer was called.
     4465        $this->assertSame(
     4466            array( 'loading' => 'lazy' ),
     4467            wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4468        );
     4469    }
     4470
     4471    /**
     4472     * Tests that wp_get_loading_optimization_attributes() returns 'lazy' for special contexts when they're used outside of 'the_content' filter.
     4473     *
     4474     * @ticket 58089
     4475     * @ticket 58235
     4476     *
     4477     * @covers ::wp_get_loading_optimization_attributes
     4478     *
     4479     * @dataProvider data_special_contexts_for_the_content
     4480     *
     4481     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4482     */
     4483    public function test_wp_get_loading_optimization_attributes_should_return_lazy_for_special_contexts_outside_of_the_content( $context ) {
     4484        $attr = $this->get_width_height_for_high_priority();
     4485        $this->assertSame(
     4486            array( 'loading' => 'lazy' ),
     4487            wp_get_loading_optimization_attributes( 'img', $attr, $context )
     4488        );
     4489    }
     4490
     4491    /**
     4492     * Tests that wp_get_loading_optimization_attributes() returns false for special contexts when they're used within 'the_content' filter.
     4493     *
     4494     * @ticket 58089
     4495     * @ticket 58235
     4496     *
     4497     * @covers ::wp_get_loading_optimization_attributes
     4498     *
     4499     * @dataProvider data_special_contexts_for_the_content
     4500     *
     4501     * @param string $context Context for the element for which the `loading` attribute value is requested.
     4502     */
     4503    public function test_wp_get_loading_optimization_attributes_should_return_false_for_special_contexts_within_the_content( $context ) {
     4504        remove_all_filters( 'the_content' );
     4505
     4506        $result = null;
     4507        add_filter(
     4508            'the_content',
     4509            function( $content ) use ( &$result, $context ) {
     4510                $attr   = $this->get_width_height_for_high_priority();
     4511                $result = wp_get_loading_optimization_attributes( 'img', $attr, $context );
     4512                return $content;
     4513            }
     4514        );
     4515        apply_filters( 'the_content', '' );
     4516
     4517        $this->assertSame(
     4518            array( 'fetchpriority' => 'high' ),
     4519            $result,
     4520            'First large image is loaded with high fetchpriority.'
     4521        );
     4522    }
     4523
     4524    /**
     4525     * @ticket 44427
     4526     * @ticket 50367
     4527     * @ticket 58235
     4528     */
     4529    public function test_wp_img_tag_add_loading_optimization_attrs() {
     4530        $img = '<img src="example.png" alt=" width="300" height="225" />';
     4531        $img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
     4532
     4533        $this->assertStringContainsString( ' loading="lazy"', $img );
     4534    }
     4535
     4536    /**
     4537     * @ticket 44427
     4538     * @ticket 50367
     4539     * @ticket 58235
     4540     */
     4541    public function test_wp_img_tag_add_loading_optimization_attrs_without_src() {
     4542        $img = '<img alt="" width="300" height="225" />';
     4543        $img = wp_img_tag_add_loading_optimization_attrs( $img, 'test' );
     4544
     4545        $this->assertStringNotContainsString( ' loading=', $img );
    40704546    }
    40714547
     
    41214597     *
    41224598     * @ticket 56588
     4599     * @ticket 58235
    41234600     *
    41244601     * @covers ::wp_trim_excerpt
     
    41324609         */
    41334610        $this->force_omit_loading_attr_threshold( 2 );
     4611
    41344612        $post_content  = '<img src="example.jpg" width="800" height="600">';
    41354613        $post_content .= '<p>Some text.</p>';
     
    41454623        update_post_meta( $post_id, '_thumbnail_id', $featured_image_id );
    41464624
    4147         $expected_image_tag = get_the_post_thumbnail( $post_id, 'post-thumbnail', array( 'loading' => false ) );
     4625        $expected_image_tag = get_the_post_thumbnail(
     4626            $post_id,
     4627            'post-thumbnail',
     4628            array(
     4629                'loading'       => false,
     4630                'fetchpriority' => 'high',
     4631            )
     4632        );
     4633
     4634        // Reset high priority flag as the forced `fetchpriority="high"` above already modified it.
     4635        $this->reset_high_priority_element_flag();
    41484636
    41494637        $wp_query     = new WP_Query( array( 'post__in' => array( $post_id ) ) );
     
    41794667        // Clean up the above filter.
    41804668        remove_filter( 'wp_omit_loading_attr_threshold', '__return_null', 100 );
     4669    }
     4670
     4671    private function reset_high_priority_element_flag() {
     4672        wp_high_priority_element_flag( true );
    41814673    }
    41824674
     
    42774769     * @ticket 58212
    42784770     *
    4279      * @covers ::wp_get_attachment_image()
     4771     * @covers ::wp_get_attachment_image
    42804772     */
    42814773    public function test_wp_get_attachment_image_context_filter_default() {
     
    42924784     * @ticket 58212
    42934785     *
    4294      * @covers ::wp_get_attachment_image()
     4786     * @covers ::wp_get_attachment_image
    42954787     */
    42964788    public function test_wp_get_attachment_image_context_filter_value_is_passed_correctly() {
     
    43084800        wp_get_attachment_image( self::$large_id );
    43094801        $this->assertSame( 'my_custom_context', $last_context );
     4802    }
     4803
     4804    /**
     4805     * Tests tag restriction for `wp_get_loading_optimization_attributes()`.
     4806     *
     4807     * @ticket 58235
     4808     *
     4809     * @covers ::wp_get_loading_optimization_attributes
     4810     *
     4811     * @dataProvider data_wp_get_loading_optimization_attributes_min_required_attrs
     4812     *
     4813     * @param string $tag_name The tag name.
     4814     * @param string $attr Element attributes.
     4815     * @param array  $expected Expected return value.
     4816     * @param string $message Message to display if the test fails.
     4817     */
     4818    public function test_wp_get_loading_optimization_attributes_min_required_attrs( $tag_name, $attr, $expected, $message ) {
     4819        $context = 'the_post_thumbnail';
     4820        $this->assertSame( wp_get_loading_optimization_attributes( $tag_name, $attr, $context ), $expected, $message );
     4821    }
     4822
     4823    /**
     4824     * Data provider.
     4825     *
     4826     * @return array[]
     4827     */
     4828    public function data_wp_get_loading_optimization_attributes_min_required_attrs() {
     4829        return array(
     4830            'img_with_min_attrs' => array(
     4831                'img',
     4832                array(
     4833                    'width'  => 100,
     4834                    'height' => 100,
     4835                ),
     4836                array( 'loading' => 'lazy' ),
     4837                'Expected default `loading="lazy"`.',
     4838            ),
     4839            'img_without_height' => array(
     4840                'img',
     4841                array( 'width' => 100 ),
     4842                array(),
     4843                'Expected blank array as height is required.',
     4844            ),
     4845            'img_without_width'  => array(
     4846                'img',
     4847                array( 'height' => 100 ),
     4848                array(),
     4849                'Expected blank array as width is required.',
     4850            ),
     4851        );
     4852    }
     4853
     4854    /**
     4855     * Tests tag restriction for `wp_get_loading_optimization_attributes()`.
     4856     *
     4857     * @ticket 58235
     4858     *
     4859     * @covers ::wp_get_loading_optimization_attributes
     4860     *
     4861     * @dataProvider data_wp_get_loading_optimization_attributes_check_allowed_tags
     4862     *
     4863     * @param string $tag_name The tag name.
     4864     * @param array  $expected Expected return value.
     4865     * @param string $message Message to display if the test fails.
     4866     */
     4867    public function test_wp_get_loading_optimization_attributes_check_allowed_tags( $tag_name, $expected, $message ) {
     4868        $attr    = $this->get_width_height_for_high_priority();
     4869        $context = 'the_post_thumbnail';
     4870        $this->assertSame( wp_get_loading_optimization_attributes( $tag_name, $attr, $context ), $expected, $message );
     4871    }
     4872
     4873    /**
     4874     * Data provider.
     4875     *
     4876     * @return array[]
     4877     */
     4878    public function data_wp_get_loading_optimization_attributes_check_allowed_tags() {
     4879        return array(
     4880            'img'    => array(
     4881                'img',
     4882                array( 'loading' => 'lazy' ),
     4883                'Expected `loading="lazy"` for the img.',
     4884            ),
     4885            'iframe' => array(
     4886                'iframe',
     4887                array(
     4888                    'loading' => 'lazy',
     4889                ),
     4890                'Expected `loading="lazy"` for the iframe.',
     4891            ),
     4892            'video'  =>
     4893            array(
     4894                'video',
     4895                array(),
     4896                'Function should return empty array as video tag is not supported.',
     4897            ),
     4898        );
     4899    }
     4900
     4901    /**
     4902     * @ticket 58235
     4903     *
     4904     * @covers ::wp_get_loading_optimization_attributes
     4905     */
     4906    public function test_wp_get_loading_optimization_attributes_skip_for_block_template() {
     4907        $attr = $this->get_width_height_for_high_priority();
     4908
     4909        // Skip logic if context is `template`.
     4910        $this->assertSame(
     4911            array(),
     4912            wp_get_loading_optimization_attributes( 'img', $attr, 'template' ),
     4913            'Skip logic and return blank array for block template.'
     4914        );
     4915    }
     4916
     4917    /**
     4918     * @ticket 58235
     4919     *
     4920     * @covers ::wp_get_loading_optimization_attributes
     4921     */
     4922    public function test_wp_get_loading_optimization_attributes_header_block_template() {
     4923        $attr = $this->get_width_height_for_high_priority();
     4924
     4925        // Skip logic if context is `template`.
     4926        $this->assertSame(
     4927            array( 'fetchpriority' => 'high' ),
     4928            wp_get_loading_optimization_attributes( 'img', $attr, 'template_part_' . WP_TEMPLATE_PART_AREA_HEADER ),
     4929            'Images in the header block template part should not be lazy-loaded and first large image is set high fetchpriority.'
     4930        );
     4931    }
     4932
     4933    /**
     4934     * @ticket 58235
     4935     *
     4936     * @covers ::wp_get_loading_optimization_attributes
     4937     * @expectedIncorrectUsage wp_get_loading_optimization_attributes
     4938     */
     4939    public function test_wp_get_loading_optimization_attributes_incorrect_loading_attrs() {
     4940        $attr                  = $this->get_width_height_for_high_priority();
     4941        $attr['loading']       = 'lazy';
     4942        $attr['fetchpriority'] = 'high';
     4943
     4944        $this->assertSame(
     4945            array(
     4946                'loading'       => 'lazy',
     4947                'fetchpriority' => 'high',
     4948            ),
     4949            wp_get_loading_optimization_attributes( 'img', $attr, 'test' ),
     4950            'This should return both lazy-loading and high fetchpriority, but with doing_it_wrong message.'
     4951        );
     4952    }
     4953
     4954    /**
     4955     * @ticket 58235
     4956     *
     4957     * @covers ::wp_get_loading_optimization_attributes
     4958     */
     4959    public function test_wp_get_loading_optimization_attributes_if_loading_attr_present() {
     4960        $attr            = $this->get_width_height_for_high_priority();
     4961        $attr['loading'] = 'eager';
     4962
     4963        // Check fetchpriority high logic if loading attribute is present.
     4964        $this->assertSame(
     4965            array(
     4966                'fetchpriority' => 'high',
     4967            ),
     4968            wp_get_loading_optimization_attributes( 'img', $attr, 'test' ),
     4969            'fetchpriority should be set to high.'
     4970        );
     4971    }
     4972
     4973    /**
     4974     * @ticket 58235
     4975     *
     4976     * @covers ::wp_get_loading_optimization_attributes
     4977     */
     4978    public function test_wp_get_loading_optimization_attributes_low_res_image() {
     4979        $attr = array(
     4980            'width'   => 100,
     4981            'height'  => 100,
     4982            'loading' => 'eager',
     4983        );
     4984
     4985        // fetchpriority not set as image is of lower resolution.
     4986        $this->assertSame(
     4987            array(),
     4988            wp_get_loading_optimization_attributes( 'img', $attr, 'test' ),
     4989            'loading optimization attr array should be empty.'
     4990        );
     4991    }
     4992
     4993    /**
     4994     * @ticket 58235
     4995     *
     4996     * @covers ::wp_maybe_add_fetchpriority_high_attr
     4997     *
     4998     * @dataProvider data_wp_maybe_add_fetchpriority_high_attr
     4999     */
     5000    public function test_wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr, $expected_fetchpriority ) {
     5001        $loading_attrs = wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr );
     5002
     5003        if ( $expected_fetchpriority ) {
     5004            $this->assertArrayHasKey( 'fetchpriority', $loading_attrs, 'fetchpriority attribute should be present' );
     5005            $this->assertSame( $expected_fetchpriority, $loading_attrs['fetchpriority'], 'fetchpriority attribute has incorrect value' );
     5006        } else {
     5007            $this->assertArrayNotHasKey( 'fetchpriority', $loading_attrs, 'fetchpriority attribute should not be present' );
     5008        }
     5009    }
     5010
     5011    /**
     5012     * Data provider.
     5013     *
     5014     * @return array[]
     5015     */
     5016    public function data_wp_maybe_add_fetchpriority_high_attr() {
     5017        return array(
     5018            'small image'                   => array(
     5019                array(),
     5020                'img',
     5021                $this->get_insufficient_width_height_for_high_priority(),
     5022                false,
     5023            ),
     5024            'large image'                   => array(
     5025                array(),
     5026                'img',
     5027                $this->get_width_height_for_high_priority(),
     5028                'high',
     5029            ),
     5030            'image with loading=lazy'       => array(
     5031                array( 'loading' => 'lazy' ),
     5032                'img',
     5033                $this->get_width_height_for_high_priority(),
     5034                false,
     5035            ),
     5036            'image with loading=eager'      => array(
     5037                array( 'loading' => 'eager' ),
     5038                'img',
     5039                $this->get_width_height_for_high_priority(),
     5040                'high',
     5041            ),
     5042            'image with fetchpriority=high' => array(
     5043                array(),
     5044                'img',
     5045                array_merge(
     5046                    $this->get_insufficient_width_height_for_high_priority(),
     5047                    array( 'fetchpriority' => 'high' )
     5048                ),
     5049                'high',
     5050            ),
     5051            'image with fetchpriority=low'  => array(
     5052                array(),
     5053                'img',
     5054                array_merge(
     5055                    $this->get_insufficient_width_height_for_high_priority(),
     5056                    array( 'fetchpriority' => 'low' )
     5057                ),
     5058                false,
     5059            ),
     5060            'non-image element'             => array(
     5061                array(),
     5062                'video',
     5063                $this->get_width_height_for_high_priority(),
     5064                false,
     5065            ),
     5066        );
     5067    }
     5068
     5069    /**
     5070     * @ticket 58235
     5071     *
     5072     * @covers ::wp_maybe_add_fetchpriority_high_attr
     5073     */
     5074    public function test_wp_maybe_add_fetchpriority_high_attr_min_priority_filter() {
     5075        $attr = array(
     5076            'width'  => 50,
     5077            'height' => 50,
     5078        );
     5079
     5080        add_filter(
     5081            'wp_min_priority_img_pixels',
     5082            static function( $res ) {
     5083                return 2500; // 50*50=2500
     5084            }
     5085        );
     5086
     5087        // fetchpriority set to high as resolution is equal to (or greater than) 2500.
     5088        $this->assertSame(
     5089            array(
     5090                'fetchpriority' => 'high',
     5091            ),
     5092            wp_maybe_add_fetchpriority_high_attr( array(), 'img', $attr )
     5093        );
    43105094    }
    43115095
     
    44085192        $wp_the_query = $query;
    44095193    }
     5194
     5195    /**
     5196     * Returns an array with dimension attribute values eligible for a high priority image.
     5197     *
     5198     * @return array Associative array with 'width' and 'height' keys.
     5199     */
     5200    private function get_width_height_for_high_priority() {
     5201        /*
     5202         * The product of width * height must be >50000 to qualify for high priority image.
     5203         * 300 * 200 = 60000
     5204         */
     5205        return array(
     5206            'width'  => 300,
     5207            'height' => 200,
     5208        );
     5209    }
     5210
     5211    /**
     5212     * Returns an array with dimension attribute values ineligible for a high priority image.
     5213     *
     5214     * @return array Associative array with 'width' and 'height' keys.
     5215     */
     5216    private function get_insufficient_width_height_for_high_priority() {
     5217        /*
     5218         * The product of width * height must be >50000 to qualify for high priority image.
     5219         * 200 * 100 = 20000
     5220         */
     5221        return array(
     5222            'width'  => 200,
     5223            'height' => 100,
     5224        );
     5225    }
    44105226}
    44115227
Note: See TracChangeset for help on using the changeset viewer.