-
Notifications
You must be signed in to change notification settings - Fork 138
Description
As discussed in #1445, there is an opportunity to leverage the HTML Tag Processor to make auto_sizes_update_content_img_tag() more robust as follows:
function auto_sizes_update_content_img_tag( $html ): string {
if ( ! is_string( $html ) ) {
$html = '';
}
$processor = new WP_HTML_Tag_Processor( $html );
// Bail if there is no IMG tag.
if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) {
return $html;
}
// Bail early if the image is not lazy-loaded.
if ( 'lazy' !== $processor->get_attribute( 'loading' ) ) {
return $html;
}
$sizes = $processor->get_attribute( 'sizes' );
// Bail early if the image is not responsive.
if ( ! is_string( $sizes ) ) {
return $html;
}
// Don't add 'auto' to the sizes attribute if it already exists.
if ( auto_sizes_attribute_includes_auto( $sizes ) ) {
return $html;
}
$processor->set_attribute( 'sizes', "auto, $sizes" );
return $processor->get_updated_html();
}This not only provides a better DX, but it is safer and ensures the result is correct. It's being used increasingly throughout core as this is its intended purpose. We're also using it in our Performance plugins:
performance/plugins/auto-sizes/hooks.php
Lines 122 to 141 in e731385
| function auto_sizes_filter_image_tag( string $content, array $parsed_block ): string { | |
| $processor = new WP_HTML_Tag_Processor( $content ); | |
| $has_image = $processor->next_tag( array( 'tag_name' => 'img' ) ); | |
| // Only update the markup if an image is found. | |
| if ( $has_image ) { | |
| $processor->set_attribute( 'data-needs-sizes-update', true ); | |
| if ( isset( $parsed_block['attrs']['align'] ) ) { | |
| $processor->set_attribute( 'data-align', $parsed_block['attrs']['align'] ); | |
| } | |
| // Resize image width. | |
| if ( isset( $parsed_block['attrs']['width'] ) ) { | |
| $processor->set_attribute( 'data-resize-width', $parsed_block['attrs']['width'] ); | |
| } | |
| $content = $processor->get_updated_html(); | |
| } | |
| return $content; | |
| } |
performance/plugins/auto-sizes/hooks.php
Lines 153 to 157 in e731385
| function auto_sizes_improve_image_sizes_attributes( string $content ): string { | |
| $processor = new WP_HTML_Tag_Processor( $content ); | |
| if ( ! $processor->next_tag( array( 'tag_name' => 'img' ) ) ) { | |
| return $content; | |
| } |
performance/plugins/webp-uploads/picture-element.php
Lines 84 to 89 in e731385
| $processor = new WP_HTML_Tag_Processor( $image ); | |
| if ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { | |
| $width = (int) $processor->get_attribute( 'width' ); | |
| $height = (int) $processor->get_attribute( 'height' ); | |
| } | |
| $size_to_use = ( $width > 0 && $height > 0 ) ? array( $width, $height ) : 'full'; |
performance/plugins/webp-uploads/hooks.php
Lines 542 to 562 in e731385
| foreach ( $img_tags as list( $img ) ) { | |
| $processor = new WP_HTML_Tag_Processor( $img ); | |
| if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { | |
| // This condition won't ever be met since we're iterating over the IMG tags extracted with preg_match_all() above. | |
| continue; | |
| } | |
| // Find the ID of each image by the class. | |
| // TODO: It would be preferable to use the $processor->class_list() method but there seems to be some typing issues with PHPStan. | |
| $class_name = $processor->get_attribute( 'class' ); | |
| if ( | |
| ! is_string( $class_name ) | |
| || | |
| 1 !== preg_match( '/(?:^|\s)wp-image-([1-9]\d*)(?:\s|$)/i', $class_name, $matches ) | |
| ) { | |
| continue; | |
| } | |
| // Make sure we use the last item on the list of matches. | |
| $images[ $img ] = (int) $matches[1]; | |
| } |
Test Cases
Here are some cases where the current implementation breaks. Some of them rely on attribute values being single-quoted, and it's true that core only emits double quotes for attribute values. However, images can come from other places than core. For example, these snippets can be pasted in a Custom HTML block to reproduce the problems. Problematic attribute modifications could also come from plugin filters. Additionally, images may come from Embed blocks. (Aside: I just discovered that if I have these four images in Custom HTML blocks and then put a Flickr embed as the 5th block, core's heuristics are incorrectly adding fetchpriority=high to this image from Flickr since it apparently doesn't count the images in the Custom HTML blocks.)
Incorrectly not adding auto when it should be present due to the use of single quotes:
<img
src='https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg'
srcset='https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg 300w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1024x768.jpg 1024w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-768x576.jpg 768w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1536x1152.jpg 1536w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-2048x1536.jpg 2048w'
sizes='(max-width: 650px) 100vw, 650px'
loading='lazy'
>Incorrectly prefixing a data-tshirt-sizes attribute with auto and duplicating auto on an existing sizes attribute:
<img
data-tshirt-sizes="S M L"
src="https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg"
srcset="https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg 300w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1024x768.jpg 1024w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-768x576.jpg 768w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1536x1152.jpg 1536w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-2048x1536.jpg 2048w"
sizes="auto, (max-width: 650px) 100vw, 650px"
loading="lazy"
>Incorrectly adding auto due to loading="lazy" appearing in an attribute value:
<img
src="https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg"
srcset="https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg 300w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1024x768.jpg 1024w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-768x576.jpg 768w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1536x1152.jpg 1536w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-2048x1536.jpg 2048w"
sizes="(max-width: 650px) 100vw, 650px"
alt='This is the LCP image and it should not get loading="lazy"!'
>Incorrectly adding auto for an eager-loaded image when there is a data-removed-loading="lazy" attribute:
<img
src="https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg"
srcset="https://pd.w.org/2024/08/36566abfcb2952e20.99062056-300x225.jpg 300w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1024x768.jpg 1024w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-768x576.jpg 768w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-1536x1152.jpg 1536w, https://pd.w.org/2024/08/36566abfcb2952e20.99062056-2048x1536.jpg 2048w"
sizes="(max-width: 650px) 100vw, 650px"
data-removed-loading="lazy"
>All of these cases are fixed by using the HTML Tag Processor.
Other scenarios from @dmsnell in #1445 (comment):
- the
sizesattribute could be in any case, but this only tests for all-lowercase matches- the
sizesattribute could have single quotes, or no quotes, and have whitespace around the equals sign, e.g.Sizes = Auto- the
sizesattribute must be preceded by whitespace or a/, but this test also matchesdata-sizes="unrelated attribute"
Metadata
Metadata
Assignees
Labels
Type
Projects
Status