Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plugins/auto-sizes/hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ function auto_sizes_render_generator(): void {
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 );
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 );
add_filter( 'render_block_core/post-featured-image', 'auto_sizes_filter_image_tag', 10, 3 );
add_filter( 'render_block_core/group', 'auto_sizes_add_background_image_data_attributes', 5, 2 );
add_filter( 'render_block_core/cover', 'auto_sizes_add_background_image_data_attributes', 5, 2 );
Comment on lines +34 to +35
Copy link
Member

Choose a reason for hiding this comment

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

Taking auto_sizes_filter_image_tag() for inspiration, something like:

Suggested change
add_filter( 'render_block_core/group', 'auto_sizes_add_background_image_data_attributes', 5, 2 );
add_filter( 'render_block_core/cover', 'auto_sizes_add_background_image_data_attributes', 5, 2 );
add_filter( 'render_block_core/group', 'auto_sizes_filter_background_image_style', 5, 2 );
add_filter( 'render_block_core/cover', 'auto_sizes_filter_background_image_style', 5, 2 );

A new auto_sizes_filter_background_image_style() function will be needed modeled after auto_sizes_filter_image_tag().

add_filter( 'get_block_type_uses_context', 'auto_sizes_filter_uses_context', 10, 2 );
add_filter( 'render_block_context', 'auto_sizes_filter_render_block_context', 10, 3 );
// @codeCoverageIgnoreEnd
59 changes: 59 additions & 0 deletions plugins/auto-sizes/includes/improve-calculate-sizes.php
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,62 @@ function auto_sizes_get_featured_image_attachment_id( int $post_id ): int {

return (int) get_post_thumbnail_id( $post_id );
}

/**
* Adds data attributes for background image attachment ID to Group and Cover blocks.
*
* This exposes the attachment ID from block attributes as data attributes on the HTML element,
* allowing Image Prioritizer to access this information without using attachment_url_to_postid().
* This is particularly important for Cover blocks that may use sizes other than "full".
*
* @since n.e.x.t
*
* @param string|mixed $content The block content about to be rendered.
* @param array{ attrs?: array<string, mixed>, blockName?: string } $parsed_block The parsed block.
* @return string The updated block content.
*/
function auto_sizes_add_background_image_data_attributes( $content, array $parsed_block ): string {
if ( ! is_string( $content ) || '' === $content ) {
return '';
}

$block_name = $parsed_block['blockName'] ?? '';
$attrs = $parsed_block['attrs'] ?? array();
$attachment_id = null;
$image_url = null;

// Extract background image data based on block type.
if ( 'core/cover' === $block_name ) {
$attachment_id = $attrs['id'] ?? null;
$image_url = $attrs['url'] ?? null;
} elseif ( 'core/group' === $block_name ) {
$attachment_id = $attrs['style']['background']['backgroundImage']['id'] ?? null;
$image_url = $attrs['style']['background']['backgroundImage']['url'] ?? null;
}

// Normalize attachment ID type if possible.
if ( isset( $attachment_id ) && is_numeric( $attachment_id ) ) {
$attachment_id = (int) $attachment_id;
}

// Validate extracted data and update the element with background image.
if (
isset( $attachment_id, $image_url ) &&
is_int( $attachment_id ) &&
$attachment_id > 0 &&
'' !== $image_url &&
is_array( wp_get_attachment_metadata( $attachment_id ) )
) {
$processor = new WP_HTML_Tag_Processor( $content );
while ( $processor->next_tag() ) {
$style = $processor->get_attribute( 'style' );
if ( is_string( $style ) && str_contains( $style, 'background-image:' ) && str_contains( $style, $image_url ) ) {
$processor->set_attribute( 'data-bg-attachment-id', (string) $attachment_id );
Copy link
Member

Choose a reason for hiding this comment

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

As noted below, this attribute is being added here exclusively for the use of another plugin: Image Prioritizer. This dependency needs to be removed. Instead, this needs to be determining the size of the block as informed by the layout it has access to based on the block structure on a block template.

In other words, I believe all of the logic here in auto_sizes_filter_render_block_context() needs to be re-factored into auto_sizes_filter_uses_context(), auto_sizes_filter_render_block_context(), and auto_sizes_filter_image_tag(), for example, to follow the pattern of how other blocks are currently handled.

$content = $processor->get_updated_html();
break;
}
}
}

return $content;
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
$this->add_image_preload_link( $context->link_collection, $group, $background_image_url );
}

// Reduce the background image size if URL Metrics are available.
$this->reduce_background_image_size( $background_image_url, $context );

$this->lazy_load_bg_images( $context );

return true;
Expand Down Expand Up @@ -238,4 +241,40 @@ private function lazy_load_bg_images( OD_Tag_Visitor_Context $context ): void {
$this->added_lazy_assets = true;
}
}

