Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e35dd28
Ensure IMG with fetchpriority=low does not get lazy-loaded or increas…
westonruter Mar 6, 2026
1477ca6
Improve comment grammar/clarity
westonruter Mar 7, 2026
36ebcb0
Fix typo in comment
westonruter Mar 7, 2026
1fa9a16
Fix grammar typo
westonruter Mar 7, 2026
7f0c9e7
Use single quotes
westonruter Mar 7, 2026
9b1c141
Re-use condition
westonruter Mar 7, 2026
2093108
Add phpdoc params
westonruter Mar 8, 2026
f5de5b2
Correct return description for wp_high_priority_element_flag()
westonruter Mar 8, 2026
edf76eb
Add missing return type for wp_high_priority_element_flag()
westonruter Mar 8, 2026
ec3c508
Update wp_maybe_add_fetchpriority_high_attr() to account for fetchpri…
westonruter Mar 9, 2026
1439f77
Update wp_get_loading_optimization_attributes() to account for fetchp…
westonruter Mar 9, 2026
e32f008
Fix fetchpriority attr handling in wp_maybe_add_fetchpriority_high_at…
westonruter Mar 9, 2026
003806e
Backport https://github.com/WordPress/gutenberg/pull/76302
westonruter Mar 9, 2026
e489e94
Improve since description
westonruter Mar 9, 2026
6e4e3bc
Fix typo in phpdoc
westonruter Mar 9, 2026
e3ec304
Account for IMG as root block element
westonruter Mar 9, 2026
7657a54
Improve test to ensure fetchpriority=auto is added to IMG
westonruter Mar 9, 2026
a5a575b
Reuse processor
westonruter Mar 9, 2026
0208a08
Mark WP_HTML_Tag_Processor::next_tag() as impure
westonruter Mar 9, 2026
0b88b8b
Leverage assertEqualHTML in tests and add IMG to more test cases
westonruter Mar 9, 2026
e19e290
Apply static analysis fixes to Tests_Block_Supports_Block_Visibility
westonruter Mar 9, 2026
d771766
Fix typo in comment
westonruter Mar 9, 2026
11c8683
Correct phpdoc
westonruter Mar 9, 2026
fcbcd95
Fix method reference
westonruter Mar 9, 2026
dcf1ecb
Refine phpdoc types
westonruter Mar 9, 2026
10c8c91
Fix typo in test_wp_loading_optimization_force_header_contexts_filter
westonruter Mar 9, 2026
0d3b2a0
Use affect instead of effect
westonruter Mar 9, 2026
a1bf2a3
Merge branch 'trunk' into trac-64823-fetchpriority-loading-optimizati…
westonruter Mar 10, 2026
86909e1
Avoid treating fetchpriority=auto as maybe_in_viewport and prevent in…
westonruter Mar 10, 2026
caaf92b
Fix typo in assertion message
westonruter Mar 10, 2026
c8e469d
Replicate assertion in companion test
westonruter Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/wp-includes/block-supports/block-visibility.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ function wp_render_block_visibility_support( $block_content, $block ) {
$processor = new WP_HTML_Tag_Processor( $block_content );
if ( $processor->next_tag() ) {
$processor->add_class( implode( ' ', $class_names ) );

/*
* Set all IMG tags to be `fetchpriority=auto` so that wp_get_loading_optimization_attributes() won't add
* `fetchpriority=high` or increment the media count to affect whether subsequent IMG tags get `loading=lazy`.
*/
do {
if ( 'IMG' === $processor->get_tag() ) {
$processor->set_attribute( 'fetchpriority', 'auto' );
}
} while ( $processor->next_tag() );
Comment on lines +143 to +151
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a backport from WordPress/gutenberg#76302

Copy link
Member

Choose a reason for hiding this comment

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

This function is for “block visibility” support, but it’s now also altering image-loading heuristics globally for any image inside the block. That may be intended, but it’s something that it extend the scope of this function. In future we need to check better place for it. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the context in which we know that an IMG will be conditionally displayed based on the viewport size. There isn't any other code I'm aware of where PHP is aware of such styles being applied. So I don't think there is currently another spot.

Copy link
Member Author

Choose a reason for hiding this comment

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

For visibility: this change was approved in the the corresponding Gutenberg PR by @t-hamano and @ramonjd.

$block_content = $processor->get_updated_html();
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/wp-includes/html-api/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,8 @@ public function change_parsing_namespace( string $new_namespace ): bool {
* @type string|null $tag_closers "visit" or "skip": whether to stop on tag closers, e.g. </div>.
* }
* @return bool Whether a tag was matched.
*
* @phpstan-impure
Copy link
Member Author

Choose a reason for hiding this comment

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

See https://phpstan.org/writing-php-code/phpdocs-basics#impure-functions

This is needed so that $processor->next_tag() isn't flagged by PHPStan in the above.

*/
public function next_tag( $query = null ): bool {
$this->parse_query( $query );
Expand Down
92 changes: 66 additions & 26 deletions src/wp-includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -5967,6 +5967,7 @@ function wp_get_webp_info( $filename ) {
* both attributes are present with those values.
*
* @since 6.3.0
* @since 7.0.0 Support `fetchpriority=low` and `fetchpriority=auto` so that `loading=lazy` is not added and the media count is not increased.
*
* @global WP_Query $wp_query WordPress Query object.
*
Expand Down Expand Up @@ -6067,7 +6068,9 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
}

// Logic to handle a `fetchpriority` attribute that is already provided.
if ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] ) {
$existing_fetchpriority = ( $attr['fetchpriority'] ?? null );
$is_low_fetchpriority = ( 'low' === $existing_fetchpriority );
if ( 'high' === $existing_fetchpriority ) {
/*
* If the image was already determined to not be in the viewport (e.g.
* from an already provided `loading` attribute), trigger a warning.
Expand All @@ -6090,6 +6093,31 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
} else {
$maybe_in_viewport = true;
}
} elseif ( $is_low_fetchpriority ) {
/*
* An IMG with fetchpriority=low is not initially displayed; it may be hidden in the Navigation Overlay,
* or it may be occluded in a non-initial carousel slide. Such images must not be lazy-loaded because the browser
* has no heuristic to know when to start loading them before the user needs to see them.
*/
$maybe_in_viewport = false;

// Preserve fetchpriority=low.
$loading_attrs['fetchpriority'] = 'low';
} elseif ( 'auto' === $existing_fetchpriority ) {
/*
* When a block's visibility support identifies that the block is conditionally displayed based on the viewport
* size, then it adds `fetchpriority=auto` to the block's IMG tags. These images must not be fetched with high
* priority because they could be erroneously loaded in viewports which do not even display them. Contrarily,
* they must not get `fetchpriority=low` because they may in fact be displayed in the current viewport. So as
* a signal to indicate that an IMG may be in the viewport, `fetchpriority=auto` is added. This has the effect
* here of preventing the media count from being increased, so that images hidden with block visibility do not
* affect whether a following IMG gets `loading=lazy`. In particular, `loading=lazy` should still be omitted
* on an IMG following any number of initial IMGs with `fetchpriority=auto` since those initial images may not
* be displayed.
*/

// Preserve fetchpriority=auto.
$loading_attrs['fetchpriority'] = 'auto';
}

if ( null === $maybe_in_viewport ) {
Expand Down Expand Up @@ -6140,7 +6168,7 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
* does not include any loop.
*/
&& did_action( 'get_header' ) && ! did_action( 'get_footer' )
) {
) {
$maybe_in_viewport = true;
$maybe_increase_count = true;
}
Expand All @@ -6149,12 +6177,14 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
/*
* If the element is in the viewport (`true`), potentially add
* `fetchpriority` with a value of "high". Otherwise, i.e. if the element
* is not not in the viewport (`false`) or it is unknown (`null`), add
* `loading` with a value of "lazy".
* is not in the viewport (`false`) or it is unknown (`null`), add
* `loading` with a value of "lazy" if the element is not already being
* de-prioritized with `fetchpriority=low` due to occlusion in
* Navigation Overlay, non-initial carousel slides, or a collapsed Details block.
*/
if ( $maybe_in_viewport ) {
$loading_attrs = wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr );
} else {
} elseif ( ! $is_low_fetchpriority ) {
// Only add `loading="lazy"` if the feature is enabled.
if ( wp_lazy_loading_enabled( $tag_name, $context ) ) {
$loading_attrs['loading'] = 'lazy';
Expand All @@ -6164,16 +6194,20 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
/*
* If flag was set based on contextual logic above, increase the content
* media count, either unconditionally, or based on whether the image size
* is larger than the threshold.
* is larger than the threshold. This does not apply when the IMG has
* fetchpriority=auto because it may be conditionally displayed by viewport
* size.
*/
if ( $increase_count ) {
wp_increase_content_media_count();
} elseif ( $maybe_increase_count ) {
/** This filter is documented in wp-includes/media.php */
$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );

if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
if ( 'auto' !== $existing_fetchpriority ) {
if ( $increase_count ) {
wp_increase_content_media_count();
} elseif ( $maybe_increase_count ) {
/** This filter is documented in wp-includes/media.php */
$wp_min_priority_img_pixels = apply_filters( 'wp_min_priority_img_pixels', 50000 );

if ( $wp_min_priority_img_pixels <= $attr['width'] * $attr['height'] ) {
wp_increase_content_media_count();
}
}
}

Expand Down Expand Up @@ -6245,27 +6279,31 @@ function wp_increase_content_media_count( $amount = 1 ) {
* Determines whether to add `fetchpriority='high'` to loading attributes.
*
* @since 6.3.0
* @since 7.0.0 Support is added for IMG tags with `fetchpriority='low'` and `fetchpriority='auto'`.
* @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.
* @param array<string, string> $loading_attrs Array of the loading optimization attributes for the element.
* @param string $tag_name The tag name.
* @param array<string, mixed> $attr Array of the attributes for the element.
* @return array<string, string> 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'] ) ) {
$existing_fetchpriority = $attr['fetchpriority'] ?? null;
if ( null !== $existing_fetchpriority && 'auto' !== $existing_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.
* When an IMG has been explicitly marked with `fetchpriority=high`, then honor that this is the element that
* should have the priority. In contrast, the Navigation block may add `fetchpriority=low` to an IMG which
* appears in the Navigation Overlay; such images should never be considered candidates for
* `fetchpriority=high`. Lastly, block visibility may add `fetchpriority=auto` to an IMG when the block is
* conditionally displayed based on viewport size. Such an image is considered an LCP element candidate if it
* exceeds the threshold for the minimum number of square pixels.
*/
if ( 'high' === $attr['fetchpriority'] ) {
if ( 'high' === $existing_fetchpriority ) {
$loading_attrs['fetchpriority'] = 'high';
wp_high_priority_element_flag( false );
}
Expand All @@ -6292,7 +6330,9 @@ function wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr
$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';
if ( 'auto' !== $existing_fetchpriority ) {
$loading_attrs['fetchpriority'] = 'high';
}
wp_high_priority_element_flag( false );
}

Expand All @@ -6306,9 +6346,9 @@ function wp_maybe_add_fetchpriority_high_attr( $loading_attrs, $tag_name, $attr
* @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.
* @return bool Returns true if the high-priority element was not already marked.
*/
function wp_high_priority_element_flag( $value = null ) {
function wp_high_priority_element_flag( $value = null ): bool {
static $high_priority_element = true;

if ( is_bool( $value ) ) {
Expand Down
Loading
Loading