Skip to content
43 changes: 38 additions & 5 deletions src/wp-includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -5625,24 +5625,46 @@ function wp_get_webp_info( $filename ) {
function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
global $wp_query;

/**
* Filters whether to short-circuit loading optimization attributes.
*
* Returning an array from the filter will effectively short-circuit the loading of optimization attributes,
* returning that value instead.
*
* @since 6.4.0
*
* @param array|false $loading_attrs False by default, or array of loading optimization attributes to short-circuit.
* @param string $tag_name The tag name.
* @param array $attr Array of the attributes for the tag.
* @param string $context Context for the element for which the loading optimization attribute is requested.
*/
$loading_attrs = apply_filters( 'pre_wp_get_loading_optimization_attributes', false, $tag_name, $attr, $context );

if ( is_array( $loading_attrs ) ) {
return $loading_attrs;
}

$loading_attrs = array();

/*
* Skip lazy-loading for the overall block template, as it is handled more granularly.
* The skip is also applicable for `fetchpriority`.
*/
if ( 'template' === $context ) {
return $loading_attrs;
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}

// For now this function only supports images and iframes.
if ( 'img' !== $tag_name && 'iframe' !== $tag_name ) {
return $loading_attrs;
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}

// For any resources, width and height must be provided, to avoid layout shifts.
if ( ! isset( $attr['width'], $attr['height'] ) ) {
return $loading_attrs;
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}

/*
Expand All @@ -5654,7 +5676,8 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
*/
// TODO: Handle shortcode images together with the content (see https://core.trac.wordpress.org/ticket/58853).
if ( 'the_content' !== $context && 'do_shortcode' !== $context && doing_filter( 'the_content' ) ) {
return $loading_attrs;
/** This filter is documented in wp-includes/media.php */
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}

/*
Expand Down Expand Up @@ -5789,7 +5812,17 @@ function wp_get_loading_optimization_attributes( $tag_name, $attr, $context ) {
}
}

return $loading_attrs;
/**
* Filters the loading optimization attributes.
*
* @since 6.4.0
*
* @param array $loading_attrs The loading optimization attributes.
* @param string $tag_name The tag name.
* @param array $attr Array of the attributes for the tag.
* @param string $context Context for the element for which the loading optimization attribute is requested.
*/
return apply_filters( 'wp_get_loading_optimization_attributes', $loading_attrs, $tag_name, $attr, $context );
}

