Skip to content

Commit d14ae9c

Browse files
committed
Script Loader: Fall back to hoisting late-printed styles to end of HEAD if wp-block-library is not enqueued.
When the `wp-block-library` stylesheet is not enqueued, there will be no associated inline style present. This inline style normally contains the placeholder CSS comment for the HTML Tag Processor to identify the token after which the late-printed styles should be inserted. However, when the `wp-block-library` stylesheet is not enqueued (such as in themes which do not use blocks), or else the inline style is not printed for whatever reason, this adds a fallback to insert the late-printed styles immediately before `</head>`. This ensures that late-printed styles will always get hoisted. Developed in #10417 Follow-up to [61008]. Props westonruter, peterwilsoncc, Soean. See #64099, #43258. Fixes #64150. git-svn-id: https://develop.svn.wordpress.org/trunk@61076 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 8d24041 commit d14ae9c

File tree

2 files changed

+120
-52
lines changed

2 files changed

+120
-52
lines changed

src/wp-includes/script-loader.php

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,13 +3593,21 @@ function wp_load_classic_theme_block_styles_on_demand() {
35933593
}
35943594

35953595
/*
3596-
* Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally,
3597-
* and so that block-specific styles will only be enqueued when they are used on the page.
3596+
* If the theme supports block styles, add filters to ensure they are loaded separately and on demand. Without this,
3597+
* if a theme does not want or support block styles, then enabling these filters can result in undesired separate
3598+
* block-specific styles being enqueued, though a theme may also be trying to nullify the wp-block-library
3599+
* stylesheet.
35983600
*/
3599-
add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
3601+
if ( current_theme_supports( 'wp-block-styles' ) ) {
3602+
/*
3603+
* Load separate block styles so that the large block-library stylesheet is not enqueued unconditionally,
3604+
* and so that block-specific styles will only be enqueued when they are used on the page.
3605+
*/
3606+
add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
36003607

3601-
// Also ensure that block assets are loaded on demand (although the default value is from should_load_separate_core_block_assets).
3602-
add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
3608+
// Also ensure that block assets are loaded on demand (although the default value is from should_load_separate_core_block_assets).
3609+
add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
3610+
}
36033611

36043612
// Add hooks which require the presence of the output buffer. Ideally the above two filters could be added here, but they run too early.
36053613
add_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' );
@@ -3684,33 +3692,50 @@ public function get_span(): WP_HTML_Span {
36843692
};
36853693

