From c7798bac2fe6109619530b162c55d669d36aa55a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 13:48:06 -0700 Subject: [PATCH 1/8] Only seek back to the current tag if a tag visitor seeked --- .../class-od-html-tag-processor.php | 11 +++++++++++ plugins/optimization-detective/optimization.php | 15 +++++++++------ .../tests/test-class-od-html-tag-processor.php | 3 +++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index e2cc154e2c..2cf8471017 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -408,6 +408,17 @@ public function seek( $bookmark_name ): bool { return $result; } + /** + * Gets the seek count. + * + * @since n.e.x.t + * + * @return int Seek count. + */ + public function get_seek_count(): int { + return $this->seek_count; + } + /** * Sets a bookmark in the HTML document. * diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index c42a656fe0..26a40532db 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -177,13 +177,16 @@ function od_optimize_template_output_buffer( string $buffer ): string { while ( $processor->next_open_tag() ) { $did_visit = false; $processor->set_bookmark( $current_tag_bookmark ); - foreach ( $visitors as $visitor ) { - $did_visit = $visitor( $tag_visitor_context ) || $did_visit; - // Since the visitor may have traversed HTML tags, we need to make sure we go back to this tag so that - // in the next iteration any relevant tag visitors may apply, in addition to properly setting the data-od-xpath - // on this tag below. - $processor->seek( $current_tag_bookmark ); + foreach ( $visitors as $visitor ) { + $seek_count = $processor->get_seek_count(); + $did_visit = $visitor( $tag_visitor_context ) || $did_visit; + + // If the visitor traversed HTML tags, we need to go back to this tag so that in the next iteration any + // relevant tag visitors may apply, in addition to properly setting the data-od-xpath on this tag below. + if ( $seek_count !== $processor->get_seek_count() ) { + $processor->seek( $current_tag_bookmark ); + } } $processor->release_bookmark( $current_tag_bookmark ); diff --git a/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php b/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php index c37a5ecc73..df637544e7 100644 --- a/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php +++ b/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php @@ -494,6 +494,7 @@ public function test_html_tag_processor_wrapper_methods(): void { /** * Test bookmarking and seeking. * + * @covers ::get_seek_count * @covers ::set_bookmark * @covers ::seek * @covers ::release_bookmark @@ -519,6 +520,7 @@ public function test_bookmarking_and_seeking(): void { ); $actual_figure_contents = array(); + $this->assertSame( 0, $processor->get_seek_count() ); $bookmarks = array(); while ( $processor->next_open_tag() ) { @@ -580,6 +582,7 @@ public function test_bookmarking_and_seeking(): void { 'depth' => $processor->get_current_depth(), ); } + $this->assertSame( count( $bookmarks ), $processor->get_seek_count() ); $this->assertSame( $expected_figure_contents, $sought_actual_contents ); From 4a2466bc43131c41c4a23d49dc901c9e7e1e17e2 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 13:48:33 -0700 Subject: [PATCH 2/8] Add missing bookmark name to warning message --- .../optimization-detective/class-od-html-tag-processor.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index 2cf8471017..91380d93af 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -550,8 +550,11 @@ public function get_updated_html(): string { } if ( ! $this->has_bookmark( $bookmark ) ) { $this->warn( - /* translators: %s is the bookmark name */ - __( 'Unable to append markup to %s since the bookmark no longer exists.', 'optimization-detective' ) + sprintf( + /* translators: %s is the bookmark name */ + __( 'Unable to append markup to %s since the bookmark no longer exists.', 'optimization-detective' ), + $bookmark + ) ); } else { $start = $this->bookmarks[ $bookmark ]->start; From a60c5d040651034ae0b3f4837ef2e5a3c9e74994 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 14:09:52 -0700 Subject: [PATCH 3/8] Fix attribute order in tests now that seeking is not happening --- plugins/auto-sizes/tests/test-optimization-detective.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/tests/test-optimization-detective.php b/plugins/auto-sizes/tests/test-optimization-detective.php index 7fab403b4d..fdcc9375ef 100644 --- a/plugins/auto-sizes/tests/test-optimization-detective.php +++ b/plugins/auto-sizes/tests/test-optimization-detective.php @@ -87,7 +87,7 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { 'intersectionRatio' => 1, ), 'buffer' => 'Foo', - 'expected' => 'Foo', + 'expected' => 'Foo', ), ); } From 442d25265f2e710fe298246cc550b7ea896cef2f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 14:10:12 -0700 Subject: [PATCH 4/8] Add test to ensure tag visitation does not exceed seek limit --- .../tests/test-optimization.php | 74 ++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/tests/test-optimization.php b/plugins/optimization-detective/tests/test-optimization.php index 7ef65c2e09..9c7f1fa301 100644 --- a/plugins/optimization-detective/tests/test-optimization.php +++ b/plugins/optimization-detective/tests/test-optimization.php @@ -262,7 +262,6 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { 'video' => array( 'set_up' => function (): void { $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $sample_size = od_get_url_metrics_breakpoint_sample_size(); foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $viewport_width ) { OD_URL_Metrics_Post_Type::store_url_metric( $slug, @@ -309,6 +308,79 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { ', ), + 'many_images' => array( + 'set_up' => function (): void { + $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); + foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $viewport_width ) { + + $elements = array(); + for ( $i = 1; $i < WP_HTML_Tag_Processor::MAX_SEEK_OPS; $i++ ) { + $elements[] = array( + 'xpath' => sprintf( '/*[1][self::HTML]/*[2][self::BODY]/*[%d][self::IMG]', $i ), + 'isLCP' => false, + ); + } + + OD_URL_Metrics_Post_Type::store_url_metric( + $slug, + $this->get_validated_url_metric( + $viewport_width, + $elements + ) + ); + } + }, + 'buffer' => ' + + + + ... + + + ' . + join( + "\n", + call_user_func( + static function () { + $tags = array(); + for ( $i = 1; $i < WP_HTML_Tag_Processor::MAX_SEEK_OPS + 1; $i++ ) { + $tags[] = sprintf( 'Foo' ); + } + return $tags; + } + ) + ) . + ' + + + ', + 'expected' => ' + + + + ... + + + ' . + join( + "\n", + call_user_func( + static function () { + $tags = array(); + for ( $i = 1; $i < WP_HTML_Tag_Processor::MAX_SEEK_OPS + 1; $i++ ) { + $tags[] = sprintf( 'Foo', $i ); + } + return $tags; + } + ) + ) . + ' + + + + ', + ), + 'rss-response' => array( 'set_up' => static function (): void { ini_set( 'default_mimetype', 'application/rss+xml' ); // phpcs:ignore WordPress.PHP.IniSet.Risky From 89d5bcc822775457ca46cbca2965853a73ffc59b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 14:19:14 -0700 Subject: [PATCH 5/8] Update attribute order in embed-optimizer test due to not seeking --- plugins/embed-optimizer/tests/test-optimization-detective.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/embed-optimizer/tests/test-optimization-detective.php b/plugins/embed-optimizer/tests/test-optimization-detective.php index 5f2027aadd..dfdc94c4b1 100644 --- a/plugins/embed-optimizer/tests/test-optimization-detective.php +++ b/plugins/embed-optimizer/tests/test-optimization-detective.php @@ -476,14 +476,14 @@ function ( OD_Tag_Visitor_Context $context ): bool {
- +

So I heard you like FIGURE?

- +
Tagline from Figurine embed.
From d406219a9fd471d8bdae9ea3e2d59efc71310f45 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 15:11:08 -0700 Subject: [PATCH 6/8] Account for visitors calling next_token() not just seek() --- .../tests/test-optimization-detective.php | 2 +- .../class-od-html-tag-processor.php | 29 ++++++++++++++-- .../optimization-detective/optimization.php | 11 ++++--- .../test-class-od-html-tag-processor.php | 33 ++++++++++++++++++- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/plugins/embed-optimizer/tests/test-optimization-detective.php b/plugins/embed-optimizer/tests/test-optimization-detective.php index dfdc94c4b1..cad101ad71 100644 --- a/plugins/embed-optimizer/tests/test-optimization-detective.php +++ b/plugins/embed-optimizer/tests/test-optimization-detective.php @@ -522,7 +522,7 @@ function ( OD_Tag_Visitor_Context $context ): bool { // Set a bunch of bookmarks to fill up the total allowed. $remaining_bookmark_count = WP_HTML_Tag_Processor::MAX_BOOKMARKS - count( $bookmarks ); for ( $i = 0; $i < $remaining_bookmark_count; $i++ ) { - $processor->set_bookmark( "body_bookmark_{$i}" ); + $this->assertTrue( $processor->set_bookmark( "body_bookmark_{$i}" ) ); } return true; } diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index 91380d93af..46b1f25750 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -107,6 +107,7 @@ final class OD_HTML_Tag_Processor extends WP_HTML_Tag_Processor { /** * Bookmark for the end of the HEAD. * + * @todo Consider reserving this. * @since 0.4.0 * @var string */ @@ -115,6 +116,7 @@ final class OD_HTML_Tag_Processor extends WP_HTML_Tag_Processor { /** * Bookmark for the end of the BODY. * + * @todo Consider reserving this. * @since 0.4.0 * @var string */ @@ -176,6 +178,15 @@ final class OD_HTML_Tag_Processor extends WP_HTML_Tag_Processor { */ private $buffered_text_replacements = array(); + /** + * Count for the number of times next_token() was called + * + * @since n.e.x.t + * @var int + * @see self::next_token() + */ + private $next_token_count = 0; + /** * Finds the next tag. * @@ -247,6 +258,7 @@ public function expects_closer( ?string $tag_name = null ): bool { */ public function next_token(): bool { $this->current_xpath = null; // Clear cache. + ++$this->next_token_count; if ( ! parent::next_token() ) { $this->open_stack_tags = array(); $this->open_stack_indices = array(); @@ -325,6 +337,18 @@ public function next_token(): bool { return true; } + /** + * Gets the number of times next_token() was called. + * + * @since n.e.x.t + * @see self::next_token() + * + * @return int Count of next_token() calls. + */ + public function get_next_token_count(): int { + return $this->next_token_count; + } + /** * Updates or creates a new attribute on the currently matched tag with the passed value. * @@ -409,11 +433,12 @@ public function seek( $bookmark_name ): bool { } /** - * Gets the seek count. + * Gets the number of times seek() was called. * * @since n.e.x.t + * @see self::seek() * - * @return int Seek count. + * @return int Count of seek() calls. */ public function get_seek_count(): int { return $this->seek_count; diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 26a40532db..679e10fc3a 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -176,16 +176,17 @@ function od_optimize_template_output_buffer( string $buffer ): string { $visitors = iterator_to_array( $tag_visitor_registry ); while ( $processor->next_open_tag() ) { $did_visit = false; - $processor->set_bookmark( $current_tag_bookmark ); + $processor->set_bookmark( $current_tag_bookmark ); // TODO: Should we break if this returns false? foreach ( $visitors as $visitor ) { - $seek_count = $processor->get_seek_count(); - $did_visit = $visitor( $tag_visitor_context ) || $did_visit; + $seek_count = $processor->get_seek_count(); + $next_token_count = $processor->get_next_token_count(); + $did_visit = $visitor( $tag_visitor_context ) || $did_visit; // If the visitor traversed HTML tags, we need to go back to this tag so that in the next iteration any // relevant tag visitors may apply, in addition to properly setting the data-od-xpath on this tag below. - if ( $seek_count !== $processor->get_seek_count() ) { - $processor->seek( $current_tag_bookmark ); + if ( $seek_count !== $processor->get_seek_count() || $next_token_count !== $processor->get_next_token_count() ) { + $processor->seek( $current_tag_bookmark ); // TODO: Should this break out of the optimization loop if it returns false? } } $processor->release_bookmark( $current_tag_bookmark ); diff --git a/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php b/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php index df637544e7..c1c3da3720 100644 --- a/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php +++ b/plugins/optimization-detective/tests/test-class-od-html-tag-processor.php @@ -494,7 +494,6 @@ public function test_html_tag_processor_wrapper_methods(): void { /** * Test bookmarking and seeking. * - * @covers ::get_seek_count * @covers ::set_bookmark * @covers ::seek * @covers ::release_bookmark @@ -605,6 +604,38 @@ public function test_bookmarking_and_seeking(): void { // TODO: Try adding too many bookmarks. } + /** + * Test get_seek_count. + * + * @covers ::get_seek_count + */ + public function test_get_next_token_count(): void { + $processor = new OD_HTML_Tag_Processor( + trim( + ' + + + + + ' + ) + ); + $this->assertSame( 0, $processor->get_next_token_count() ); + $this->assertTrue( $processor->next_tag() ); + $this->assertSame( 'HTML', $processor->get_tag() ); + $this->assertSame( 1, $processor->get_next_token_count() ); + $this->assertTrue( $processor->next_tag() ); + $this->assertSame( 'HEAD', $processor->get_tag() ); + $this->assertSame( 3, $processor->get_next_token_count() ); // Note that next_token() call #2 was for the whitespace between and . + $this->assertTrue( $processor->next_tag() ); + $this->assertSame( 'HEAD', $processor->get_tag() ); + $this->assertTrue( $processor->is_tag_closer() ); + $this->assertSame( 4, $processor->get_next_token_count() ); + $this->assertTrue( $processor->next_tag() ); + $this->assertSame( 'BODY', $processor->get_tag() ); + $this->assertSame( 6, $processor->get_next_token_count() ); // Note that next_token() call #5 was for the whitespace between and . + } + /** * Export an array as a PHP literal to use as a snapshot. * From ca2a99b51cb1cdd8a72fd77400ab29e900536bf1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 15:18:02 -0700 Subject: [PATCH 7/8] Update attribute order in Image Prioritizer test after eliminating excessive seek() calls --- plugins/image-prioritizer/tests/test-helper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 551e6faabe..084ce77dcc 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -48,7 +48,7 @@ public function data_provider_test_filter_tag_visitors(): array { ... - Foo + Foo @@ -340,8 +340,8 @@ public function data_provider_test_filter_tag_visitors(): array { - Foo - Bar + Foo + Bar From 1bb8bbc90f8f9ff3724e6a96d13518b6a3e7fd73 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 18 Jul 2024 16:14:02 -0700 Subject: [PATCH 8/8] Prepare 0.4.1 release of Optimization Detective --- .../class-od-html-tag-processor.php | 6 +++--- plugins/optimization-detective/load.php | 4 ++-- plugins/optimization-detective/readme.txt | 12 +++++++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index 46b1f25750..e6b4da6184 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -181,7 +181,7 @@ final class OD_HTML_Tag_Processor extends WP_HTML_Tag_Processor { /** * Count for the number of times next_token() was called * - * @since n.e.x.t + * @since 0.4.1 * @var int * @see self::next_token() */ @@ -340,7 +340,7 @@ public function next_token(): bool { /** * Gets the number of times next_token() was called. * - * @since n.e.x.t + * @since 0.4.1 * @see self::next_token() * * @return int Count of next_token() calls. @@ -435,7 +435,7 @@ public function seek( $bookmark_name ): bool { /** * Gets the number of times seek() was called. * - * @since n.e.x.t + * @since 0.4.1 * @see self::seek() * * @return int Count of seek() calls. diff --git a/plugins/optimization-detective/load.php b/plugins/optimization-detective/load.php index 197e136bdf..a477263e04 100644 --- a/plugins/optimization-detective/load.php +++ b/plugins/optimization-detective/load.php @@ -5,7 +5,7 @@ * Description: Provides an API for leveraging real user metrics to detect optimizations to apply on the frontend to improve page performance. * Requires at least: 6.5 * Requires PHP: 7.2 - * Version: 0.4.0 + * Version: 0.4.1 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -65,7 +65,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'optimization_detective_pending_plugin', - '0.4.0', + '0.4.1', static function ( string $version ): void { // Define the constant. diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 150940fd89..5bf5a01c9c 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.6 -Stable tag: 0.4.0 +Stable tag: 0.4.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, rum @@ -133,6 +133,16 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.4.1 = + +**Enhancements** + +* Upgrade web-vitals.js from [v3.5.0](https://github.com/GoogleChrome/web-vitals/blob/main/CHANGELOG.md#v350-2023-09-28) to [v4.2.1](https://github.com/GoogleChrome/web-vitals/blob/main/CHANGELOG.md#v422-2024-07-17). + +**Bug Fixes** + +* Fix logic for seeking during optimization loop to prevent emitting seek() notices. ([1376](https://github.com/WordPress/performance/pull/1376)) + = 0.4.0 = **Enhancements**