Make WordPress Core

Opened 6 weeks ago

Closed 6 weeks ago

#64846 closed defect (bug) (fixed)

Loading separate styles on demand thwarted by plugins that register styles at 'init' with priority less than 8

Reported by: westonruter's profile westonruter Owned by: westonruter's profile westonruter
Milestone: 7.0 Priority: normal
Severity: normal Version: 6.9
Component: Script Loader Keywords: has-patch has-unit-tests needs-testing
Focuses: css, performance Cc:

Description (last modified by westonruter)

This is a follow-up to #64099.

With Elementor patched to re-enable loading block styles on demand, I was confused to find that the single block library stylesheet was being loaded in spite of the output buffer being started and other styles being hoisted as expected. With Elementor deactivated, I would see on the frontend as expected the non-combined block-library common.css loaded:

<style id="wp-block-library-inline-css">
...

/*# sourceURL=/wp-includes/css/dist/block-library/common.css */
</style>

But with Elementor active, I see:

<link rel='stylesheet' id='wp-block-library-css' href='http://localhost:8000/wp-includes/css/dist/block-library/style.css?ver=7.0-beta4-61919-src' media='all' />

What's more is that I see the separate block styles also enqueued on top of the combined wp-block-library stylesheet:

<link rel='stylesheet' id='wp-block-library-css' href='http://localhost:8000/wp-includes/css/dist/block-library/style.css?ver=7.0-beta4-61919-src' media='all' />

<style id="wp-block-archives-inline-css">
.wp-block-archives {
  box-sizing: border-box;
}

.wp-block-archives-dropdown label {
  display: block;
}
/*# sourceURL=http://localhost:8000/wp-includes/blocks/archives/style.css */
</style>

The issue is that the elementor plugin initializes at the init action with priority 0 (source) which causes the default styles to be registered earlier than normal. The Elementor\Core\Page_Assets\Loader component which does wp_register_style():

#0 /var/www/src/wp-includes/script-loader.php(1829): WP_Dependencies->add()
#1 /var/www/src/wp-includes/class-wp-hook.php(347): wp_default_styles()
#2 /var/www/src/wp-includes/class-wp-hook.php(371): WP_Hook->apply_filters()
#3 /var/www/src/wp-includes/plugin.php(570): WP_Hook->do_action()
#4 /var/www/src/wp-includes/class-wp-styles.php(121): do_action_ref_array()
#5 /var/www/src/wp-includes/functions.wp-styles.php(24): WP_Styles->__construct()
#6 /var/www/src/wp-includes/functions.wp-styles.php(132): wp_styles()
#7 /var/www/src/wp-content/plugins/elementor/core/page-assets/loader.php(172): wp_register_style()
#8 /var/www/src/wp-content/plugins/elementor/core/page-assets/loader.php(181): Elementor\Core\Page_Assets\Loader->register_assets()
#9 /var/www/src/wp-content/plugins/elementor/includes/plugin.php(711): Elementor\Core\Page_Assets\Loader->__construct()
#10 /var/www/src/wp-content/plugins/elementor/includes/plugin.php(622): Elementor\Plugin->init_components()
#11 /var/www/src/wp-includes/class-wp-hook.php(347): Elementor\Plugin->init()
#12 /var/www/src/wp-includes/class-wp-hook.php(371): WP_Hook->apply_filters()
#13 /var/www/src/wp-includes/plugin.php(522): WP_Hook->do_action()

At this point, when wp_default_styles() is called, this logic is run to register the wp-block-library style:

<?php
$path   = "/wp-includes/css/dist/$package/style$suffix.css";

if ( 'block-library' === $package && wp_should_load_separate_core_block_assets() ) {
        $path = "/wp-includes/css/dist/$package/common$suffix.css";
}

The issue is that wp_should_load_separate_core_block_assets() here returns false because wp_load_classic_theme_block_styles_on_demand() has run yet, since it runs later in the init action at priority 8:

add_action( 'init', 'wp_load_classic_theme_block_styles_on_demand', 8 ); // Must happen before register_core_block_style_handles() at priority 9.

This function is responsible for adding the adding the filters to opt-in to loading separate block styles on demand:

add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );

If the init priority of wp_load_classic_theme_block_styles_on_demand() is changed to -1 then this fixes the problem, but this is not the ideal solution since a theme/plugin could always register a style earlier during the init action (e.g. PHP_INT_MIN). Note that the init action is the earliest point to register a style safely since _wp_scripts_maybe_doing_it_wrong() will issue a warning otherwise. Nevertheless, the better solution would be to not use the init action at all, but rather to use the wp_default_styles action (with a low priority) so that we'll be better guaranteed that the filters will have been added.

