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.

Built from https://develop.svn.wordpress.org/trunk@56037


git-svn-id: http://core.svn.wordpress.org/trunk@55549 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Felix Arntz 2023-06-26 16:17:23 +00:00
parent e8d4e05333
commit 5ca27acd20
4 changed files with 446 additions and 76 deletions

View File

@ -4658,3 +4658,149 @@ function wp_queue_comments_for_comment_meta_lazyload( $comments ) {
wp_lazyload_comment_meta( $comment_ids );
}
/**
* Gets the default value to use for a `loading` attribute on an element.
*
* This function should only be called for a tag and context if lazy-loading is generally enabled.
*
* The function usually returns 'lazy', but uses certain heuristics to guess whether the current element is likely to
* appear above the fold, in which case it returns a boolean `false`, which will lead to the `loading` attribute being
* omitted on the element. The purpose of this refinement is to avoid lazy-loading elements that are within the initial
* viewport, which can have a negative performance impact.
*
* Under the hood, the function uses {@see wp_increase_content_media_count()} every time it is called for an element
* within the main content. If the element is the very first content element, the `loading` attribute will be omitted.
* This default threshold of 3 content elements to omit the `loading` attribute for can be customized using the
* {@see 'wp_omit_loading_attr_threshold'} filter.
*
* @since 5.9.0
* @deprecated 6.3.0 Use wp_get_loading_optimization_attributes() instead.
* @see wp_get_loading_optimization_attributes()
*
* @global WP_Query $wp_query WordPress Query object.
*
* @param string $context Context for the element for which the `loading` attribute value is requested.
* @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
* that the `loading` attribute should be skipped.
*/
function wp_get_loading_attr_default( $context ) {
_deprecated_function( __FUNCTION__, '6.3.0', 'wp_get_loading_optimization_attributes' );
global $wp_query;
// Skip lazy-loading for the overall block template, as it is handled more granularly.
if ( 'template' === $context ) {
return false;
}
/*
* Do not lazy-load images in the header block template part, as they are likely above the fold.
* For classic themes, this is handled in the condition below using the 'get_header' action.
*/
$header_area = WP_TEMPLATE_PART_AREA_HEADER;
if ( "template_part_{$header_area}" === $context ) {
return false;
}
// Special handling for programmatically created image tags.
if ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) {
/*
* Skip programmatically created images within post content as they need to be handled together with the other
* images within the post content.
* Without this clause, they would already be counted below which skews the number and can result in the first
* post content image being lazy-loaded only because there are images elsewhere in the post content.
*/
if ( doing_filter( 'the_content' ) ) {
return false;
}
// Conditionally skip lazy-loading on images before the loop.
if (
// Only apply for main query but before the loop.
$wp_query->before_loop && $wp_query->is_main_query()
/*
* Any image before the loop, but after the header has started should not be lazy-loaded,
* except when the footer has already started which can happen when the current template
* does not include any loop.
*/
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
) {
return false;
}
}
/*
* The first elements in 'the_content' or 'the_post_thumbnail' should not be lazy-loaded,
* as they are likely above the fold.
*/
if ( 'the_content' === $context || 'the_post_thumbnail' === $context ) {
// Only elements within the main query loop have special handling.
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
return 'lazy';
}
// Increase the counter since this is a main query content element.
$content_media_count = wp_increase_content_media_count();
// If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
return false;
}
// For elements after the threshold, lazy-load them as usual.
return 'lazy';
}
// Lazy-load by default for any unknown context.
return 'lazy';
}
/**
* Adds `loading` attribute to an `img` HTML tag.
*
* @since 5.5.0
* @deprecated 6.3.0 Use wp_img_tag_add_loading_optimization_attrs() instead.
* @see wp_img_tag_add_loading_optimization_attrs()
*
* @param string $image The HTML `img` tag where the attribute should be added.
* @param string $context Additional context to pass to the filters.
* @return string Converted `img` tag with `loading` attribute added.
*/
function wp_img_tag_add_loading_attr( $image, $context ) {
_deprecated_function( __FUNCTION__, '6.3.0', 'wp_img_tag_add_loading_optimization_attrs' );
/*
* Get loading attribute value to use. This must occur before the conditional check below so that even images that
* are ineligible for being lazy-loaded are considered.
*/
$value = wp_get_loading_attr_default( $context );
// Images should have source and dimension attributes for the `loading` attribute to be added.
if ( ! str_contains( $image, ' src="' ) || ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) {
return $image;
}
/**
* Filters the `loading` attribute value to add to an image. Default `lazy`.
*
* Returning `false` or an empty string will not add the attribute.
* Returning `true` will add the default value.
*
* @since 5.5.0
*
* @param string|bool $value The `loading` attribute value. Returning a falsey value will result in
* the attribute being omitted for the image.
* @param string $image The HTML `img` tag to be filtered.
* @param string $context Additional context about how the function was called or where the img tag is.
*/
$value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context );
if ( $value ) {
if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
$value = 'lazy';
}
return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image );
}
return $image;
}

