From 906f5f78681bb77eee202131ee0f8ae1daffa886 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 16 Oct 2024 12:38:26 +0200 Subject: [PATCH 01/14] Fix typo --- plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php b/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php index f4b98b14ae..e3b4e9c077 100644 --- a/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php +++ b/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php @@ -208,7 +208,7 @@ private function reduce_layout_shifts( OD_Tag_Visitor_Context $context ): void { $embed_wrapper_xpath = self::get_embed_wrapper_xpath( $processor->get_xpath() ); /** - * Collection of the minimum heights for the element with each group keyed by the minimum viewport with. + * Collection of the minimum heights for the element with each group keyed by the minimum viewport width. * * @var array $minimums */ From 7a9d556c64bccb6754b1fb6dffb3191ba543dd24 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 16 Oct 2024 12:38:48 +0200 Subject: [PATCH 02/14] Add new method for reducing poster image size --- ...ss-image-prioritizer-video-tag-visitor.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php index 152489f629..578d6d8aa6 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php @@ -41,6 +41,8 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { return false; } + $this->reduce_poster_image_size( $context ); + $xpath = $processor->get_xpath(); // TODO: If $context->url_metric_group_collection->get_element_max_intersection_ratio( $xpath ) is 0.0, then the video is not in any initial viewport and the VIDEO tag could get the preload=none attribute added. @@ -69,4 +71,32 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { return true; } + + /** + * Reduce poster image size by choosing one that fits the maximum video size more closely. + * + * @since n.e.x.t + * + * @param OD_Tag_Visitor_Context $context Tag visitor context, with the cursor currently at an embed block. + */ + private function reduce_poster_image_size( OD_Tag_Visitor_Context $context ): void { + $processor = $context->processor; + $xpath = $processor->get_xpath(); + + $max_element_width = 0; + + $denormalized_elements = $context->url_metric_group_collection->get_all_denormalized_elements()[ $xpath ] ?? array(); + + foreach ( $denormalized_elements as list( , , $element ) ) { + $max_element_width = max( $max_element_width, $element['boundingClientRect']['width'] ?? 0 ); + } + + $poster = trim( (string) $processor->get_attribute( 'poster' ) ); + $poster_id = attachment_url_to_postid( $poster ); + + if ( $poster_id > 0 && $max_element_width > 0 ) { + $smaller_image_url = wp_get_attachment_image_url( $poster_id, array( (int) $max_element_width, 0 ) ); + $processor->set_attribute( 'poster', $smaller_image_url ); + } + } } From c2b20266efef9677f5c980c1f1a45e5082390b25 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 16 Oct 2024 12:58:33 +0200 Subject: [PATCH 03/14] Add test case --- .../test-cases/video-with-large-poster.php | 101 ++++++++++++++++++ .../image-prioritizer/tests/test-helper.php | 11 +- 2 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php b/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php new file mode 100644 index 0000000000..c0f5e3c897 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php @@ -0,0 +1,101 @@ + static function ( Test_Image_Prioritizer_Helper $test_case, WP_UnitTest_Factory $factory ) use( &$full_url, &$expected_url ): void { + $breakpoint_max_widths = array( 480, 600, 782 ); + + add_filter( + 'od_breakpoint_max_widths', + static function () use ( $breakpoint_max_widths ) { + return $breakpoint_max_widths; + } + ); + + foreach ( $breakpoint_max_widths as $non_desktop_viewport_width ) { + $elements = array( + array( + 'isLCP' => false, + 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::VIDEO]', + 'boundingClientRect' => $test_case->get_sample_dom_rect(), + ), + ); + OD_URL_Metrics_Post_Type::store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $non_desktop_viewport_width, + 'elements' => $elements, + ) + ) + ); + } + + OD_URL_Metrics_Post_Type::store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => 1000, + 'elements' => array( + array( + 'isLCP' => false, + 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::VIDEO]', + 'boundingClientRect' => $test_case->get_sample_dom_rect(), + ), + ), + ) + ) + ); + + $attachment_id = $factory->attachment->create_object( + DIR_TESTDATA . '/images/33772.jpg', + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_excerpt' => 'A sample caption', + ) + ); + + wp_generate_attachment_metadata( $attachment_id, DIR_TESTDATA . '/images/33772.jpg' ); + + $dom_rect = $test_case->get_sample_dom_rect(); + + $full_url = wp_get_attachment_url( $attachment_id ); + $expected_url = wp_get_attachment_image_url( $attachment_id, array( (int) $dom_rect['width'], 0 ) ); + }, + 'buffer' => static function () use ( &$full_url ) { + return " + + + + ... + + + + + + "; + }, + 'expected' => static function () use ( &$full_url, &$expected_url ) { + return " + + + + ... + + + + + + + "; + }, +); diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index c58bdaff07..8c286b31d0 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -79,16 +79,23 @@ public function data_provider_test_filter_tag_visitors(): array { * @covers Image_Prioritizer_Background_Image_Styled_Tag_Visitor * * @dataProvider data_provider_test_filter_tag_visitors + * + * @param callable $set_up Setup function. + * @param callable|string $buffer Content before. + * @param callable|string $expected Expected content after. */ - public function test_image_prioritizer_register_tag_visitors( Closure $set_up, string $buffer, string $expected ): void { - $set_up( $this ); + public function test_image_prioritizer_register_tag_visitors( Closure $set_up, $buffer, $expected ): void { + $set_up( $this, $this::factory() ); + $buffer = is_string( $buffer ) ? $buffer : $buffer(); $buffer = preg_replace( '::s', '', od_optimize_template_output_buffer( $buffer ) ); + $expected = is_string( $expected ) ? $expected : $expected(); + $this->assertEquals( $this->remove_initial_tabs( $expected ), $this->remove_initial_tabs( $buffer ), From e1276850e64e84bb04600be91990b697636ab85e Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 16 Oct 2024 13:09:21 +0200 Subject: [PATCH 04/14] Lint fixes --- .../tests/test-cases/video-with-large-poster.php | 6 +++--- plugins/image-prioritizer/tests/test-helper.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php b/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php index c0f5e3c897..3f8a895a6e 100644 --- a/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php +++ b/plugins/image-prioritizer/tests/test-cases/video-with-large-poster.php @@ -1,10 +1,10 @@ static function ( Test_Image_Prioritizer_Helper $test_case, WP_UnitTest_Factory $factory ) use( &$full_url, &$expected_url ): void { + 'set_up' => static function ( Test_Image_Prioritizer_Helper $test_case, WP_UnitTest_Factory $factory ) use ( &$full_url, &$expected_url ): void { $breakpoint_max_widths = array( 480, 600, 782 ); add_filter( @@ -65,7 +65,7 @@ static function () use ( $breakpoint_max_widths ) { $full_url = wp_get_attachment_url( $attachment_id ); $expected_url = wp_get_attachment_image_url( $attachment_id, array( (int) $dom_rect['width'], 0 ) ); }, - 'buffer' => static function () use ( &$full_url ) { + 'buffer' => static function () use ( &$full_url ) { return " diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 8c286b31d0..8919154e2f 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -84,7 +84,7 @@ public function data_provider_test_filter_tag_visitors(): array { * @param callable|string $buffer Content before. * @param callable|string $expected Expected content after. */ - public function test_image_prioritizer_register_tag_visitors( Closure $set_up, $buffer, $expected ): void { + public function test_image_prioritizer_register_tag_visitors( callable $set_up, $buffer, $expected ): void { $set_up( $this, $this::factory() ); $buffer = is_string( $buffer ) ? $buffer : $buffer(); From ca0f538a9c662c4e6f62a8c513fa2cf7b41cfe87 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 16 Oct 2024 14:43:05 +0200 Subject: [PATCH 05/14] Extract other logic to own method too --- ...ss-image-prioritizer-video-tag-visitor.php | 86 ++++++++++++------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php index 578d6d8aa6..01cf7e1c10 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php @@ -35,39 +35,10 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { return false; } - // Skip empty poster attributes and data: URLs. - $poster = trim( (string) $processor->get_attribute( 'poster' ) ); - if ( '' === $poster || $this->is_data_url( $poster ) ) { - return false; - } - - $this->reduce_poster_image_size( $context ); - - $xpath = $processor->get_xpath(); - // TODO: If $context->url_metric_group_collection->get_element_max_intersection_ratio( $xpath ) is 0.0, then the video is not in any initial viewport and the VIDEO tag could get the preload=none attribute added. - // If this element is the LCP (for a breakpoint group), add a preload link for it. - foreach ( $context->url_metric_group_collection->get_groups_by_lcp_element( $xpath ) as $group ) { - $link_attributes = array( - 'rel' => 'preload', - 'fetchpriority' => 'high', - 'as' => 'image', - 'href' => $poster, - 'media' => 'screen', - ); - - $crossorigin = $this->get_attribute_value( $processor, 'crossorigin' ); - if ( null !== $crossorigin ) { - $link_attributes['crossorigin'] = 'use-credentials' === $crossorigin ? 'use-credentials' : 'anonymous'; - } - - $context->link_collection->add_link( - $link_attributes, - $group->get_minimum_viewport_width(), - $group->get_maximum_viewport_width() - ); - } + $this->reduce_poster_image_size( $context ); + $this->preload_poster_image( $context ); return true; } @@ -81,7 +52,14 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { */ private function reduce_poster_image_size( OD_Tag_Visitor_Context $context ): void { $processor = $context->processor; - $xpath = $processor->get_xpath(); + + // Skip empty poster attributes and data: URLs. + $poster = trim( (string) $processor->get_attribute( 'poster' ) ); + if ( '' === $poster || $this->is_data_url( $poster ) ) { + return; + } + + $xpath = $processor->get_xpath(); $max_element_width = 0; @@ -91,7 +69,6 @@ private function reduce_poster_image_size( OD_Tag_Visitor_Context $context ): vo $max_element_width = max( $max_element_width, $element['boundingClientRect']['width'] ?? 0 ); } - $poster = trim( (string) $processor->get_attribute( 'poster' ) ); $poster_id = attachment_url_to_postid( $poster ); if ( $poster_id > 0 && $max_element_width > 0 ) { @@ -99,4 +76,47 @@ private function reduce_poster_image_size( OD_Tag_Visitor_Context $context ): vo $processor->set_attribute( 'poster', $smaller_image_url ); } } + + /** + * Preload poster image for the LCP