Skip to content

Commit a248246

Browse files
authored
Merge branch 'trunk' into add/code_coverage_auto_sizes
2 parents 6038a46 + e467655 commit a248246

25 files changed

+673
-138
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"devDependencies": {
1414
"@octokit/rest": "^21.1.0",
15-
"@wordpress/env": "^10.17.0",
15+
"@wordpress/env": "^10.18.0",
1616
"@wordpress/prettier-config": "^4.18.0",
1717
"@wordpress/scripts": "^30.10.0",
1818
"commander": "13.1.0",

plugins/optimization-detective/class-od-url-metric-group-collection.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public function get_freshness_ttl(): int {
233233
}
234234

235235
/**
236-
* Gets the first URL Metric group.
236+
* Gets the first URL Metric group (with the lowest minimum viewport width, e.g. for mobile).
237237
*
238238
* This group normally represents viewports for mobile devices. This group always has a minimum viewport width of 0
239239
* and the maximum viewport width corresponds to the smallest defined breakpoint returned by
@@ -248,7 +248,7 @@ public function get_first_group(): OD_URL_Metric_Group {
248248
}
249249

250250
/**
251-
* Gets the last URL Metric group.
251+
* Gets the last URL Metric group (with the highest minimum viewport width, e.g. for desktop).
252252
*
253253
* This group normally represents viewports for desktop devices. This group always has a minimum viewport width
254254
* defined as one greater than the largest breakpoint returned by {@see od_get_breakpoint_max_widths()}.
@@ -266,6 +266,7 @@ public function get_last_group(): OD_URL_Metric_Group {
266266
* Clears result cache.
267267
*
268268
* @since 0.3.0
269+
* @access private
269270
*/
270271
public function clear_cache(): void {
271272
$this->result_cache = array();
@@ -298,6 +299,7 @@ private function create_groups(): array {
298299
*
299300
* @since 0.1.0
300301
* @throws InvalidArgumentException If there is no group available to add a URL Metric to.
302+
* @access private
301303
*
302304
* @param OD_URL_Metric $new_url_metric New URL Metric.
303305
*/
@@ -317,7 +319,7 @@ public function add_url_metric( OD_URL_Metric $new_url_metric ): void {
317319
}
318320

319321
/**
320-
* Gets group for viewport width.
322+
* Gets the group for the provided viewport width.
321323
*
322324
* @since 0.1.0
323325
* @throws InvalidArgumentException When there is no group for the provided viewport width. This would only happen if a negative width is provided.
@@ -411,7 +413,11 @@ public function is_every_group_populated(): bool {
411413
}
412414

413415
/**
414-
* Checks whether every group is complete.
416+
* Checks whether every group is complete (full sample of non-stale URL Metrics).
417+
*
418+
* Completeness means the full sample size of URL Metrics has been collected,
419+
* none of the collected URL Metrics are stale (with a mismatching ETag or a
420+
* timestamp older than the freshness TTL).
415421
*
416422
* @since 0.1.0
417423
* @see OD_URL_Metric_Group::is_complete()
@@ -438,7 +444,7 @@ public function is_every_group_complete(): bool {
438444
}
439445

440446
/**
441-
* Gets the groups with the provided LCP element XPath.
447+
* Gets the groups which have an LCP element with the provided XPath.
442448
*
443449
* @since 0.3.0
444450
* @see OD_URL_Metric_Group::get_lcp_element()
@@ -468,7 +474,7 @@ public function get_groups_by_lcp_element( string $xpath ): array {
468474
}
469475

470476
/**
471-
* Gets common LCP element.
477+
* Gets the LCP element which is shared by all groups, or at least the first group (mobile) and last group (desktop) if the intermediary groups are not populated.
472478
*
473479
* @since 0.3.0
474480
* @since 0.9.0 An LCP element is also considered common if it is the same in the narrowest and widest viewport groups, and all intermediate groups are empty.
@@ -585,7 +591,7 @@ public function get_all_element_max_intersection_ratios(): array {
585591
}
586592

587593
/**
588-
* Gets all elements' status for whether they are positioned in any initial viewport.
594+
* Gets the status for whether each element is positioned in any initial viewport.
589595
*
590596
* An element is positioned in the initial viewport if its `boundingClientRect.top` is less than the
591597
* `viewport.height` for any of its recorded URL Metrics. Note that even though the element may be positioned in the

plugins/optimization-detective/class-od-url-metric-group.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ public function is_viewport_width_in_range( int $viewport_width ): bool {
242242
* Adds a URL Metric to the group.
243243
*
244244
* @since 0.1.0
245+
* @access private
245246
*
246247
* @throws InvalidArgumentException If the viewport width of the URL Metric is not within the min/max bounds of the group.
247248
*
@@ -279,7 +280,8 @@ static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int {
279280
* Determines whether the URL Metric group is complete.
280281
*
281282
* A group is complete if it has the full sample size of URL Metrics
282-
* and all of these URL Metrics are fresh.
283+
* and all of these URL Metrics are fresh (with a current ETag and a
284+
* timestamp that is not older than the freshness TTL).
283285
*
284286
* @since 0.1.0
285287
* @since 0.9.0 If the current environment's generated ETag does not match the URL Metric's ETag, the URL Metric is considered stale.
@@ -486,6 +488,7 @@ public function count(): int {
486488
* Clears result cache.
487489
*
488490
* @since 0.9.0
491+
* @access private
489492
*/
490493
public function clear_cache(): void {
491494
$this->result_cache = array();

plugins/optimization-detective/class-od-url-metric.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ private function prepare_data( array $data ): array {
145145
}
146146

147147
/**
148-
* Gets the group that this URL Metric is a part of (which may not be any).
148+
* Gets the group that this URL Metric is a part of.
149149
*
150150
* @since 0.7.0
151151
*
152-
* @return OD_URL_Metric_Group|null Group.
152+
* @return OD_URL_Metric_Group|null Group. Null will never occur in the context of a tag visitor.
153153
*/
154154
public function get_group(): ?OD_URL_Metric_Group {
155155
return $this->group;
@@ -159,6 +159,7 @@ public function get_group(): ?OD_URL_Metric_Group {
159159
* Sets the group that this URL Metric is a part of.
160160
*
161161
* @since 0.7.0
162+
* @access private
162163
*
163164
* @param OD_URL_Metric_Group $group Group.
164165
*
@@ -177,6 +178,7 @@ public function set_group( OD_URL_Metric_Group $group ): void {
177178
* @since 0.1.0
178179
* @since 0.9.0 Added the 'etag' property to the schema.
179180
* @since 1.0.0 The 'etag' property is now required.
181+
* @access private
180182
*
181183
* @todo Cache the return value?
182184
*

plugins/optimization-detective/docs/extensions.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@ As mentioned above, this plugin is a dependency that doesn't provide features on
3535

3636
## Extension Plugins
3737

38-
For production, from the WordPress.org Plugin Directory:
38+
Stable plugins for use in production from the WordPress.org Plugin Directory:
3939

4040
* [Image Prioritizer](https://wordpress.org/plugins/image-prioritizer/): Prioritizes the loading of images and videos based on how visible they are to actual visitors; adds fetchpriority and applies lazy loading.
4141
* [Embed Optimizer](https://wordpress.org/plugins/embed-optimizer/): Optimizes the performance of embeds through lazy-loading, preconnecting, and reserving space to reduce layout shifts.
4242

43-
For development and debugging, from repositories on GitHub:
43+
Experimental plugins being explored exploration, from repositories on GitHub:
44+
45+
* [Optimization Detective Content Visibility](https://github.com/westonruter/od-content-visibility): Applies content-visibility to posts in The Loop to improve rendering performance.
46+
* [Optimization Detective Intrinsic Dimensions](https://github.com/westonruter/od-intrinsic-dimensions): Supplies width and height attributes to IMG and VIDEO tags that lack them according to their intrinsic dimensions. This reduces Cumulative Layout Shift (CLS).
47+
48+
For development and debugging, also on GitHub:
4449

4550
* [Optimization Detective Admin UI](https://github.com/westonruter/od-admin-ui): Provides an admin UI to inspect URL Metrics from the Optimization Detective plugin.
51+
* [Optimization Detective Debug Helper](https://github.com/swissspidy/od-debug-helper/): Makes data from Optimization Detective visible on the front end through the admin bar.
4652
* [Optimization Detective Store Query Vars](https://github.com/westonruter/od-store-query-vars): Stores the Query Vars with a URL Metric in the Optimization Detective plugin. This is useful for debugging URL Metrics, in particular what the slug was computed from.
4753
* [Optimization Detective Store User Agent](https://github.com/westonruter/od-store-user-agent): Stores the User Agent with a URL Metric in the Optimization Detective plugin. This is useful for debugging URL Metrics, in particular to understand what device has a given viewport dimensions.
4854
* [Optimization Detective Dev Mode](https://github.com/westonruter/od-dev-mode): Adds filters to facilitate development of the Optimization Detective plugin.
49-
* [Optimization Detective Content Visibility](https://github.com/westonruter/od-content-visibility): Applies content-visibility to posts in The Loop to improve rendering performance.
50-
* [Optimization Detective Intrinsic Dimensions](https://github.com/westonruter/od-intrinsic-dimensions): Supplies width and height attributes to IMG and VIDEO tags that lack them according to their intrinsic dimensions. This reduces Cumulative Layout Shift (CLS).

plugins/optimization-detective/docs/hooks.md

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,104 @@
66

77
### Action: `od_init` (argument: plugin version)
88

9-
Fires when the Optimization Detective is initializing. This action is useful for loading extension code that depends on Optimization Detective to be running. The version of the plugin is passed as the sole argument so that if the required version is not present, the callback can short circuit.
9+
Fires when the Optimization Detective is initializing.
10+
11+
This action is useful for loading extension code that depends on Optimization Detective to be running. The version
12+
of the plugin is passed as the sole argument so that if the required version is not present, the callback can short circuit.
13+
14+
Example:
15+
16+
```php
17+
add_action( 'od_init', function ( string $version ) {
18+
if ( version_compare( $version, '1.0', '<' ) ) {
19+
add_action( 'admin_notices', 'my_plugin_warn_optimization_plugin_outdated' );
20+
return;
21+
}
22+
23+
// Bootstrap the Optimization Detective extension.
24+
require_once __DIR__ . '/functions.php';
25+
// ...
26+
} );
27+
```
1028

1129
### Action: `od_register_tag_visitors` (argument: `OD_Tag_Visitor_Registry`)
1230

1331
Fires to register tag visitors before walking over the document to perform optimizations.
1432

15-
For example, to register a new tag visitor that targets `H1` elements:
33+
Once a page has finished rendering and the output buffer is processed, the page contents are loaded into
34+
an HTML Tag Processor instance. It then iterates over each tag in the document, and at each open tag it will
35+
invoke all registered tag visitors. A tag visitor is simply a callable (such as a regular function, closure,
36+
or even a class with an `__invoke` method defined). The tag visitor callback is invoked by passing an instance
37+
of the `OD_Tag_Visitor_Context` object which includes the following read-only properties:
38+
39+
- `$processor` (`OD_HTML_Tag_Processor`): The processor with the cursor at the current open tag.
40+
- `$url_metric_group_collection` (`OD_URL_Metric_Group_Collection`): The URL Metrics which may include information about the current tag to inform what optimizations the callback performs.
41+
- `$link_collection` (`OD_Link_Collection`): Collection of links which will be added to the `HEAD` when the page is served. This allows you to add preload links and preconnect links as needed.
42+
- `$url_metrics_id` (`positive-int|null`): The post ID for the `od_url_metrics` post from which the URL Metrics were loaded (if any). For advanced usage.
43+
44+
Note that you are free to call `$processor->next_tag()` in the callback (such as to walk over any child elements)
45+
since the tag processor's cursor will be reset to the tag after the callback finishes.
46+
47+
When a tag visitor sees it is at a relevant open tag (e.g. by checking `$processor->get_tag()`), it can call the
48+
`$context->track_tag()` method to indicate that the tag should be measured during detection. This will cause the
49+
tag to be included among the `elements` in the stored URL Metrics. The element data includes properties such
50+
as `intersectionRatio`, `intersectionRect`, and `boundingClientRect` (provided by an `IntersectionObserver`) as
51+
well as whether the tag is the LCP element (`isLCP`) or LCP element candidate (`isLCPCandidate`). This method
52+
should not be called if the current tag is not relevant for the tag visitor or if the tag visitor callback does
53+
not need to query the provided `OD_URL_Metric_Group_Collection` instance to apply the desired optimizations. (In
54+
addition to calling the `$context->track_tag()`, a callback may also return `true` to indicate the tag should be
55+
tracked.)
56+
57+
Here's an example tag visitor that depends on URL Metrics data:
1658

1759
```php
18-
add_action(
19-
'od_register_tag_visitors',
20-
static function ( OD_Tag_Visitor_Registry $registry ) {
21-
$registry->register(
22-
'my-plugin/h1',
23-
static function ( OD_Tag_Visitor_Context $context ): bool {
24-
if ( $context->processor->get_tag() !== 'H1' ) {
25-
return false;
26-
}
27-
// Now optimize based on stored URL Metrics in $context->url_metric_group_collection.
28-
// ...
29-
30-
// Returning true causes the tag to be tracked in URL Metrics. If there is no need
31-
// for this, as in there is no reference to $context->url_metric_group_collection
32-
// in a tag visitor, then this can instead return false.
33-
return true;
34-
}
35-
);
60+
$tag_visitor_registry->register(
61+
'lcp-img-fetchpriority-high',
62+
static function ( OD_Tag_Visitor_Context $context ): void {
63+
if ( $context->processor->get_tag() !== 'IMG' ) {
64+
return; // Tag is not relevant for this tag visitor.
65+
}
66+
67+
// Mark the tag for measurement during detection so it is included among the elements stored in URL Metrics.
68+
$context->track_tag();
69+
70+
// Make sure fetchpriority=high is added to LCP IMG elements based on the captured URL Metrics.
71+
$common_lcp_element = $context->url_metric_group_collection->get_common_lcp_element();
72+
if (
73+
null !== $common_lcp_element
74+
&&
75+
$common_lcp_element->get_xpath() === $context->processor->get_xpath()
76+
) {
77+
$context->processor->set_attribute( 'fetchpriority', 'high' );
78+
}
79+
}
80+
);
81+
````
82+
83+
Please note this implementation of setting `fetchpriority=high` on the LCP `IMG` element is simplified. Please
84+
see the Image Prioritizer extension for a more robust implementation.
85+
86+
Here's an example tag visitor that does not depend on any URL Metrics data:
87+
88+
```php
89+
$tag_visitor_registry->register(
90+
'img-decoding-async',
91+
static function ( OD_Tag_Visitor_Context $context ): bool {
92+
if ( $context->processor->get_tag() !== 'IMG' ) {
93+
return; // Tag is not relevant for this tag visitor.
94+
}
95+
96+
// Set the decoding attribute if it is absent.
97+
if ( null === $context->processor->get_attribute( 'decoding' ) ) {
98+
$context->processor->set_attribute( 'decoding', 'async' );
99+
}
36100
}
37101
);
38102
```
39103

40-
Refer to [Image Prioritizer](https://github.com/WordPress/performance/tree/trunk/plugins/image-prioritizer) and [Embed Optimizer](https://github.com/WordPress/performance/tree/trunk/plugins/embed-optimizer) for real world examples of how tag visitors are used. Registered tag visitors need only be callables, so in addition to providing a closure you may provide a `callable-string` or even a class which has an `__invoke()` method.
104+
Refer to [Image Prioritizer](https://github.com/WordPress/performance/tree/trunk/plugins/image-prioritizer) and
105+
[Embed Optimizer](https://github.com/WordPress/performance/tree/trunk/plugins/embed-optimizer) for additional
106+
examples of how tag visitors are used.
41107

42108
### Action: `od_url_metric_stored` (argument: `OD_URL_Metric_Store_Request_Context`)
43109

115 KB
Loading
68.8 KB
Loading
95.9 KB
Loading

0 commit comments

Comments
 (0)