View File

@ -1058,13 +1058,19 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f
* @param string $context The context. Default 'wp_get_attachment_image'.
*/
$context = apply_filters( 'wp_get_attachment_image_context', 'wp_get_attachment_image' );
$attr = wp_parse_args( $attr, $default_attr );
// Add `loading` attribute.
if ( wp_lazy_loading_enabled( 'img', $context ) ) {
$default_attr['loading'] = wp_get_loading_attr_default( $context );
}
$loading_attr = $attr;
$loading_attr['width'] = $width;
$loading_attr['height'] = $height;
$loading_optimization_attr = wp_get_loading_optimization_attributes(
'img',
$loading_attr,
$context
);
$attr = wp_parse_args( $attr, $default_attr );
// Add loading optimization attributes if not available.
$attr = array_merge( $attr, $loading_optimization_attr );
// Omit the `decoding` attribute if the value is invalid according to the spec.
if ( empty( $attr['decoding'] ) || ! in_array( $attr['decoding'], array( 'async', 'sync', 'auto' ), true ) ) {
@ -1073,10 +1079,15 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f
// If the default value of `lazy` for the `loading` attribute is overridden
// to omit the attribute for this image, ensure it is not included.
if ( array_key_exists( 'loading', $attr ) && ! $attr['loading'] ) {
if ( isset( $attr['loading'] ) && ! $attr['loading'] ) {
unset( $attr['loading'] );
}
// If the `fetchpriority` attribute is overridden and set to false or an empty string.
if ( isset( $attr['fetchpriority'] ) && ! $attr['fetchpriority'] ) {
unset( $attr['fetchpriority'] );
}
// Generate 'srcset' and 'sizes' if not already present.
if ( empty( $attr['srcset'] ) ) {
$image_meta = wp_get_attachment_metadata( $attachment_id );
@ -1780,7 +1791,7 @@ function wp_lazy_loading_enabled( $tag_name, $context ) {
*
* @see wp_img_tag_add_width_and_height_attr()
* @see wp_img_tag_add_srcset_and_sizes_attr()
* @see wp_img_tag_add_loading_attr()
* @see wp_img_tag_add_loading_optimization_attrs()
* @see wp_iframe_tag_add_loading_attr()
*
* @param string $content The HTML content to be filtered.
@ -1793,7 +1804,6 @@ function wp_filter_content_tags( $content, $context = null ) {
$context = current_filter();
}
$add_img_loading_attr = wp_lazy_loading_enabled( 'img', $context );
$add_iframe_loading_attr = wp_lazy_loading_enabled( 'iframe', $context );
if ( ! preg_match_all( '/<(img|iframe)\s[^>]+>/', $content, $matches, PREG_SET_ORDER ) ) {
@ -1857,10 +1867,8 @@ function wp_filter_content_tags( $content, $context = null ) {
$filtered_image = wp_img_tag_add_srcset_and_sizes_attr( $filtered_image, $context, $attachment_id );
}
// Add 'loading' attribute if applicable.
if ( $add_img_loading_attr && ! str_contains( $filtered_image, ' loading=' ) ) {
$filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context );
}
// Add loading optimization attributes if applicable.
$filtered_image = wp_img_tag_add_loading_optimization_attrs( $filtered_image, $context );
// Add 'decoding=async' attribute unless a 'decoding' attribute is already present.
if ( ! str_contains( $filtered_image, ' decoding=' ) ) {
@ -1914,45 +1922,101 @@ function wp_filter_content_tags( $content, $context = null ) {
}
/**
* Adds `loading` attribute to an `img` HTML tag.
* Adds optimization attributes to an `img` HTML tag.
*
* @since 5.5.0
* @since 6.3.0
*
* @param string $image The HTML `img` tag where the attribute should be added.
* @param string $context Additional context to pass to the filters.
* @return string Converted `img` tag with `loading` attribute added.
* @return string Converted `img` tag with optimization attributes added.
*/
function wp_img_tag_add_loading_attr( $image, $context ) {
// Get loading attribute value to use. This must occur before the conditional check below so that even images that
// are ineligible for being lazy-loaded are considered.
$value = wp_get_loading_attr_default( $context );
function wp_img_tag_add_loading_optimization_attrs( $image, $context ) {
$width = preg_match( '/ width=["\']([0-9]+)["\']/', $image, $match_width ) ? (int) $match_width[1] : null;
$height = preg_match( '/ height=["\']([0-9]+)["\']/', $image, $match_height ) ? (int) $match_height[1] : null;
$loading_val = preg_match( '/ loading=["\']([A-Za-z]+)["\']/', $image, $match_loading ) ? $match_loading[1] : null;
$fetchpriority_val = preg_match( '/ fetchpriority=["\']([A-Za-z]+)["\']/', $image, $match_fetchpriority ) ? $match_fetchpriority[1] : null;
// Images should have source and dimension attributes for the `loading` attribute to be added.
/*
* Get loading optimization attributes to use.
* This must occur before the conditional check below so that even images
* that are ineligible for being lazy-loaded are considered.
*/
$optimization_attrs = wp_get_loading_optimization_attributes(
'img',
array(
'width' => $width,
'height' => $height,
'loading' => $loading_val,
'fetchpriority' => $fetchpriority_val,
),
$context
);
// Images should have source and dimension attributes for the loading optimization attributes to be added.
if ( ! str_contains( $image, ' src="' ) || ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) {
return $image;
}
/**
* Filters the `loading` attribute value to add to an image. Default `lazy`.
*
* Returning `false` or an empty string will not add the attribute.
* Returning `true` will add the default value.
*
* @since 5.5.0
*
* @param string|bool $value The `loading` attribute value. Returning a falsey value will result in
* the attribute being omitted for the image.
* @param string $image The HTML `img` tag to be filtered.
* @param string $context Additional context about how the function was called or where the img tag is.
*/
$value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context );
// Retained for backward compatibility.
$loading_attrs_enabled = wp_lazy_loading_enabled( 'img', $context );
if ( $value ) {
if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) {
$value = 'lazy';
if ( empty( $loading_val ) && $loading_attrs_enabled ) {
/**
* Filters the `loading` attribute value to add to an image. Default `lazy`.
* This filter is added in for backward compatibility.
*
* Returning `false` or an empty string will not add the attribute.
* Returning `true` will add the default value.
* `true` and `false` usage supported for backward compatibility.
*
* @since 5.5.0
*
* @param string|bool $loading Current value for `loading` attribute for the image.
* @param string $image The HTML `img` tag to be filtered.
* @param string $context Additional context about how the function was called or where the img tag is.
*/
$filtered_loading_attr = apply_filters(
'wp_img_tag_add_loading_attr',
isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false,
$image,
$context
);
// Validate the values after filtering.
if ( isset( $optimization_attrs['loading'] ) && ! $filtered_loading_attr ) {
// Unset `loading` attributes if `$filtered_loading_attr` is set to `false`.
unset( $optimization_attrs['loading'] );
} elseif ( in_array( $filtered_loading_attr, array( 'lazy', 'eager' ), true ) ) {
/*
* If the filter changed the loading attribute to "lazy" when a fetchpriority attribute
* with value "high" is already present, trigger a warning since those two attribute
* values should be mutually exclusive.
*
* The same warning is present in `wp_get_loading_optimization_attributes()`, and here it
* is only intended for the specific scenario where the above filtered caused the problem.
*/
if ( isset( $optimization_attrs['fetchpriority'] ) && 'high' === $optimization_attrs['fetchpriority'] &&
( isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false ) !== $filtered_loading_attr &&
'lazy' === $filtered_loading_attr
) {
_doing_it_wrong(
__FUNCTION__,
__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
'6.3.0'
);
}
// The filtered value will still be respected.
$optimization_attrs['loading'] = $filtered_loading_attr;
}
return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image );
if ( ! empty( $optimization_attrs['loading'] ) ) {
$image = str_replace( '<img', '<img loading="' . esc_attr( $optimization_attrs['loading'] ) . '"', $image );
}
}
if ( empty( $fetchpriority_val ) && ! empty( $optimization_attrs['fetchpriority'] ) ) {
$image = str_replace( '<img', '<img fetchpriority="' . esc_attr( $optimization_attrs['fetchpriority'] ) . '"', $image );
}
return $image;
@ -2103,13 +2167,30 @@ function wp_iframe_tag_add_loading_attr( $iframe, $context ) {
// Get loading attribute value to use. This must occur before the conditional check below so that even iframes that
// are ineligible for being lazy-loaded are considered.
$value = wp_get_loading_attr_default( $context );
$optimization_attrs = wp_get_loading_optimization_attributes(
'iframe',
array(
/*
* The concrete values for width and height are not important here for now
* since fetchpriority is not yet supported for iframes.
* TODO: Use WP_HTML_Tag_Processor to extract actual values once support is
* added.
*/
'width' => str_contains( $iframe, ' width="' ) ? 100 : null,
'height' => str_contains( $iframe, ' height="' ) ? 100 : null,
// This function is never called when a 'loading' attribute is already present.
'loading' => null,
),
$context
);
// Iframes should have source and dimension attributes for the `loading` attribute to be added.
if ( ! str_contains( $iframe, ' src="' ) || ! str_contains( $iframe, ' width="' ) || ! str_contains( $iframe, ' height="' ) ) {
return $iframe;
}
$value = isset( $optimization_attrs['loading'] ) ? $optimization_attrs['loading'] : false;
/**
* Filters the `loading` attribute value to add to an iframe. Default `lazy`.
*
@ -5469,45 +5550,102 @@ function wp_get_webp_info( $filename ) {
}
/**
* Gets the default value to use for a `loading` attribute on an element.
* Gets loading optimization attributes.
*
* This function should only be called for a tag and context if lazy-loading is generally enabled.
* This function returns an array of attributes that should be merged into the given attributes array to optimize
* loading performance. Potential attributes returned by this function are:
* - `loading` attribute with a value of "lazy"
* - `fetchpriority` attribute with a value of "high"
*
* The function usually returns 'lazy', but uses certain heuristics to guess whether the current element is likely to
* appear above the fold, in which case it returns a boolean `false`, which will lead to the `loading` attribute being
* omitted on the element. The purpose of this refinement is to avoid lazy-loading elements that are within the initial
* viewport, which can have a negative performance impact.
* If any of these attributes are already present in the given attributes, they will not be modified. Note that no
* element should have both `loading="lazy"` and `fetchpriority="high"`, so the function will trigger a warning in case
* both attributes are present with those values.
*
* Under the hood, the function uses {@see wp_increase_content_media_count()} every time it is called for an element
* within the main content. If the element is the very first content element, the `loading` attribute will be omitted.
* This default threshold of 3 content elements to omit the `loading` attribute for can be customized using the
* {@see 'wp_omit_loading_attr_threshold'} filter.
*
* @since 5.9.0
* @since 6.3.0
*
* @global WP_Query $wp_query WordPress Query object.
*
* @param string $context Context for the element for which the `loading` attribute value is requested.
* @return string|bool The default `loading` attribute value. Either 'lazy', 'eager', or a boolean `false`, to indicate
* that the `loading` attribute should be skipped.
* @param string $tag_name The tag name.
* @param array $attr Array of the attributes for the tag.
* @param string $context Context for the element for which the loading optimization attribute is requested.
* @return array Loading optimization attributes.
*/
function wp_get_loading_attr_default( $context ) {
function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
global $wp_query;
// Skip lazy-loading for the overall block template, as it is handled more granularly.
/*
* Closure for postprocessing logic.
* It is here to avoid duplicate logic in many places below, without having
* to introduce a very specific private global function.
*/
$postprocess = static function( $loading_attributes, $with_fetchpriority = false ) use ( $tag_name, $attr, $context ) {
// Potentially add `fetchpriority="high"`.
if ( $with_fetchpriority ) {
$loading_attributes = wp_maybe_add_fetchpriority_high_attr( $loading_attributes, $tag_name, $attr );
}
// Potentially strip `loading="lazy"` if the feature is disabled.
if ( isset( $loading_attributes['loading'] ) && ! wp_lazy_loading_enabled( $tag_name, $context ) ) {
unset( $loading_attributes['loading'] );
}
return $loading_attributes;
};
$loading_attrs = array();
/*
* Skip lazy-loading for the overall block template, as it is handled more granularly.
* The skip is also applicable for `fetchpriority`.
*/
if ( 'template' === $context ) {
return false;
return $loading_attrs;
}
// Do not lazy-load images in the header block template part, as they are likely above the fold.
// For classic themes, this is handled in the condition below using the 'get_header' action.
// For now this function only supports images and iframes.
if ( 'img' !== $tag_name && 'iframe' !== $tag_name ) {
return $loading_attrs;
}
// For any resources, width and height must be provided, to avoid layout shifts.
if ( ! isset( $attr['width'], $attr['height'] ) ) {
return $loading_attrs;
}
if ( isset( $attr['loading'] ) ) {
/*
* While any `loading` value could be set in `$loading_attrs`, for
* consistency we only do it for `loading="lazy"` since that is the
* only possible value that WordPress core would apply on its own.
*/
if ( 'lazy' === $attr['loading'] ) {
$loading_attrs['loading'] = 'lazy';
if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
_doing_it_wrong(
__FUNCTION__,
__( 'An image should not be lazy-loaded and marked as high priority at the same time.' ),
'6.3.0'
);
}
}
return $postprocess( $loading_attrs, true );
}
// An image with `fetchpriority="high"` cannot be assigned `loading="lazy"` at the same time.
if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
return $postprocess( $loading_attrs, true );
}
/*
* Do not lazy-load images in the header block template part, as they are likely above the fold.
* For classic themes, this is handled in the condition below using the 'get_header' action.
*/
$header_area = WP_TEMPLATE_PART_AREA_HEADER;
if ( "template_part_{$header_area}" === $context ) {
return false;
return $postprocess( $loading_attrs, true );
}
// Special handling for programmatically created image tags.
if ( ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) ) {
if ( 'the_post_thumbnail' === $context || 'wp_get_attachment_image' === $context ) {
/*
* Skip programmatically created images within post content as they need to be handled together with the other
* images within the post content.
@ -5515,7 +5653,7 @@ function wp_get_loading_attr_default( $context ) {
* post content image being lazy-loaded only because there are images elsewhere in the post content.
*/
if ( doing_filter( 'the_content' ) ) {
return false;
return $postprocess( $loading_attrs, true );
}
// Conditionally skip lazy-loading on images before the loop.
@ -5529,7 +5667,7 @@ function wp_get_loading_attr_default( $context ) {
*/
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
) {
return false;
return $postprocess( $loading_attrs, true );
}
}
@ -5540,23 +5678,23 @@ function wp_get_loading_attr_default( $context ) {
if ( 'the_content' === $context || 'the_post_thumbnail' === $context ) {
// Only elements within the main query loop have special handling.
if ( is_admin() || ! in_the_loop() || ! is_main_query() ) {
return 'lazy';
$loading_attrs['loading'] = 'lazy';
return $postprocess( $loading_attrs, false );
}
// Increase the counter since this is a main query content element.
$content_media_count = wp_increase_content_media_count();
// If the count so far is below the threshold, return `false` so that the `loading` attribute is omitted.
// If the count so far is below the threshold, `loading` attribute is omitted.
if ( $content_media_count <= wp_omit_loading_attr_threshold() ) {
return false;
// The first largest image will still get `fetchpriority='high'`.
return $postprocess( $loading_attrs, true );
}
// For elements after the threshold, lazy-load them as usual.
return 'lazy';
}
// Lazy-load by default for any unknown context.
return 'lazy';
$loading_attrs['loading'] = 'lazy';
return $postprocess( $loading_attrs, false );
}
/**
@ -5609,3 +5747,76 @@ function wp_increase_content_media_count( $amount = 1 ) {
return $content_media_count;
}
/**
* Determines whether to add `fetchpriority='high'` to loading attributes.
*
* @since 6.3.0
* @access private
*
* @param array $loading_attrs Array of the loading optimization attributes for the element.
* @param string $tag_name The tag name.
* @param array $attr Array of the attributes for the element.
* @return array Updated loading optimization attributes for the element.
*/
function wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr ) {
// For now, adding `fetchpriority="high"` is only supported for images.
if ( 'img' !== $tag_name ) {
return $loading_attrs;
}
if ( isset( $attr['fetchpriority'] ) ) {
/*
* While any `fetchpriority` value could be set in `$loading_attrs`,
* for consistency we only do it for `fetchpriority="high"` since that
* is the only possible value that WordPress core would apply on its
* own.
*/
if ( 'high' === $attr['fetchpriority'] ) {
$loading_attrs['fetchpriority'] = 'high';
wp_high_priority_element_flag( false );
}
return $loading_attrs;
}
// Lazy-loading and `fetchpriority="high"` are mutually exclusive.
if ( isset( $loading_attrs['loading'] ) && 'lazy' === $loading_attrs['loading'] ) {
return $loading_attrs;
}
if ( ! wp_high_priority_element_flag() ) {
return $loading_attrs;
}
/**
* Filters the minimum square-pixels threshold for an image to be eligible as the high-priority image.
*
* @since 6.3.0
*
* @param int $threshold Minimum square-pixels threshold. Default 50000.
*/
$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );
if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
$loading_attrs['fetchpriority'] = 'high';
wp_high_priority_element_flag( false );
}
return $loading_attrs;
}
/**
* Accesses a flag that indicates if an element is a possible candidate for `fetchpriority='high'`.
*
* @since 6.3.0
* @access private
*
* @param bool $value Optional. Used to change the static variable. Default null.
* @return bool Returns true if high-priority element was marked already, otherwise false.
*/
function wp_high_priority_element_flag( $value = null ) {
static $high_priority_element = true;
if ( is_bool( $value ) ) {
$high_priority_element = $value;
}
return $high_priority_element;
}

View File

@ -2815,14 +2815,11 @@ if ( ! function_exists( 'get_avatar' ) ) :
'class' => null,
'force_display' => false,
'loading' => null,
'fetchpriority' => null,
'extra_attr' => '',
'decoding' => 'async',
);
if ( wp_lazy_loading_enabled( 'img', 'get_avatar' ) ) {
$defaults['loading'] = wp_get_loading_attr_default( 'get_avatar' );
}
if ( empty( $args ) ) {
$args = array();
}
@ -2840,6 +2837,11 @@ if ( ! function_exists( 'get_avatar' ) ) :
$args['width'] = $args['size'];
}
// Update args with loading optimized attributes.
$loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' );
$args = array_merge( $args, $loading_optimization_attr );
if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
$id_or_email = get_comment( $id_or_email );
}
@ -2892,7 +2894,7 @@ if ( ! function_exists( 'get_avatar' ) ) :
}
}
// Add `loading` and `decoding` attributes.
// Add `loading`, `fetchpriority` and `decoding` attributes.
$extra_attr = $args['extra_attr'];
if ( in_array( $args['loading'], array( 'lazy', 'eager' ), true )
@ -2915,6 +2917,17 @@ if ( ! function_exists( 'get_avatar' ) ) :
$extra_attr .= "decoding='{$args['decoding']}'";
}
// Add support for `fetchpriority`.
if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true )
&& ! preg_match( '/\bfetchpriority\s*=/', $extra_attr )
) {
if ( ! empty( $extra_attr ) ) {
$extra_attr .= ' ';
}
$extra_attr .= "fetchpriority='{$args['fetchpriority']}'";
}
$avatar = sprintf(
"<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>",
esc_attr( $args['alt'] ),

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.3-alpha-56036';
$wp_version = '6.3-alpha-56037';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.