Note: There are no relevant occurrences of wp_load_classic_theme_block_styles_on_demand() in indexed plugins or themes:

So there is not a concern for back-compat for unhooking the function at init.

Reproduction

Activate this plugin:

<?php
<?php
/**
 * Plugin Name: Register test style at early init.
 * Plugin URI: https://core.trac.wordpress.org/ticket/64846
 */

add_action(
        'init',
        function () {
                wp_register_style( 'test-early-init', false );
                wp_add_inline_style( 'test-early-init', '/* With a classic theme active, make sure wp-block-library is common.css and not style.css */' );
                wp_enqueue_style( 'test-early-init' );
        },
        0
);

This will reproduce the aforementioned issue observed with Elementor.

Change History (6)

#1 @westonruter
6 weeks ago

  • Focuses css added

#2 @westonruter
6 weeks ago

  • Description modified (diff)

#3 @westonruter
6 weeks ago

  • Description modified (diff)

This ticket was mentioned in PR #11232 on WordPress/wordpress-develop by @westonruter.


6 weeks ago
#4

  • Keywords has-patch has-unit-tests added

Trac ticket: https://core.trac.wordpress.org/ticket/64846

The wp_load_classic_theme_block_styles_on_demand() function was added to run at init action with priority 8 in r61008. This turns out to be too late since styles can be registered earlier. Registering a style earlier causes wp_default_styles() to be called, which registers the wp-block-library style. When a classic theme is active, before wp_load_classic_theme_block_styles_on_demand() is run, the wp_should_load_block_assets_on_demand() function will return false. This results in the combined block library stylesheet being incorrectly registered:

https://github.com/WordPress/wordpress-develop/blob/2bb252ae83bc78852c0d2e11749d640ac8b6bc8a/src/wp-includes/script-loader.php#L1819-L1823

The point of wp_load_classic_theme_block_styles_on_demand() is to opt-in to loading separate block styles on demand, but at the point it runs it is already too late since the combined wp-block-library has already been registered.

If the init priority of wp_load_classic_theme_block_styles_on_demand() is changed to -1 then this fixes the problem, but this is not the ideal solution since a theme/plugin could always register a style earlier during the init action (e.g. PHP_INT_MIN). Note that the init action is the earliest point to register a style safely since _wp_scripts_maybe_doing_it_wrong() will issue a warning otherwise. Nevertheless, the better solution would be to not use the init action at all, but rather to use the wp_default_styles action (with a low priority) so that we'll be better guaranteed that the filters will have been added.

The issue can be reproduced with the following example plugin:

<?php
/**
 * Plugin Name: Register test style at early init.
 * Plugin URI: https://core.trac.wordpress.org/ticket/64846
 */

add_action(
        'init',
        function () {
                wp_register_style( 'test-early-init', false );
                wp_add_inline_style( 'test-early-init', '/* With a classic theme active, make sure wp-block-library is common.css and not style.css */' );
                wp_enqueue_style( 'test-early-init' );
        },
        0
);

### Before ❌

<link rel='stylesheet' id='wp-block-library-css' href='http://localhost:8000/wp-includes/css/dist/block-library/style.css?ver=7.0-beta4-61919-src' media='all' />

<style id="wp-block-accordion-inline-css">
.wp-block-accordion {
  box-sizing: border-box;
}
/*# sourceURL=http://localhost:8000/wp-includes/blocks/accordion/style.css */
</style>

### After ✅

<style id="wp-block-library-inline-css">
/**
 * Colors
 */
...

/*# sourceURL=/wp-includes/css/dist/block-library/common.css */
</style>
<style id="wp-block-archives-inline-css">
.wp-block-archives {
  box-sizing: border-box;
}

.wp-block-archives-dropdown label {
  display: block;
}
/*# sourceURL=http://localhost:8000/wp-includes/blocks/archives/style.css */
</style>

## Use of AI Tools

None 🧠

#5 @westonruter
6 weeks ago

  • Description modified (diff)
  • Keywords needs-testing added

#6 @westonruter
6 weeks ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 61981:

Script Loader: Move wp_load_classic_theme_block_styles_on_demand() from init to wp_default_styles.

This ensures the filters to opt in to loading separate block styles on demand are added at the moment WP_Styles is constructed. This accounts for styles being registered at the init action before register_core_block_style_handles() runs at priority 9. Without this, the wp-block-library stylesheet may get registered with the full combined block styles as style.css instead of just common.css, due to wp_should_load_block_assets_on_demand() still returning false. The wp_default_styles action still runs during init.

Developed in https://github.com/WordPress/wordpress-develop/pull/11232

Follow-up to r61008.

Props westonruter, adamsilverstein.
See #64099.
Fixes #64846.

Note: See TracTickets for help on using tickets.