@@ -2264,10 +2264,14 @@ function wp_print_head_scripts() {
22642264/**
22652265 * Private, for use in *_footer_scripts hooks
22662266 *
2267- * In classic themes, when block styles are loaded on demand via {@see wp_load_classic_theme_block_styles_on_demand()},
2268- * this function is replaced by a closure in {@see wp_hoist_late_printed_styles()} which will capture the output of
2269- * {@see print_late_styles()} before printing footer scripts as usual. The captured late-printed styles are then hoisted
2270- * to the HEAD by means of the template enhancement output buffer.
2267+ * In classic themes, when block styles are loaded on demand via wp_load_classic_theme_block_styles_on_demand(),
2268+ * this function is replaced by a closure in wp_hoist_late_printed_styles() which will capture the printing of
2269+ * two sets of "late" styles to be hoisted to the HEAD by means of the template enhancement output buffer:
2270+ *
2271+ * 1. Styles related to blocks are inserted right after the wp-block-library stylesheet.
2272+ * 2. All other styles are appended to the end of the HEAD.
2273+ *
2274+ * The closure calls print_footer_scripts() to print scripts in the footer as usual.
22712275 *
22722276 * @since 3.3.0
22732277 */
@@ -3601,20 +3605,23 @@ function wp_load_classic_theme_block_styles_on_demand() {
36013605 // The following two filters are added by default for block themes in _add_default_theme_supports().
36023606
36033607 /*
3604- * Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally,
3605- * and so that block-specific styles will only be enqueued when they are used on the page.
3606- * A priority of zero allows for this to be easily overridden by themes which wish to opt out.
3608+ * Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally, and so
3609+ * that block-specific styles will only be enqueued when they are used on the page. A priority of zero allows for
3610+ * this to be easily overridden by themes which wish to opt out. If a site has explicitly opted out of loading
3611+ * separate block styles, then abort.
36073612 */
36083613 add_filter ( 'should_load_separate_core_block_assets ' , '__return_true ' , 0 );
3614+ if ( ! wp_should_load_separate_core_block_assets () ) {
3615+ return ;
3616+ }
36093617
36103618 /*
36113619 * Also ensure that block assets are loaded on demand (although the default value is from should_load_separate_core_block_assets).
3612- * As above, a priority of zero allows for this to be easily overridden by themes which wish to opt out.
3620+ * As above, a priority of zero allows for this to be easily overridden by themes which wish to opt out. If a site
3621+ * has explicitly opted out of loading block styles on demand, then abort.
36133622 */
36143623 add_filter ( 'should_load_block_assets_on_demand ' , '__return_true ' , 0 );
3615-
3616- // If a site has explicitly opted out of loading block styles on demand via filters with priorities higher than above, then abort.
3617- if ( ! wp_should_load_separate_core_block_assets () || ! wp_should_load_block_assets_on_demand () ) {
3624+ if ( ! wp_should_load_block_assets_on_demand () ) {
36183625 return ;
36193626 }
36203627
@@ -3637,37 +3644,73 @@ function wp_hoist_late_printed_styles() {
36373644 }
36383645
36393646 /*
3640- * While normally late styles are printed, there is a filter to disable prevent this, so this makes sure they are
3641- * printed. Note that this filter was intended to control whether to print the styles queued too late for the HTML
3642- * head. This filter was introduced in <https://core.trac.wordpress.org/ticket/9346>. However, with the template
3643- * enhancement output buffer, essentially no style can be enqueued too late, because an output buffer filter can
3644- * always hoist it to the HEAD.
3647+ * Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles
3648+ * can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement
3649+ * output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at
3650+ * `enqueue_block_assets` or `wp_enqueue_scripts` that the placeholder will be sure to be present.
36453651 */
3646- add_filter ( 'print_late_styles ' , '__return_true ' , PHP_INT_MAX );
3652+ $ placeholder = sprintf ( '/*%s*/ ' , uniqid ( 'wp_block_styles_on_demand_placeholder: ' ) );
3653+ add_action (
3654+ 'wp_print_styles ' ,
3655+ static function () use ( $ placeholder ) {
3656+ wp_add_inline_style ( 'wp-block-library ' , $ placeholder );
3657+ }
3658+ );
36473659
36483660 /*
3649- * Print a placeholder comment where the late styles can be hoisted from the footer to be printed in the header
3650- * by means of a filter below on the template enhancement output buffer.
3661+ * Create a substitute for `print_late_styles()` which is aware of block styles. This substitute does not print
3662+ * the styles, but it captures what would be printed for block styles and non-block styles so that they can be
3663+ * later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts`
3664+ * before `print_footer_scripts()` is called.
36513665 */
3652- $ placeholder = sprintf ( '/*%s*/ ' , uniqid ( 'wp_late_styles_placeholder: ' ) );
3666+ $ printed_block_styles = '' ;
3667+ $ printed_late_styles = '' ;
3668+ $ capture_late_styles = static function () use ( &$ printed_block_styles , &$ printed_late_styles ) {
3669+ // Gather the styles related to on-demand block enqueues.
3670+ $ all_block_style_handles = array ();
3671+ foreach ( WP_Block_Type_Registry::get_instance ()->get_all_registered () as $ block_type ) {
3672+ foreach ( $ block_type ->style_handles as $ style_handle ) {
3673+ $ all_block_style_handles [] = $ style_handle ;
3674+ }
3675+ }
3676+ $ all_block_style_handles = array_merge (
3677+ $ all_block_style_handles ,
3678+ array (
3679+ 'global-styles ' ,
3680+ 'block-style-variation-styles ' ,
3681+ 'core-block-supports ' ,
3682+ 'core-block-supports-duotone ' ,
3683+ )
3684+ );
36533685
3654- wp_add_inline_style ( 'wp-block-library ' , $ placeholder );
3686+ /*
3687+ * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet
3688+ * to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`.
3689+ */
3690+ $ enqueued_block_styles = array_values ( array_intersect ( $ all_block_style_handles , wp_styles ()->queue ) );
3691+ if ( count ( $ enqueued_block_styles ) > 0 ) {
3692+ ob_start ();
3693+ wp_styles ()->do_items ( $ enqueued_block_styles );
3694+ $ printed_block_styles = ob_get_clean ();
3695+ }
36553696
3656- // Wrap print_late_styles() with a closure that captures the late-printed styles.
3657- $ printed_late_styles = '' ;
3658- $ capture_late_styles = static function () use ( &$ printed_late_styles ) {
3697+ /*
3698+ * Print all remaining styles not related to blocks. This contains a subset of the logic from
3699+ * `print_late_styles()`, without admin-specific logic and the `print_late_styles` filter to control whether
3700+ * late styles are printed (since they are being hoisted anyway).
3701+ */
36593702 ob_start ();
3660- print_late_styles ();
3703+ wp_styles ()-> do_footer_items ();
36613704 $ printed_late_styles = ob_get_clean ();
36623705 };
36633706
36643707 /*
3665- * If _wp_footer_scripts() was unhooked from the wp_print_footer_scripts action, or if wp_print_footer_scripts()
3666- * was unhooked from running at the wp_footer action, then only add a callback to wp_footer which will capture the
3708+ * If ` _wp_footer_scripts()` was unhooked from the ` wp_print_footer_scripts` action, or if ` wp_print_footer_scripts()`
3709+ * was unhooked from running at the ` wp_footer` action, then only add a callback to ` wp_footer` which will capture the
36673710 * late-printed styles.
36683711 *
3669- * Otherwise, in the normal case where _wp_footer_scripts() will run at the wp_print_footer_scripts action, then
3670- * swap out _wp_footer_scripts() with an alternative which captures the printed styles (for hoisting to HEAD) before
3712+ * Otherwise, in the normal case where ` _wp_footer_scripts()` will run at the ` wp_print_footer_scripts` action, then
3713+ * swap out ` _wp_footer_scripts()` with an alternative which captures the printed styles (for hoisting to HEAD) before
36713714 * proceeding with printing the footer scripts.
36723715 */
36733716 $ wp_print_footer_scripts_priority = has_action ( 'wp_print_footer_scripts ' , '_wp_footer_scripts ' );
@@ -3689,65 +3732,99 @@ static function () use ( $capture_late_styles ) {
36893732 // Replace placeholder with the captured late styles.
36903733 add_filter (
36913734 'wp_template_enhancement_output_buffer ' ,
3692- function ( $ buffer ) use ( $ placeholder , &$ printed_late_styles ) {
3735+ static function ( $ buffer ) use ( $ placeholder, & $ printed_block_styles , &$ printed_late_styles ) {
36933736
36943737 // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans.
36953738 $ processor = new class ( $ buffer ) extends WP_HTML_Tag_Processor {
3696- public function get_span (): WP_HTML_Span {
3697- $ instance = $ this ; // phpcs:ignore PHPCompatibility.FunctionDeclarations.NewClosure.ThisFoundOutsideClass -- It is inside an anonymous class.
3698- $ instance ->set_bookmark ( 'here ' );
3699- return $ instance ->bookmarks ['here ' ];
3739+ /**
3740+ * Gets the span for the current token.
3741+ *
3742+ * @return WP_HTML_Span Current token span.
3743+ */
3744+ private function get_span (): WP_HTML_Span {
3745+ // Note: This call will never fail according to the usage of this class, given it is always called after ::next_tag() is true.
3746+ $ this ->set_bookmark ( 'here ' );
3747+ return $ this ->bookmarks ['here ' ];
3748+ }
3749+
3750+ /**
3751+ * Inserts text before the current token.
3752+ *
3753+ * @param string $text Text to insert.
3754+ */
3755+ public function insert_before ( string $ text ) {
3756+ $ this ->lexical_updates [] = new WP_HTML_Text_Replacement ( $ this ->get_span ()->start , 0 , $ text );
3757+ }
3758+
3759+ /**
3760+ * Inserts text after the current token.
3761+ *
3762+ * @param string $text Text to insert.
3763+ */
3764+ public function insert_after ( string $ text ) {
3765+ $ span = $ this ->get_span ();
3766+
3767+ $ this ->lexical_updates [] = new WP_HTML_Text_Replacement ( $ span ->start + $ span ->length , 0 , $ text );
3768+ }
3769+
3770+ /**
3771+ * Removes the current token.
3772+ */
3773+ public function remove () {
3774+ $ span = $ this ->get_span ();
3775+
3776+ $ this ->lexical_updates [] = new WP_HTML_Text_Replacement ( $ span ->start , $ span ->length , '' );
37003777 }
37013778 };
37023779
3703- // Loop over STYLE tags.
3780+ /*
3781+ * Insert block styles right after wp-block-library (if it is present), and then insert any remaining styles
3782+ * at </head> (or else print everything there). The placeholder CSS comment will always be added to the
3783+ * wp-block-library inline style since it gets printed at `wp_head` before the blocks are rendered.
3784+ * This means that there may not actually be any block styles to hoist from the footer to insert after this
3785+ * inline style. The placeholder CSS comment needs to be added so that the inline style gets printed, but
3786+ * if the resulting inline style is empty after the placeholder is removed, then the inline style is
3787+ * removed.
3788+ */
37043789 while ( $ processor ->next_tag ( array ( 'tag_closers ' => 'visit ' ) ) ) {
3705-
3706- // We've encountered the inline style for the 'wp-block-library' stylesheet which probably has the placeholder comment.
37073790 if (
3708- ! $ processor ->is_tag_closer () &&
37093791 'STYLE ' === $ processor ->get_tag () &&
37103792 'wp-block-library-inline-css ' === $ processor ->get_attribute ( 'id ' )
37113793 ) {
3712- // If the inline style lacks the placeholder comment, then we have to continue until we get to </HEAD> to append the styles there.
37133794 $ css_text = $ processor ->get_modifiable_text ();
3714- if ( ! str_contains ( $ css_text , $ placeholder ) ) {
3715- continue ;
3716- }
37173795
3718- // Remove the placeholder now that we've located the inline style.
3719- $ processor ->set_modifiable_text ( str_replace ( $ placeholder , '' , $ css_text ) );
3720- $ buffer = $ processor ->get_updated_html ();
3796+ /*
3797+ * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
3798+ * be printed. Now that we've located the inline style, the placeholder comment can be removed. If
3799+ * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
3800+ * comment, then remove the STYLE entirely.)
3801+ */
3802+ $ css_text = str_replace ( $ placeholder , '' , $ css_text );
3803+ if ( preg_match ( ':^/\*# sourceURL=\S+? \*/$: ' , trim ( $ css_text ) ) ) {
3804+ $ processor ->remove ();
3805+ } else {
3806+ $ processor ->set_modifiable_text ( $ css_text );
3807+ }
37213808
37223809 // Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
3723- $ span = $ processor ->get_span ();
3724- $ buffer = implode (
3725- '' ,
3726- array (
3727- substr ( $ buffer , 0 , $ span ->start + $ span ->length ),
3728- $ printed_late_styles ,
3729- substr ( $ buffer , $ span ->start + $ span ->length ),
3730- )
3731- );
3732- break ;
3733- }
3810+ if ( '' !== $ printed_block_styles ) {
3811+ $ processor ->insert_after ( $ printed_block_styles );
37343812
3735- // As a fallback, append the hoisted late styles to the end of the HEAD.
3736- if ( $ processor ->is_tag_closer () && 'HEAD ' === $ processor ->get_tag () ) {
3737- $ span = $ processor ->get_span ();
3738- $ buffer = implode (
3739- '' ,
3740- array (
3741- substr ( $ buffer , 0 , $ span ->start ),
3742- $ printed_late_styles ,
3743- substr ( $ buffer , $ span ->start ),
3744- )
3745- );
3813+ // Prevent printing them again at </head>.
3814+ $ printed_block_styles = '' ;
3815+ }
3816+
3817+ // If there aren't any late styles, there's no need to continue to finding </head>.
3818+ if ( '' === $ printed_late_styles ) {
3819+ break ;
3820+ }
3821+ } elseif ( 'HEAD ' === $ processor ->get_tag () && $ processor ->is_tag_closer () ) {
3822+ $ processor ->insert_before ( $ printed_block_styles . $ printed_late_styles );
37463823 break ;
37473824 }
37483825 }
37493826
3750- return $ buffer ;
3827+ return $ processor -> get_updated_html () ;
37513828 }
37523829 );
37533830}
0 commit comments