-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move Auto Sizes logic from Enhanced Responsive Images to Image Prioritizer #1476
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
'intersectionRatio' => 1, | ||
), | ||
'buffer' => '<img src="https://example.com/foo.jpg" alt="Foo" width="1200" height="800" loading="lazy" srcset="https://example.com/foo-480w.jpg 480w, https://example.com/foo-800w.jpg 800w" sizes="auto">', | ||
'expected' => '<img data-od-removed-loading="lazy" data-od-replaced-sizes="auto" src="https://example.com/foo.jpg" alt="Foo" width="1200" height="800" srcset="https://example.com/foo-480w.jpg 480w, https://example.com/foo-800w.jpg 800w" sizes="">', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why extra space is needed before srcset
attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the loading
attribute was previously located here and the tag processor doesn't currently strip out that extra space.
plugins/auto-sizes/hooks.php
Outdated
$value = $processor->get_attribute( 'loading' ); | ||
if ( ! is_string( $value ) || 'lazy' !== strtolower( trim( $value, " \t\f\r\n" ) ) ) { | ||
return $html; | ||
auto_sizes_apply_tag_parser_updates( $processor ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll trust that you know what you're doing, but the Tag and HTML Processors are most happy when local and not passed around. There's the danger here of making code appear to change without explanation, because something the process was sent to leads to its internal state changing, and those transitions aren't evident here where the processor was created.
An alternative style would be to pass in the HTML itself to the updater function, let it create its own processor, and receive the string output from it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for passing around the processor is that there are two different code paths to this logic being needed:
- When updating an IMG tag in isolation via the
wp_content_img_tag
filter. - When updating an IMG tag in the context of an entire document that is currently being iterated over in Optimization Detective.
In Optimization Detective the tag processor is passed to each of the registered tag visitors for each open tag while iterating over the document. See the auto_sizes_visit_tag()
function in the diff below for how it is being used there.
There's the danger here of making code appear to change without explanation, because something the process was sent to leads to its internal state changing, and those transitions aren't evident here where the processor was created.
I don't understand. What's the danger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is, by the way, a problem that eventually we'll want to solve in Core. I have no idea what the answer might look like today, but it's in the realm of "limited access processors" that could be handed off in a WordPress filter, for example.
here's an example of where things can go fishy.
$processor = new WP_HTML_Tag_Processor( $html );
$total_images = 0;
while ( $processor->next_tag( 'IMG' ) ) {
++$total_images;
do_something( $processor );
}
At the end of this script we only see $total_images === 1
and wonder why it's so wrong, given that we sent 1,000 images to it. Well, do_something()
called $processor->next_tag( 'SOURCE' )
but there weren't any, so by the time it returns control to the calling loop, the Tag Processor has reached the end of the document and skips all remaining images.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for that. Optimization Detective is actually accounting for that by setting a bookmark on the tag prior to invoking all the tag visitors, and then after they have been invoked it checks if the cursor has moved at all, and if so, it seeks back to the bookmark before continuing to the next tag:
performance/plugins/optimization-detective/optimization.php
Lines 217 to 232 in 4d22998
do { | |
$did_visit = false; | |
$processor->set_bookmark( $current_tag_bookmark ); // TODO: Should we break if this returns false? | |
foreach ( $visitors as $visitor ) { | |
$seek_count = $processor->get_seek_count(); | |
$next_token_count = $processor->get_next_token_count(); | |
$did_visit = $visitor( $tag_visitor_context ) || $did_visit; | |
// If the visitor traversed HTML tags, we need to go back to this tag so that in the next iteration any | |
// relevant tag visitors may apply, in addition to properly setting the data-od-xpath on this tag below. | |
if ( $seek_count !== $processor->get_seek_count() || $next_token_count !== $processor->get_next_token_count() ) { | |
$processor->seek( $current_tag_bookmark ); // TODO: Should this break out of the optimization loop if it returns false? | |
} | |
} | |
$processor->release_bookmark( $current_tag_bookmark ); |
The processor subclass introduces $processor->get_seek_count()
and $processor->get_next_token_count()
(the name of the latter I see is confusing) which return the number of times seek()
and next_token()
were called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is all baked into "I trust you" but in general, I like noting it, and don't think it's a terrible idea to caution others against following the example. it looks deceivingly simpler than it is.
plugins/auto-sizes/hooks.php
Outdated
$processor->set_attribute( | ||
'sizes', | ||
(string) preg_replace( '/^[ \t\f\r\n]*auto[ \t\f\r\n]*(,[ \t\f\r\n]*)?/i', '', $sizes ) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at this point it seems like there's some zig-zagging going on for the auto
keyword. just going to toss this out there, but we're using one method to detect its presence, and another method to add it, but I wonder if the PCRE method doesn't already in some way capture both situations, and might not be clearer to rely on vs. splitting the detection and replacement.
function toggle_auto_on_sizes_attribute( string $existing, bool $wants_auto ): string {
// Find any existing `auto` keyword at the front of the attribute.
preg_match( '/^([ \t\f\r\n]*auto[ \t\r\f\n]*)(?:,|$)/i', $existing, $auto_match, PREG_OFFSET_CAPTURE );
$has_auto = isset( $auto_match[1][0] );
if ( $wants_auto && ! $has_auto ) {
return "auto, {$sizes}";
} elseif ( ! $wants_auto && $has_auto ) {
// Chop off leading `auto` keyword.
return substr( $existing, strlen( $auto_match[1][0] ) );
}
return $existing;
}
Then the calling code uses $is_lazy
as a proxy for $wants_auto
.
$processor->set_attribute( 'sizes', toggle_auto_on_sizes_attribute( $sizes, $is_lazy ) );
I was talking to @felixarntz last week and think we should reconsider whether the OD compatibility code belongs in this plugin at all. When this was added, it was to ensure that when an Image Prioritizer removed In principle, it is a great goal to try to avoid adding Even so, I'm think it would be good to think about how we write a callback that can be consumed by other plugins that need to correct the |
Yeah, that makes sense. The current Optimization Detective extension code should instead be put inside of |
+1 to removing the OD integration for this from
|
fa8d99b
to
65bc8f0
Compare
$token = strtok( strtolower( $sizes_attr ), ',' ); | ||
return false !== $token && 'auto' === trim( $token, " \t\f\r\n" ); | ||
list( $first_size ) = explode( ',', $sizes_attr, 2 ); | ||
return 'auto' === strtolower( trim( $first_size, " \t\f\r\n" ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This matches what is currently in WordPress/wordpress-develop#7253
@@ -25,7 +25,7 @@ | |||
* href?: non-empty-string, | |||
* imagesrcset?: non-empty-string, | |||
* imagesizes?: non-empty-string, | |||
* crossorigin?: ''|'anonymous'|'use-credentials', | |||
* crossorigin?: 'anonymous'|'use-credentials', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the HTML spec allows for links the crossorigin
attribute to be (cf. MDN):
crossorigin
(boolean attribute, same asanonymous
)crossorigin=""
(empty value, same as anonymous`)crossorigin=sooooo-secret
(invalid value, same asanonymous
)crossorigin=anonymous
crossorigin=use-credentials
This PR normalizes the first three cases to only use the fourth case, which aligns with the reflected DOM crossOrigin
property. This will ensure that OD_Link_Collection::OD_Link_Collection()
will be able to properly merge links for all anonymous
links.
plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php
Outdated
Show resolved
Hide resolved
…available Co-authored-by: felixarntz <[email protected]>
plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php
Outdated
Show resolved
Hide resolved
Co-authored-by: Mukesh Panchal <[email protected]>
Thanks for handling this. Reviewed the changes post-merge and looks good to me. |
Now that Auto Sizes is proposed for Core (Core-61847, WordPress/wordpress-develop#7253), the logic in the Enhanced Responsive Images to apply
sizes=auto
in response to changes applied by Image Prioritizer doesn't make sense, as reasoned by @joemcgill in #1476 (comment). So this PR eliminates the Optimization Detective logic from that plugin and instead moves it to Image Prioritizer, incorporating it as part of theIMG
tag visitor which applies changes to theloading
attribute.I also realized that the
crossorigin
attribute wasn't being handled properly when constructing preload links. That is fixed in 5dc1eb5.Secondly, the
IMG
tag visitor also wasn't accounting for attribute values for theloading
,fetchpriority
, andcrossorigin
attributes which include whitespace or non-lowercase characters. These have been normalized in ae5d3ce.Obsolete PR Description
There are currently two different code paths in the Enhanced Responsive Images plugin for adding the
auto
value to thesizes
attribute, the first uses thewp_content_img_tag
filter and the second uses a tag visitor for Optimization Detective:performance/plugins/auto-sizes/hooks.php
Lines 55 to 88 in c5de91c
performance/plugins/auto-sizes/optimization-detective.php
Lines 21 to 52 in c5de91c
As of #1471, the non-Optimization Detective code path is leveraging HTML Tag Processor in the same as the Optimization Detective code path has been using since #1322. What this means is that we can reuse the same underlying tag processor logic for in both cases. This is also the case for Embed Optimizer in #1302.
Also, something that the Optimization Detective code path implemented was removing the
auto
value from thesizes
attribute when it is not lazy-loaded.This was not implemented in current logic forThis remains exclusively in the Optimization Detective code path, per @felixarntz in WordPress/wordpress-develop#7253 (comment).auto_sizes_update_content_img_tag()
, so this PR also handles that case.Lastly, in 7e8cebb I've gone back against myself for chosing
strtok()
which was discussed in #1445 (comment) with a follow-up by @dmsnell in #1445 (comment). The problem withstrtok()
is that if there are initial,
characters they will be skipped over. So,,,,,auto
is currently detected as having an initialauto
value, whereas Chrome will not recognize this (as it aligns with the HTML spec).