36863694
// Loop over STYLE tags.
3687-
while ( $processor->next_tag( array( 'tag_name' => 'STYLE' ) ) ) {
3688-
// Skip to the next if this is not the inline style for the wp-block-library stylesheet (which contains the placeholder).
3689-
if ( 'wp-block-library-inline-css' !== $processor->get_attribute( 'id' ) ) {
3690-
continue;
3691-
}
3695+
while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
3696+
3697+
// We've encountered the inline style for the 'wp-block-library' stylesheet which probably has the placeholder comment.
3698+
if (
3699+
! $processor->is_tag_closer() &&
3700+
'STYLE' === $processor->get_tag() &&
3701+
'wp-block-library-inline-css' === $processor->get_attribute( 'id' )
3702+
) {
3703+
// If the inline style lacks the placeholder comment, then we have to continue until we get to </HEAD> to append the styles there.
3704+
$css_text = $processor->get_modifiable_text();
3705+
if ( ! str_contains( $css_text, $placeholder ) ) {
3706+
continue;
3707+
}
36923708

3693-
// If the inline style lacks the placeholder comment, then something went wrong and we need to abort.
3694-
$css_text = $processor->get_modifiable_text();
3695-
if ( ! str_contains( $css_text, $placeholder ) ) {
3709+
// Remove the placeholder now that we've located the inline style.
3710+
$processor->set_modifiable_text( str_replace( $placeholder, '', $css_text ) );
3711+
$buffer = $processor->get_updated_html();
3712+
3713+
// Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
3714+
$span = $processor->get_span();
3715+
$buffer = implode(
3716+
'',
3717+
array(
3718+
substr( $buffer, 0, $span->start + $span->length ),
3719+
$printed_late_styles,
3720+
substr( $buffer, $span->start + $span->length ),
3721+
)
3722+
);
36963723
break;
36973724
}
36983725

3699-
// Remove the placeholder now that we've located the inline style.
3700-
$processor->set_modifiable_text( str_replace( $placeholder, '', $css_text ) );
3701-
$buffer = $processor->get_updated_html();
3702-
3703-
// Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
3704-
$span = $processor->get_span();
3705-
$buffer = implode(
3706-
'',
3707-
array(
3708-
substr( $buffer, 0, $span->start + $span->length ),
3709-
$printed_late_styles,
3710-
substr( $buffer, $span->start + $span->length ),
3711-
)
3712-
);
3713-
break;
3726+
// As a fallback, append the hoisted late styles to the end of the HEAD.
3727+
if ( $processor->is_tag_closer() && 'HEAD' === $processor->get_tag() ) {
3728+
$span = $processor->get_span();
3729+
$buffer = implode(
3730+
'',
3731+
array(
3732+
substr( $buffer, 0, $span->start ),
3733+
$printed_late_styles,
3734+
substr( $buffer, $span->start ),
3735+
)
3736+
);
3737+
break;
3738+
}
37143739
}
37153740

37163741
return $buffer;

tests/phpunit/tests/template.php

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
7878
*/
7979
protected $original_wp_styles;
8080

81+
/**
82+
* @var array|null
83+
*/
84+
protected $original_theme_features;
85+
8186
public function set_up() {
8287
parent::set_up();
8388
$this->original_default_mimetype = ini_get( 'default_mimetype' );
@@ -110,13 +115,17 @@ public function set_up() {
110115
$wp_styles = null;
111116
wp_scripts();
112117
wp_styles();
118+
119+
$this->original_theme_features = $GLOBALS['_wp_theme_features'];
113120
}
114121

115122
public function tear_down() {
116123
global $wp_scripts, $wp_styles;
117124
$wp_scripts = $this->original_wp_scripts;
118125
$wp_styles = $this->original_wp_styles;
119126

127+
$GLOBALS['_wp_theme_features'] = $this->original_theme_features;
128+
120129
ini_set( 'default_mimetype', $this->original_default_mimetype );
121130
unregister_post_type( 'cpt' );
122131
unregister_taxonomy( 'taxo' );
@@ -913,46 +922,76 @@ public function test_wp_load_classic_theme_block_styles_on_demand_in_block_theme
913922
}
914923

915924
/**
916-
* Tests that wp_load_classic_theme_block_styles_on_demand() does not add hooks for classic themes when output buffering is blocked.
925+
* Data provider.
917926
*
918-
* @ticket 64099
919-
* @covers ::wp_load_classic_theme_block_styles_on_demand
927+
* @return array<string, array{theme: string, set_up: Closure|null, expected_on_demand: bool, expected_buffer_started: bool}>
920928
*/
921-
public function test_wp_load_classic_theme_block_styles_on_demand_in_classic_theme_but_output_buffering_blocked(): void {
922-
add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_false' );
923-
switch_theme( 'default' );
924-
925-
wp_load_classic_theme_block_styles_on_demand();
926-
927-
$this->assertFalse( has_filter( 'should_load_separate_core_block_assets' ), 'Expect should_load_separate_core_block_assets filter NOT to be added for block themes.' );
928-
$this->assertFalse( has_filter( 'should_load_block_assets_on_demand', '__return_true' ), 'Expect should_load_block_assets_on_demand filter NOT to be added for block themes.' );
929-
$this->assertFalse( has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ), 'Expect wp_template_enhancement_output_buffer_started action NOT to be added for block themes.' );
929+
public function data_wp_load_classic_theme_block_styles_on_demand(): array {
930+
return array(
931+
'block_theme' => array(
932+
'theme' => 'block-theme',
933+
'set_up' => static function () {},
934+
'expected_on_demand' => false,
935+
'expected_buffer_started' => false,
936+
),
937+
'classic_theme_with_output_buffer_blocked' => array(
938+
'theme' => 'default',
939+
'set_up' => static function () {
940+
add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_false' );
941+
},
942+
'expected_on_demand' => false,
943+
'expected_buffer_started' => false,
944+
),
945+
'classic_theme_with_block_styles_support' => array(
946+
'theme' => 'default',
947+
'set_up' => static function () {
948+
add_theme_support( 'wp-block-styles' );
949+
},
950+
'expected_on_demand' => true,
951+
'expected_buffer_started' => true,
952+
),
953+
'classic_theme_without_block_styles_support' => array(
954+
'theme' => 'default',
955+
'set_up' => static function () {
956+
remove_theme_support( 'wp-block-styles' );
957+
},
958+
'expected_on_demand' => false,
959+
'expected_buffer_started' => true,
960+
),
961+
);
930962
}
931963

932964
/**
933-
* Tests that wp_load_classic_theme_block_styles_on_demand() adds the expected hooks for classic themes.
965+
* Tests that wp_load_classic_theme_block_styles_on_demand() adds the expected hooks (or not).
934966
*
935967
* @ticket 64099
968+
* @ticket 64150
969+
*
936970
* @covers ::wp_load_classic_theme_block_styles_on_demand
971+
*
972+
* @dataProvider data_wp_load_classic_theme_block_styles_on_demand
937973
*/
938-
public function test_wp_load_classic_theme_block_styles_on_demand_in_classic_theme(): void {
939-
switch_theme( 'default' );
940-
974+
public function test_wp_load_classic_theme_block_styles_on_demand( string $theme, ?Closure $set_up, bool $expected_on_demand, bool $expected_buffer_started ) {
941975
$this->assertFalse( wp_should_load_separate_core_block_assets(), 'Expected wp_should_load_separate_core_block_assets() to return false initially.' );
942976
$this->assertFalse( wp_should_load_block_assets_on_demand(), 'Expected wp_should_load_block_assets_on_demand() to return true' );
943977
$this->assertFalse( has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ), 'Expected wp_template_enhancement_output_buffer_started action to be added for classic themes.' );
944978

979+
switch_theme( $theme );
980+
if ( $set_up ) {
981+
$set_up();
982+
}
983+
945984
wp_load_classic_theme_block_styles_on_demand();
946985

947-
$this->assertTrue( wp_should_load_separate_core_block_assets(), 'Expected wp_should_load_separate_core_block_assets() filters to return true' );
948-
$this->assertTrue( wp_should_load_block_assets_on_demand(), 'Expected wp_should_load_block_assets_on_demand() to return true' );
949-
$this->assertNotFalse( has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ), 'Expected wp_template_enhancement_output_buffer_started action to be added for classic themes.' );
986+
$this->assertSame( $expected_on_demand, wp_should_load_separate_core_block_assets(), 'Expected wp_should_load_separate_core_block_assets() return value.' );
987+
$this->assertSame( $expected_on_demand, wp_should_load_block_assets_on_demand(), 'Expected wp_should_load_block_assets_on_demand() return value.' );
988+
$this->assertSame( $expected_buffer_started, (bool) has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ), 'Expected wp_template_enhancement_output_buffer_started action added status.' );
950989
}
951990