/**
* Reduces background image size by choosing one that fits the element dimensions more closely.
*
* This is similar to how VIDEO poster images are optimized in the Video Tag Visitor.
*
* @since n.e.x.t
*
* @param non-empty-string $background_image_url Background image URL.
* @param OD_Tag_Visitor_Context $context Tag visitor context, with the cursor currently at an element with a background image.
*/
private function reduce_background_image_size( string $background_image_url, OD_Tag_Visitor_Context $context ): void {
$processor = $context->processor;

$max_element_width = $this->get_max_element_width( $context );

// If the element wasn't present in any URL Metrics gathered for desktop, then abort downsizing the background image.
if ( null === $max_element_width ) {
return;
}

// Try to get the attachment ID from the data attribute (populated via filter from block attributes).
$attachment_id = $processor->get_attribute( 'data-bg-attachment-id' );
Comment on lines +265 to +266
Copy link
Member

Choose a reason for hiding this comment

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

There is a plugin dependency issue here. Currently this requires that the auto-sizes plugin be active in order for this attribute to be added. This dependency shouldn't exist. This attribute should be added in the Image Prioritizer plugin for its own use. If the Image Prioritizer plugin isn't active, then the auto-sizes plugin should be able to scale down the image all on its own because it has access to template layout information on its own. The Image Prioritizer implementation is a parallel one which will benefit sites without auto-sizes active, or sites which are using a classic theme in which case the layout information is not available.


if ( is_numeric( $attachment_id ) && $attachment_id > 0 ) {
$smaller_image_url = wp_get_attachment_image_url( (int) $attachment_id, array( (int) $max_element_width, 0 ) );
if ( is_string( $smaller_image_url ) && $smaller_image_url !== $background_image_url ) {
// Replace the background image URL in the style attribute.
$style = $processor->get_attribute( 'style' );
if ( is_string( $style ) ) {
$updated_style = str_replace( $background_image_url, $smaller_image_url, $style );
Comment on lines +271 to +274
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

The str_replace approach could potentially replace unintended occurrences if the same URL appears multiple times in the style attribute. While unlikely in practice, consider using a more targeted replacement that specifically replaces within the background-image or background property. Alternatively, verify that this edge case is acceptable for the intended use cases.

Suggested change
// Replace the background image URL in the style attribute.
$style = $processor->get_attribute( 'style' );
if ( is_string( $style ) ) {
$updated_style = str_replace( $background_image_url, $smaller_image_url, $style );
// Replace the background image URL in the background/background-image declarations in the style attribute.
$style = $processor->get_attribute( 'style' );
if ( is_string( $style ) ) {
$declarations = explode( ';', $style );
foreach ( $declarations as &$declaration ) {
$trimmed_declaration = ltrim( $declaration );
if (
0 === stripos( $trimmed_declaration, 'background:' ) ||
0 === stripos( $trimmed_declaration, 'background-image:' )
) {
$declaration = str_replace( $background_image_url, $smaller_image_url, $declaration );
}
}
unset( $declaration );
$updated_style = implode( ';', $declarations );

Copilot uses AI. Check for mistakes.
$processor->set_attribute( 'style', $updated_style );
}
}
}
}
Comment on lines +255 to +279
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

The new reduce_background_image_size method lacks test coverage. Consider adding snapshot tests similar to the existing video poster tests (e.g., video-with-large-poster-and-desktop-url-metrics-collected) to verify the background image size reduction functionality works correctly.

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,33 @@ protected function get_attribute_value( OD_HTML_Tag_Processor $processor, string
}
return $value;
}

/**
* Obtains maximum width of the element from the URL Metrics group with the widest viewport width.
*
* This would be the desktop group. This prevents the situation where if URL Metrics have only so far been
* gathered for mobile viewports that an excessively-small image would end up getting served to the first
* desktop visitor.
*
* @since n.e.x.t
*
* @param OD_Tag_Visitor_Context $context Tag visitor context.
* @return float|null Maximum element width, or null if element not found.
*/
protected function get_max_element_width( OD_Tag_Visitor_Context $context ): ?float {
$xpath = $context->processor->get_xpath();
$max_element_width = null;

foreach ( $context->url_metric_group_collection->get_last_group() as $url_metric ) {
foreach ( $url_metric->get_elements() as $element ) {
if ( $element->get_xpath() === $xpath ) {
$width = $element->get_bounding_client_rect()['width'];
$max_element_width = null === $max_element_width ? $width : max( $max_element_width, $width );
break; // Move on to the next URL Metric.
}
}
}

return $max_element_width;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,10 @@ private function get_poster( OD_Tag_Visitor_Context $context ): ?string {
private function reduce_poster_image_size( string $poster, OD_Tag_Visitor_Context $context ): void {
$processor = $context->processor;

$xpath = $processor->get_xpath();

/*
* Obtain maximum width of the element exclusively from the URL Metrics group with the widest viewport width,
* which would be desktop. This prevents the situation where if URL Metrics have only so far been gathered for
* mobile viewports that an excessively-small poster would end up getting served to the first desktop visitor.
*/
$max_element_width = 0;
foreach ( $context->url_metric_group_collection->get_last_group() as $url_metric ) {
foreach ( $url_metric->get_elements() as $element ) {
if ( $element->get_xpath() === $xpath ) {
$max_element_width = max( $max_element_width, $element->get_bounding_client_rect()['width'] );
break; // Move on to the next URL Metric.
}
}
}
$max_element_width = $this->get_max_element_width( $context );

// If the element wasn't present in any URL Metrics gathered for desktop, then abort downsizing the poster.
if ( 0 === $max_element_width ) {
if ( null === $max_element_width ) {
return;
}

Expand Down
Loading