diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php
index 0dc67f7b86..74796210e2 100644
--- a/src/wp-includes/media.php
+++ b/src/wp-includes/media.php
@@ -1048,6 +1048,12 @@ function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = f
$attr = wp_parse_args( $attr, $default_attr );
+ // If `loading` attribute default of `lazy` is overridden for this
+ // image to omit the attribute, ensure it is not included.
+ if ( array_key_exists( 'loading', $attr ) && ! $attr['loading'] ) {
+ unset( $attr['loading'] );
+ }
+
// Generate 'srcset' and 'sizes' if not already present.
if ( empty( $attr['srcset'] ) ) {
$image_meta = wp_get_attachment_metadata( $attachment_id );
@@ -1725,9 +1731,10 @@ function wp_img_tag_add_loading_attr( $image, $context ) {
*
* @since 5.5.0
*
- * @param string $value The `loading` attribute value, defaults to `lazy`.
- * @param string $image The HTML `img` tag to be filtered.
- * @param string $context Additional context about how the function was called or where the img tag is.
+ * @param string|bool $value The `loading` attribute value. Returning a false-y value will result in the
+ * attribute being omitted for the image. Default is `lazy`.
+ * @param string $image The HTML `img` tag to be filtered.
+ * @param string $context Additional context about how the function was called or where the img tag is.
*/
$value = apply_filters( 'wp_img_tag_add_loading_attr', 'lazy', $image, $context );
diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php
index 89b10be605..dbab853099 100644
--- a/tests/phpunit/tests/media.php
+++ b/tests/phpunit/tests/media.php
@@ -2675,24 +2675,139 @@ EOF;
}
/**
+ * @ticket 44427
+ * @ticket 50367
+ */
+ function test_wp_img_tag_add_loading_attr() {
+ $img = '
';
+ $img = wp_img_tag_add_loading_attr( $img, 'test' );
+
+ $this->assertContains( ' loading="lazy"', $img );
+ }
+
+ /**
+ * @ticket 44427
* @ticket 50367
*/
function test_wp_img_tag_add_loading_attr_without_src() {
$img = '
';
$img = wp_img_tag_add_loading_attr( $img, 'test' );
- $this->assertNotContains( ' loading="lazy"', $img );
+ $this->assertNotContains( ' loading=', $img );
}
/**
+ * @ticket 44427
* @ticket 50367
*/
function test_wp_img_tag_add_loading_attr_with_single_quotes() {
$img = "
";
$img = wp_img_tag_add_loading_attr( $img, 'test' );
+ $this->assertNotContains( ' loading=', $img );
+
+ // Test specifically that the attribute is not there with double-quotes,
+ // to avoid regressions.
$this->assertNotContains( ' loading="lazy"', $img );
}
+
+ /**
+ * @ticket 44427
+ * @ticket 50425
+ */
+ function test_wp_img_tag_add_loading_attr_opt_out() {
+ $img = '
';
+ add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
+
+ $this->assertNotContains( ' loading=', $img );
+ }
+
+ /**
+ * @ticket 44427
+ * @ticket 50425
+ */
+ function test_wp_get_attachment_image_loading() {
+ $img = wp_get_attachment_image( self::$large_id );
+
+ $this->assertContains( ' loading="lazy"', $img );
+ }
+
+ /**
+ * @ticket 44427
+ * @ticket 50425
+ */
+ function test_wp_get_attachment_image_loading_opt_out() {
+ add_filter( 'wp_lazy_loading_enabled', '__return_false' );
+ $img = wp_get_attachment_image( self::$large_id );
+
+ // There should not be any loading attribute in this case.
+ $this->assertNotContains( ' loading=', $img );
+ }
+
+ /**
+ * @ticket 44427
+ * @ticket 50425
+ */
+ function test_wp_get_attachment_image_loading_opt_out_individual() {
+ // The default is already tested above, the filter below ensures that
+ // lazy-loading is definitely enabled globally for images.
+ add_filter( 'wp_lazy_loading_enabled', '__return_true' );
+
+ $img = wp_get_attachment_image( self::$large_id, 'thumbnail', false, array( 'loading' => false ) );
+
+ // There should not be any loading attribute in this case.
+ $this->assertNotContains( ' loading=', $img );
+ }
+
+ /**
+ * @ticket 44427
+ * @ticket 50425
+ * @dataProvider data_wp_lazy_loading_enabled_tag_name_defaults
+ *
+ * @param string $tag_name Function context.
+ * @param bool $expected Expected return value.
+ */
+ function test_wp_lazy_loading_enabled_tag_name_defaults( $tag_name, $expected ) {
+ if ( $expected ) {
+ $this->assertTrue( wp_lazy_loading_enabled( $tag_name, 'the_content' ) );
+ } else {
+ $this->assertFalse( wp_lazy_loading_enabled( $tag_name, 'the_content' ) );
+ }
+ }
+
+ function data_wp_lazy_loading_enabled_tag_name_defaults() {
+ return array(
+ 'img => true' => array( 'img', true ),
+ 'iframe => false' => array( 'iframe', false ),
+ 'arbitrary tag => false' => array( 'blink', false ),
+ );
+ }
+
+ /**
+ * @ticket 50425
+ * @dataProvider data_wp_lazy_loading_enabled_context_defaults
+ *
+ * @param string $context Function context.
+ * @param bool $expected Expected return value.
+ */
+ function test_wp_lazy_loading_enabled_context_defaults( $context, $expected ) {
+ if ( $expected ) {
+ $this->assertTrue( wp_lazy_loading_enabled( 'img', $context ) );
+ } else {
+ $this->assertFalse( wp_lazy_loading_enabled( 'img', $context ) );
+ }
+ }
+
+ function data_wp_lazy_loading_enabled_context_defaults() {
+ return array(
+ 'wp_get_attachment_image => true' => array( 'wp_get_attachment_image', true ),
+ 'the_content => true' => array( 'the_content', true ),
+ 'the_excerpt => true' => array( 'the_excerpt', true ),
+ 'widget_text_content => true' => array( 'widget_text_content', true ),
+ 'get_avatar => true' => array( 'get_avatar', true ),
+ 'arbitrary context => true' => array( 'something_completely_arbitrary', true ),
+ );
+ }
}
/**