@@ -40,14 +40,28 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
4040
4141$ xpath = $ processor ->get_xpath ();
4242
43+ /**
44+ * Gets attribute value.
45+ *
46+ * @param string $attribute_name Attribute name.
47+ * @return string|true|null Normalized attribute value.
48+ */
49+ $ get_attribute_value = static function ( string $ attribute_name ) use ( $ processor ) {
50+ $ value = $ processor ->get_attribute ( $ attribute_name );
51+ if ( is_string ( $ value ) ) {
52+ $ value = strtolower ( trim ( $ value , " \t\f\r\n" ) );
53+ }
54+ return $ value ;
55+ };
56+
4357/*
4458 * When the same LCP element is common/shared among all viewport groups, make sure that the element has
4559 * fetchpriority=high, even though it won't really be needed because a preload link with fetchpriority=high
4660 * will also be added. Additionally, ensure that this common LCP element is never lazy-loaded.
4761 */
4862$ common_lcp_element = $ context ->url_metrics_group_collection ->get_common_lcp_element ();
4963if ( ! is_null ( $ common_lcp_element ) && $ xpath === $ common_lcp_element ['xpath ' ] ) {
50- if ( 'high ' === $ processor -> get_attribute ( 'fetchpriority ' ) ) {
64+ if ( 'high ' === $ get_attribute_value ( 'fetchpriority ' ) ) {
5165$ processor ->set_meta_attribute ( 'fetchpriority-already-added ' , true );
5266} else {
5367$ processor ->set_attribute ( 'fetchpriority ' , 'high ' );
@@ -81,7 +95,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
8195} else {
8296// Otherwise, make sure visible elements omit the loading attribute, and hidden elements include loading=lazy.
8397$ is_visible = $ element_max_intersection_ratio > 0.0 ;
84- $ loading = ( string ) $ processor -> get_attribute ( 'loading ' );
98+ $ loading = $ get_attribute_value ( 'loading ' );
8599if ( $ is_visible && 'lazy ' === $ loading ) {
86100$ processor ->remove_attribute ( 'loading ' );
87101} elseif ( ! $ is_visible && 'lazy ' !== $ loading ) {
@@ -90,6 +104,23 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
90104}
91105// TODO: If an image is visible in one breakpoint but not another, add loading=lazy AND add a regular-priority preload link with media queries (unless LCP in which case it should already have a fetchpriority=high link) so that the image won't be eagerly-loaded for viewports on which it is not shown.
92106
107+ // Ensure that sizes=auto is set properly.
108+ $ sizes = $ processor ->get_attribute ( 'sizes ' );
109+ if ( is_string ( $ sizes ) ) {
110+ $ is_lazy = 'lazy ' === $ get_attribute_value ( 'loading ' );
111+ $ has_auto = $ this ->sizes_attribute_includes_valid_auto ( $ sizes );
112+
113+ if ( $ is_lazy && ! $ has_auto ) {
114+ $ processor ->set_attribute ( 'sizes ' , "auto, $ sizes " );
115+ } elseif ( ! $ is_lazy && $ has_auto ) {
116+ // Remove auto from the beginning of the list.
117+ $ processor ->set_attribute (
118+ 'sizes ' ,
119+ (string ) preg_replace ( '/^[ \t\f\r\n]*auto[ \t\f\r\n]*(,[ \t\f\r\n]*)?/i ' , '' , $ sizes )
120+ );
121+ }
122+ }
123+
93124// If this element is the LCP (for a breakpoint group), add a preload link for it.
94125foreach ( $ context ->url_metrics_group_collection ->get_groups_by_lcp_element ( $ xpath ) as $ group ) {
95126$ link_attributes = array_merge (
@@ -110,8 +141,8 @@ static function ( string $value ): bool {
110141)
111142);
112143
113- $ crossorigin = $ processor -> get_attribute ( 'crossorigin ' );
114- if ( is_string ( $ crossorigin ) ) {
144+ $ crossorigin = $ get_attribute_value ( 'crossorigin ' );
145+ if ( null !== $ crossorigin ) {
115146$ link_attributes ['crossorigin ' ] = 'use-credentials ' === $ crossorigin ? 'use-credentials ' : 'anonymous ' ;
116147}
117148
@@ -126,4 +157,24 @@ static function ( string $value ): bool {
126157
127158return true ;
128159}
160+
161+ /**
162+ * Checks whether the given 'sizes' attribute includes the 'auto' keyword as the first item in the list.
163+ *
164+ * Per the HTML spec, if present it must be the first entry.
165+ *
166+ * @since n.e.x.t
167+ *
168+ * @param string $sizes_attr The 'sizes' attribute value.
169+ * @return bool True if the 'auto' keyword is present, false otherwise.
170+ */
171+ private function sizes_attribute_includes_valid_auto ( string $ sizes_attr ): bool {
172+ if ( function_exists ( 'wp_sizes_attribute_includes_valid_auto ' ) ) {
173+ return wp_sizes_attribute_includes_valid_auto ( $ sizes_attr );
174+ } elseif ( function_exists ( 'auto_sizes_attribute_includes_valid_auto ' ) ) {
175+ return auto_sizes_attribute_includes_valid_auto ( $ sizes_attr );
176+ } else {
177+ return 'auto ' === $ sizes_attr || str_starts_with ( $ sizes_attr , 'auto, ' );
178+ }
179+ }
129180}
0 commit comments