/**
Expand Down
125 changes: 114 additions & 11 deletions tests/phpunit/tests/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,8 @@ public function test_post_galleries_images() {
$ids2_srcs[] = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . "image$i.jpg";
}

$ids1_joined = implode( ',', array_slice( $ids1, 0, 3 ) );
$ids2_joined = implode( ',', array_slice( $ids2, 3, 3 ) );
$ids1_joined = join( ',', array_slice( $ids1, 0, 3 ) );
$ids2_joined = join( ',', array_slice( $ids2, 3, 3 ) );

$blob = <<<BLOB
[gallery ids="$ids1_joined"]
Expand Down Expand Up @@ -638,8 +638,8 @@ public function test_block_post_gallery_images() {
$imgs[] = '<figure><img src="' . $url . '" data-id="' . $i . '" /></figure>';
}

$imgs1_joined = implode( "\n", array_slice( $imgs, 0, 3 ) );
$imgs2_joined = implode( "\n", array_slice( $imgs, 3, 3 ) );
$imgs1_joined = join( "\n", array_slice( $imgs, 0, 3 ) );
$imgs2_joined = join( "\n", array_slice( $imgs, 3, 3 ) );

$blob = <<<BLOB
<!-- wp:gallery -->
Expand Down Expand Up @@ -677,8 +677,8 @@ public function test_block_post_gallery_images_json() {

}

$ids1_joined = implode( ',', array_slice( $ids, 0, 3 ) );
$ids2_joined = implode( ',', array_slice( $ids, 3, 3 ) );
$ids1_joined = join( ',', array_slice( $ids, 0, 3 ) );
$ids2_joined = join( ',', array_slice( $ids, 3, 3 ) );

$blob = <<<BLOB
<!-- wp:gallery {"ids":[$ids1_joined]} -->
Expand Down Expand Up @@ -718,9 +718,9 @@ public function test_mixed_post_gallery_images() {
$imgs[] = '<figure><img src="' . $url . '" data-id="' . $i . '" /></figure>';
}

$ids1_joined = implode( "\n", array_slice( $ids, 0, 3 ) );
$ids2_joined = implode( "\n", array_slice( $ids, 3, 3 ) );
$imgs2_joined = implode( "\n", array_slice( $imgs, 3, 3 ) );
$ids1_joined = join( "\n", array_slice( $ids, 0, 3 ) );
$ids2_joined = join( "\n", array_slice( $ids, 3, 3 ) );
$imgs2_joined = join( "\n", array_slice( $imgs, 3, 3 ) );

$blob = <<<BLOB
[gallery ids="$ids1_joined"]
Expand Down Expand Up @@ -762,7 +762,7 @@ public function test_block_inner_post_gallery_images() {

}

$imgs_joined = implode( "\n", $imgs );
$imgs_joined = join( "\n", $imgs );

$blob = <<<BLOB
<!-- wp:columns -->
Expand Down Expand Up @@ -805,7 +805,7 @@ public function test_block_post_gallery_innerblock_images() {

}

$imgs_joined = implode( "\n", $imgs );
$imgs_joined = join( "\n", $imgs );

$blob = <<<BLOB
<!-- wp:gallery -->
Expand Down Expand Up @@ -5440,6 +5440,109 @@ public function test_wp_get_loading_optimization_attributes_image_before_loop_in
$this->assertSame( 1, wp_increase_content_media_count( 0 ) );
}

/**
* Tests for pre_wp_get_loading_optimization_attributes filter.
*
* @ticket 58893
*/
public function test_pre_wp_get_loading_optimization_attributes_filter() {
add_filter(
'pre_wp_get_loading_optimization_attributes',
static function ( $loading_attrs ) {
if ( false === $loading_attrs ) {
// Initialize as an empty array.
$loading_attrs = array();
}
$loading_attrs['fetchpriority'] = 'high';

return $loading_attrs;
},
10,
1
);

$attr = $this->get_width_height_for_high_priority();

$this->assertSame(
array( 'fetchpriority' => 'high' ),
wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
'The filter did not return early fetchpriority attribute'
);

// Clean up the filter.
add_filter( 'pre_wp_get_loading_optimization_attributes', '__return_false' );

$this->assertSame(
array( 'loading' => 'lazy' ),
wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
'The filter did not return the default attributes.'
);

// Return no loading attributes.
add_filter( 'pre_wp_get_loading_optimization_attributes', '__return_empty_array' );

$this->assertSame(
array(),
wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
'The filter did not clean up all attributes.'
);

// Modify the loading attributes with any custom attributes.
add_filter(
'pre_wp_get_loading_optimization_attributes',
static function ( $loading_attrs ) {
if ( false === $loading_attrs ) {
// Initialize as an empty array.
$loading_attrs = array();
}
$loading_attrs['custom_attr'] = 'custom_value';

return $loading_attrs;
},
10,
1
);

$this->assertSame(
array( 'custom_attr' => 'custom_value' ),
wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
'The filter did not return custom attributes.'
);
}

/**
* Tests for wp_get_loading_optimization_attributes filter.
*
* @ticket 58893
*/
public function test_wp_get_loading_optimization_attributes_filter() {
$attr = $this->get_width_height_for_high_priority();

$this->assertSame(
array( 'loading' => 'lazy' ),
wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
'Before the filter it will not return the loading attribute.'
);

add_filter(
'wp_get_loading_optimization_attributes',
static function ( $loading_attrs ) {
unset( $loading_attrs['loading'] );
$loading_attrs['fetchpriority'] = 'high';

return $loading_attrs;
},
10,
1
);

$this->assertSame(
array( 'fetchpriority' => 'high' ),
wp_get_loading_optimization_attributes( 'img', $attr, 'the_content' ),
'After the filter it will not return the fetchpriority attribute.'
);
}

/**
* Helper method to keep track of the last context returned by the 'wp_get_attachment_image_context' filter.
*
Expand Down