Skip to content

Commit 3cc0edc

Browse files
authored
Merge pull request #528 from WordPress/experiment/fetchpriority
Implement new experimental `fetchpriority` module
2 parents 2395c41 + 66d2a0d commit 3cc0edc

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

.github/CODEOWNERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
/tests/modules/images/dominant-color @pbearne @spacedmonkey
6060
/tests/testdata/modules/images/dominant-color @pbearne @spacedmonkey
6161

62+
# Module: Fetchpriority
63+
/modules/images/fetchpriority @pbearne @adamsilverstein
64+
/tests/modules/images/fetchpriority @pbearne @adamsilverstein
65+
/tests/testdata/modules/images/fetchpriority @pbearne @adamsilverstein
66+
6267
# Module: Full Page Cache Health Check
6368
/modules/object-cache/audit-full-page-cache @manuelRod @westonruter
6469
/tests/modules/object-cache/audit-full-page-cache @manuelRod @westonruter
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* Module Name: Fetchpriority
4+
* Description: Adds a fetchpriority hint for the primary content image on the page to load faster.
5+
* Experimental: Yes
6+
*
7+
* @since n.e.x.t
8+
* @package performance-lab
9+
*/
10+
11+
/**
12+
* Filters an image tag in content to add the fetchpriority attribute if it is not lazy-loaded.
13+
*
14+
* @since n.e.x.t
15+
*
16+
* @param string $filtered_image The image tag to filter.
17+
* @param string $context The context of the image.
18+
* @return string The filtered image tag.
19+
*/
20+
function fetchpriority_img_tag_add_attr( $filtered_image, $context ) {
21+
22+
if ( 'the_content' !== $context && 'the_post_thumbnail' !== $context ) {
23+
return $filtered_image;
24+
}
25+
26+
// Fetchpriority relies on lazy loading logic.
27+
if ( ! wp_lazy_loading_enabled( 'img', $context ) ) {
28+
return $filtered_image;
29+
}
30+
31+
if ( ! empty( $filtered_image ) && strpos( $filtered_image, 'loading="lazy"' ) === false && strpos( $filtered_image, 'fetchpriority=' ) === false ) {
32+
$filtered_image = str_replace( '<img ', '<img fetchpriority="high" ', $filtered_image );
33+
remove_filter( 'wp_content_img_tag', 'fetchpriority_img_tag_add_attr' );
34+
remove_filter( 'post_thumbnail_html', 'fetchpriority_filter_post_thumbnail_html' );
35+
}
36+
37+
return $filtered_image;
38+
}
39+
add_filter( 'wp_content_img_tag', 'fetchpriority_img_tag_add_attr', 10, 2 );
40+
41+
/**
42+
* Filters the post thumbnail HTML to conditionally add the fetchpriority attribute.
43+
*
44+
* @since n.e.x.t
45+
*
46+
* @param string $html The post thumbnail HTML to filter.
47+
* @return string The filtered post thumbnail HTML.
48+
*/
49+
function fetchpriority_filter_post_thumbnail_html( $html ) {
50+
$html = fetchpriority_img_tag_add_attr( $html, 'the_post_thumbnail' );
51+
52+
return $html;
53+
}
54+
add_filter( 'post_thumbnail_html', 'fetchpriority_filter_post_thumbnail_html' );
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* Tests the adding of fetchpriority to img tags in the content.
4+
*
5+
* @covers ::fetchpriority_img_tag_add_attr
6+
*/
7+
class Fetchpriority_Test extends WP_UnitTestCase {
8+
protected static $post;
9+
protected static $attachment_id;
10+
protected static $attachment_id_2;
11+
12+
protected $current_size_filter_data = null;
13+
protected $current_size_filter_result = null;
14+
15+
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
16+
self::$post = $factory->post->create_and_get();
17+
$file = DIR_TESTDATA . '/images/canola.jpg';
18+
self::$attachment_id = $factory->attachment->create_upload_object(
19+
$file,
20+
self::$post->ID,
21+
array(
22+
'post_mime_type' => 'image/jpeg',
23+
)
24+
);
25+
self::$attachment_id_2 = $factory->attachment->create_upload_object(
26+
$file,
27+
self::$post->ID,
28+
array(
29+
'post_mime_type' => 'image/jpeg',
30+
)
31+
);
32+
}
33+
34+
public static function tear_down_after_class() {
35+
wp_delete_attachment( self::$attachment_id, true );
36+
wp_delete_attachment( self::$attachment_id_2, true );
37+
parent::tear_down_after_class();
38+
}
39+
40+
public function test_fetchpriority_img_tag_add_attr_based_on_context_and_loading_lazy() {
41+
$img = get_image_tag( self::$attachment_id, '', '', '', 'large' );
42+
43+
$this->assertStringContainsString( 'fetchpriority="high"', fetchpriority_img_tag_add_attr( $img, 'the_content' ) );
44+
$this->assertStringNotContainsString( 'fetchpriority="high"', fetchpriority_img_tag_add_attr( $img, 'not_content' ) );
45+
46+
$img = str_replace( '<img ', '<img loading="lazy" ', $img );
47+
$this->assertStringNotContainsString( 'fetchpriority="high"', fetchpriority_img_tag_add_attr( $img, 'the_content' ) );
48+
}
49+
50+
public function test_fetchpriority_img_tag_add_in_wp_filter_content_tags() {
51+
global $wp_query;
52+
global $wp_the_query;
53+
$img = get_image_tag( self::$attachment_id, '', '', '', 'large' );
54+
$img_2 = get_image_tag( self::$attachment_id_2, '', '', '', 'large' );
55+
56+
$img = '<!-- wp:image {"id":' . self::$attachment_id . ',"sizeSlug":"large","linkDestination":"none"} -->
57+
<figure class="wp-block-image size-large">' . $img . '</figure>
58+
<!-- /wp:image -->
59+
<!-- wp:paragraph -->
60+
<p>This is an example page.</p>
61+
<!-- /wp:paragraph -->
62+
<!-- wp:image {"id":' . self::$attachment_id_2 . ',"sizeSlug":"large","linkDestination":"none"} -->
63+
<figure class="wp-block-image size-large">' . $img_2 . '</figure>
64+
<!-- /wp:image -->
65+
';
66+
// Ensure image filtering occurs 'in_the_loop', is_main_query.
67+
$wp_the_query = $wp_query;
68+
$wp_query->in_the_loop = true;
69+
$content = wp_filter_content_tags( $img, 'the_content' );
70+
$this->assertStringContainsString( 'fetchpriority="high"', $content );
71+
$this->assertStringContainsString( 'loading="lazy"', $content );
72+
$this->assertTrue( strpos( $content, 'fetchpriority="high"' ) < strpos( $content, 'loading="lazy"' ) );
73+
74+
$this->assertEquals( 1, substr_count( $content, 'fetchpriority' ) );
75+
76+
// Disable lazy loading and verify fetchpriority isn't added.
77+
add_filter( 'wp_lazy_loading_enabled', '__return_false' );
78+
$content = wp_filter_content_tags( $img, 'the_content' );
79+
$this->assertStringNotContainsString( 'fetchpriority="high"', $content );
80+
81+
}
82+
}

0 commit comments

Comments
 (0)