From 793eaf7f19f680f468cbd1f2ec0e68a74201fdd3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Oct 2023 18:46:21 +0000 Subject: [PATCH] Script Loader: Move delayed head script to footer when there is a blocking footer dependent. This prevents a performance regression when a blocking script is enqueued in the footer which depends on a delayed script in the `head` (with `async` or `defer`). In order to preserve the execution order, a delayed dependency must fall back to blocking when there is a blocking dependent. But since it was originally delayed (and thus executes similarly to a footer script), it does not need to be in the head and can be moved to the footer. This prevents blocking the critical rendering path. Props adamsilverstein, westonruter, flixos90. Fixes #59599. See #12009. Built from https://develop.svn.wordpress.org/trunk@56933 git-svn-id: http://core.svn.wordpress.org/trunk@56444 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/class-wp-scripts.php | 38 ++++++++++++++++++++++++++++++++ wp-includes/version.php | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/wp-includes/class-wp-scripts.php b/wp-includes/class-wp-scripts.php index a6a283f953..116e98f673 100644 --- a/wp-includes/class-wp-scripts.php +++ b/wp-includes/class-wp-scripts.php @@ -231,6 +231,25 @@ class WP_Scripts extends WP_Dependencies { return true; } + /** + * Checks whether all dependents of a given handle are in the footer. + * + * If there are no dependents, this is considered the same as if all dependents were in the footer. + * + * @since 6.4.0 + * + * @param string $handle Script handle. + * @return bool Whether all dependents are in the footer. + */ + private function are_all_dependents_in_footer( $handle ) { + foreach ( $this->get_dependents( $handle ) as $dep ) { + if ( isset( $this->groups[ $dep ] ) && 0 === $this->groups[ $dep ] ) { + return false; + } + } + return true; + } + /** * Processes a script dependency. * @@ -281,6 +300,25 @@ class WP_Scripts extends WP_Dependencies { $intended_strategy = ''; } + /* + * Move this script to the footer if: + * 1. The script is in the header group. + * 2. The current output is the header. + * 3. The intended strategy is delayed. + * 4. The actual strategy is not delayed. + * 5. All dependent scripts are in the footer. + */ + if ( + 0 === $group && + 0 === $this->groups[ $handle ] && + $intended_strategy && + ! $this->is_delayed_strategy( $strategy ) && + $this->are_all_dependents_in_footer( $handle ) + ) { + $this->in_footer[] = $handle; + return false; + } + if ( $conditional ) { $cond_before = "\n"; diff --git a/wp-includes/version.php b/wp-includes/version.php index e20b819d9e..c7ceb37393 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.4-beta4-56932'; +$wp_version = '6.4-beta4-56933'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.