952991
/**
953992
* Data provider.
954993
*
955-
* @return array<string, array{set_up?: Closure}>
994+
* @return array<string, array{set_up: Closure|null}>
956995
*/
957996
public function data_wp_hoist_late_printed_styles(): array {
958997
return array(
@@ -975,6 +1014,11 @@ public function data_wp_hoist_late_printed_styles(): array {
9751014
remove_action( 'wp_footer', 'wp_print_footer_scripts' );
9761015
},
9771016
),
1017+
'block_library_removed' => array(
1018+
'set_up' => static function () {
1019+
wp_deregister_style( 'wp-block-library' );
1020+
},
1021+
),
9781022
);
9791023
}
9801024

@@ -1006,9 +1050,6 @@ public function test_wp_hoist_late_printed_styles( ?Closure $set_up ): void {
10061050
// Simulate wp_head.
10071051
$head_output = get_echo( 'wp_head' );
10081052

1009-
$placeholder_pattern = '#/\*wp_late_styles_placeholder:[a-f0-9-]+\*/#';
1010-
1011-
$this->assertMatchesRegularExpression( $placeholder_pattern, $head_output, 'Expected the placeholder to be present' );
10121053
$this->assertStringContainsString( 'early', $head_output, 'Expected the early-enqueued stylesheet to be present.' );
10131054

10141055
// Enqueue a late style (after wp_head).
@@ -1024,7 +1065,9 @@ public function test_wp_hoist_late_printed_styles( ?Closure $set_up ): void {
10241065
// Apply the output buffer filter.
10251066
$filtered_buffer = apply_filters( 'wp_template_enhancement_output_buffer', $buffer );
10261067

1027-
$this->assertDoesNotMatchRegularExpression( $placeholder_pattern, $filtered_buffer, 'Expected the placeholder to be removed.' );
1068+
$this->assertStringContainsString( '</head>', $buffer, 'Expected the closing HEAD tag to be in the response.' );
1069+
1070+
$this->assertDoesNotMatchRegularExpression( '#/\*wp_late_styles_placeholder:[a-f0-9-]+\*/#', $filtered_buffer, 'Expected the placeholder to be removed.' );
10281071
$found_styles = array(
10291072
'HEAD' => array(),
10301073
'BODY' => array(),

0 commit comments

Comments
 (0)