diff --git a/src/wp-includes/class-wp-styles.php b/src/wp-includes/class-wp-styles.php
index 32fc8878b6044..c494878943525 100644
--- a/src/wp-includes/class-wp-styles.php
+++ b/src/wp-includes/class-wp-styles.php
@@ -157,16 +157,6 @@ public function do_item( $handle, $group = false ) {
$src = $obj->src;
$inline_style = $this->print_inline_style( $handle, false );
- if ( $inline_style ) {
- $processor = new WP_HTML_Tag_Processor( '' );
- $processor->next_tag();
- $processor->set_attribute( 'id', "{$handle}-inline-css" );
- $processor->set_modifiable_text( "\n{$inline_style}\n" );
- $inline_style_tag = "{$processor->get_updated_html()}\n";
- } else {
- $inline_style_tag = '';
- }
-
if ( $this->do_concat ) {
if ( is_string( $src ) && $this->in_default_dir( $src ) && ! isset( $obj->extra['alt'] ) ) {
$this->concat .= "$handle,";
@@ -182,7 +172,12 @@ public function do_item( $handle, $group = false ) {
// A single item may alias a set of items, by having dependencies, but no source.
if ( ! $src ) {
- if ( $inline_style_tag ) {
+ if ( $inline_style ) {
+ $inline_style_tag = wp_get_inline_style_tag(
+ $inline_style,
+ array( 'id' => "{$handle}-inline-css" )
+ );
+
if ( $this->do_concat ) {
$this->print_html .= $inline_style_tag;
} else {
@@ -249,8 +244,11 @@ public function do_item( $handle, $group = false ) {
if ( $this->do_concat ) {
$this->print_html .= $tag;
- if ( $inline_style_tag ) {
- $this->print_html .= $inline_style_tag;
+ if ( $inline_style ) {
+ $this->print_html .= wp_get_inline_style_tag(
+ $inline_style,
+ array( 'id' => "{$handle}-inline-css" )
+ );
}
} else {
echo $tag;
@@ -376,11 +374,10 @@ public function print_inline_style( $handle, $display = true ) {
return $output;
}
- $processor = new WP_HTML_Tag_Processor( '' );
- $processor->next_tag();
- $processor->set_attribute( 'id', "{$handle}-inline-css" );
- $processor->set_modifiable_text( "\n{$output}\n" );
- echo "{$processor->get_updated_html()}\n";
+ wp_print_inline_style_tag(
+ $output,
+ array( 'id' => "{$handle}-inline-css" )
+ );
return true;
}
diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php
index dfaaf73ede55a..e4b07847185e3 100644
--- a/src/wp-includes/script-loader.php
+++ b/src/wp-includes/script-loader.php
@@ -2453,12 +2453,9 @@ function _print_styles() {
echo "\n";
if ( ! empty( $wp_styles->print_code ) ) {
- $processor = new WP_HTML_Tag_Processor( '' );
- $processor->next_tag();
- $style_tag_contents = "\n{$wp_styles->print_code}\n"
- . sprintf( "/*# sourceURL=%s */\n", rawurlencode( $concat_source_url ) );
- $processor->set_modifiable_text( $style_tag_contents );
- echo "{$processor->get_updated_html()}\n";
+ $style_tag_contents = $wp_styles->print_code . "\n"
+ . sprintf( '/*# sourceURL=%s */', rawurlencode( $concat_source_url ) );
+ wp_print_inline_style_tag( $style_tag_contents );
}
}
@@ -3672,6 +3669,110 @@ function wp_print_inline_script_tag( $data, $attributes = array() ) {
echo wp_get_inline_script_tag( $data, $attributes );
}
+/**
+ * Constructs an inline style tag.
+ *
+ * It is possible to inject attributes in the ` $attributes
+ * @phpstan-return non-falsy-string
+ */
+function wp_get_inline_style_tag( $data, array $attributes = array() ): string {
+ $data = "\n" . trim( (string) $data, " \t\f\r\n" ) . "\n";
+
+ $processor = new WP_HTML_Tag_Processor( '' );
+ $processor->next_tag();
+
+ /*
+ * Set the text first so the filter receives the CSS as it will be printed, which may differ from $data after escaping.
+ * This cannot fail for a STYLE tag, since any closing tag in the CSS is escaped rather than rejected.
+ */
+ $processor->set_modifiable_text( $data );
+
+ /**
+ * Filters attributes to be added to an inline style tag.
+ *
+ * This filter may run more than once for the same inline style, in which case only the last result is printed.
+ * For example, the `wp-block-library` inline style is built again by {@see wp_hoist_late_printed_styles()}
+ * after a placeholder is removed from its CSS.
+ *
+ * The `id` attribute, when supplied, cannot be changed or removed by this filter, since other code may locate the
+ * `' );
- $processor->next_tag();
- $processor->set_modifiable_text( $style );
- echo "{$processor->get_updated_html()}\n";
+ wp_print_inline_style_tag( $style );
},
$priority
);
@@ -4584,7 +4682,26 @@ public function replace( string $text ): void {
if ( '' === trim( $css_text ) ) {
$processor->remove();
} else {
- $processor->set_modifiable_text( $css_text );
+ /*
+ * Rebuild the STYLE tag rather than only replacing its text, so that the 'wp_inline_style_attributes'
+ * filter runs again with the CSS as it is now printed. Otherwise, any attribute derived from the CSS,
+ * such as a hash for a Content Security Policy, would no longer match it. The attributes already on
+ * the tag are passed along so that they are preserved, while the filter may still override them. The
+ * trailing newline is removed since the newline which followed the original tag remains in the buffer.
+ */
+ $attributes = array();
+ foreach ( $processor->get_attribute_names_with_prefix( '' ) ?? array() as $attribute_name ) {
+ $attribute_value = $processor->get_attribute( $attribute_name );
+ if ( null !== $attribute_value ) {
+ $attributes[ $attribute_name ] = $attribute_value;
+ }
+ }
+ $processor->replace(
+ rtrim(
+ wp_get_inline_style_tag( $css_text, $attributes ),
+ "\n"
+ )
+ );
}
$inserted_after = $printed_core_block_styles;
@@ -4597,11 +4714,10 @@ public function replace( string $text ): void {
* a sourceURL comment after the placeholder above is removed.
*/
if ( ! preg_match( ':^\s*(/\*# sourceURL=\S+? \*/\s*)?$:s', $extra_inline_styles ) ) {
- $style_processor = new WP_HTML_Tag_Processor( '' );
- $style_processor->next_tag();
- $style_processor->set_attribute( 'id', 'wp-block-library-inline-css-extra' );
- $style_processor->set_modifiable_text( $extra_inline_styles );
- $inserted_after .= "{$style_processor->get_updated_html()}\n";
+ $inserted_after .= wp_get_inline_style_tag(
+ $extra_inline_styles,
+ array( 'id' => 'wp-block-library-inline-css-extra' )
+ );
}
if ( '' !== $inserted_after ) {
diff --git a/tests/phpunit/tests/dependencies/styles.php b/tests/phpunit/tests/dependencies/styles.php
index 6f394d8c78697..51baacd05c627 100644
--- a/tests/phpunit/tests/dependencies/styles.php
+++ b/tests/phpunit/tests/dependencies/styles.php
@@ -9,23 +9,19 @@
* @covers ::wp_add_inline_style
*/
class Tests_Dependencies_Styles extends WP_UnitTestCase {
- private $old_wp_styles;
- private $old_wp_scripts;
+ private ?WP_Styles $old_wp_styles;
+ private ?WP_Scripts $old_wp_scripts;
+ /**
+ * @global WP_Scripts|null $wp_scripts
+ * @global WP_Styles|null $wp_styles
+ */
public function set_up() {
+ global $wp_scripts, $wp_styles;
parent::set_up();
- if ( empty( $GLOBALS['wp_styles'] ) ) {
- $GLOBALS['wp_styles'] = null;
- }
-
- $this->old_wp_styles = $GLOBALS['wp_styles'];
-
- if ( empty( $GLOBALS['wp_scripts'] ) ) {
- $GLOBALS['wp_scripts'] = null;
- }
-
- $this->old_wp_styles = $GLOBALS['wp_scripts'];
+ $this->old_wp_styles = $wp_styles;
+ $this->old_wp_scripts = $wp_scripts;
remove_action( 'wp_default_styles', 'wp_default_styles' );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
@@ -37,9 +33,14 @@ public function set_up() {
$GLOBALS['wp_scripts']->default_version = get_bloginfo( 'version' );
}
+ /**
+ * @global WP_Scripts|null $wp_scripts
+ * @global WP_Styles|null $wp_styles
+ */
public function tear_down() {
- $GLOBALS['wp_styles'] = $this->old_wp_styles;
- $GLOBALS['wp_scripts'] = $this->old_wp_scripts;
+ global $wp_scripts, $wp_styles;
+ $wp_styles = $this->old_wp_styles;
+ $wp_scripts = $this->old_wp_scripts;
add_action( 'wp_default_styles', 'wp_default_styles' );
add_action( 'wp_print_styles', 'print_emoji_styles' );
@@ -225,6 +226,134 @@ public function test_inline_styles_concat() {
$this->assertEqualHTML( $expected, $wp_styles->print_html );
}
+ /**
+ * Tests that inline style attributes are filtered during normal style output.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter() {
+ $mock_action = new MockAction();
+ add_filter( 'wp_inline_style_attributes', array( $mock_action, 'filter' ) );
+ add_filter(
+ 'wp_inline_style_attributes',
+ static function ( array $attributes ) {
+ $attributes['nonce'] = 'test-nonce';
+ return $attributes;
+ }
+ );
+
+ wp_enqueue_style( 'handle', 'http://example.com', array(), '1' );
+ wp_add_inline_style( 'handle', 'body { color: #123456; }' );
+
+ $expected = <<<'HTML'
+
+
+
+ HTML;
+
+ $this->assertEqualHTML( $expected, get_echo( 'wp_print_styles' ) );
+ $this->assertSame( 1, $mock_action->get_call_count() );
+ $this->assertSame( array( 'id' => 'handle-inline-css' ), $mock_action->get_args()[0][0] );
+ }
+
+ /**
+ * Tests that inline style attributes are filtered for a concatenated non-core stylesheet.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter_with_non_core_concat() {
+ wp_styles()->do_concat = true;
+ wp_styles()->default_dirs = array( '/wp-admin/', '/wp-includes/css/' );
+
+ add_filter(
+ 'wp_inline_style_attributes',
+ static function ( array $attributes ) {
+ $attributes['nonce'] = 'test-nonce';
+ return $attributes;
+ }
+ );
+
+ wp_enqueue_style( 'handle', 'http://example.com', array(), '1' );
+ wp_add_inline_style( 'handle', 'body { color: #123456; }' );
+ wp_print_styles();
+
+ $expected = <<<'HTML'
+
+
+
+ HTML;
+
+ $this->assertEqualHTML( $expected, wp_styles()->print_html );
+ }
+
+ /**
+ * Tests that inline style attributes are filtered for concatenated core styles.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter_with_core_concat() {
+ wp_styles()->do_concat = true;
+ wp_styles()->default_dirs = array( '/wp-admin/' );
+
+ add_filter(
+ 'wp_inline_style_attributes',
+ static function ( array $attributes ) {
+ $attributes['nonce'] = 'test-nonce';
+ return $attributes;
+ }
+ );
+
+ wp_enqueue_style( 'handle', '/wp-admin/example.css' );
+ wp_add_inline_style( 'handle', 'body { color: #123456; }' );
+ wp_print_styles();
+
+ $processor = new WP_HTML_Tag_Processor( get_echo( '_print_styles' ) );
+
+ $this->assertTrue( $processor->next_tag(), 'Expected the concatenated stylesheet LINK.' );
+ $this->assertSame( 'LINK', $processor->get_tag() );
+ $this->assertSame( 'stylesheet', $processor->get_attribute( 'rel' ) );
+ $href = $processor->get_attribute( 'href' );
+ $this->assertIsString( $href );
+ $this->assertStringContainsString( '/wp-admin/load-styles.php?', $href );
+
+ $this->assertTrue( $processor->next_tag(), 'Expected the concatenated inline STYLE.' );
+ $this->assertSame( 'STYLE', $processor->get_tag() );
+ $this->assertSame(
+ array( 'nonce' ),
+ $processor->get_attribute_names_with_prefix( '' ),
+ 'Expected the filtered nonce to be the only attribute.'
+ );
+ $this->assertSame( 'test-nonce', $processor->get_attribute( 'nonce' ) );
+ $this->assertSame(
+ "\nbody { color: #123456; }\n/*# sourceURL=css-inline-concat-handle */\n",
+ $processor->get_modifiable_text()
+ );
+
+ $this->assertFalse( $processor->next_tag(), 'Expected no other tags.' );
+ }
+
+ /**
+ * Tests that inline style attributes are not filtered when style output is suppressed.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter_not_called_when_output_is_suppressed() {
+ add_filter( 'style_loader_src', '__return_false' );
+ $mock_action = new MockAction();
+ add_filter( 'wp_inline_style_attributes', array( $mock_action, 'filter' ) );
+
+ wp_enqueue_style( 'handle', 'http://example.com', array(), '1' );
+ wp_add_inline_style( 'handle', 'body { color: #123456; }' );
+
+ $this->assertSame( '', get_echo( 'wp_print_styles' ) );
+ $this->assertSame( 0, $mock_action->get_call_count() );
+ }
+
/**
* Test normalizing relative links in CSS.
*
diff --git a/tests/phpunit/tests/dependencies/wpInlineStyleTag.php b/tests/phpunit/tests/dependencies/wpInlineStyleTag.php
new file mode 100644
index 0000000000000..a22a2463803e2
--- /dev/null
+++ b/tests/phpunit/tests/dependencies/wpInlineStyleTag.php
@@ -0,0 +1,299 @@
+
+ body { color: #123456; }
+
+
+ HTML;
+
+ $this->assertEqualHTML(
+ $expected,
+ wp_get_inline_style_tag(
+ $this->get_test_css(),
+ array(
+ 'id' => 'test-inline-css',
+ 'nonce' => 'test-nonce',
+ )
+ )
+ );
+ }
+
+ /**
+ * @ticket 51325
+ */
+ public function test_print_inline_style_tag_prints_get_inline_style_tag() {
+ $attributes = array( 'id' => 'test-inline-css' );
+
+ $this->assertSame(
+ wp_get_inline_style_tag( $this->get_test_css(), $attributes ),
+ get_echo(
+ 'wp_print_inline_style_tag',
+ array( $this->get_test_css(), $attributes )
+ )
+ );
+ }
+
+ /**
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter() {
+ add_filter(
+ 'wp_inline_style_attributes',
+ static function ( array $attributes ) {
+ $attributes['nonce'] = 'test-nonce';
+ return $attributes;
+ }
+ );
+
+ $filter = new MockAction();
+ add_filter( 'wp_inline_style_attributes', array( $filter, 'filter' ), 20, 2 );
+
+ $expected = <<<'HTML'
+
+
+ HTML;
+
+ $this->assertEqualHTML( $expected, wp_get_inline_style_tag( $this->get_test_css() ) );
+ $this->assertSame(
+ array(
+ array(
+ array( 'nonce' => 'test-nonce' ),
+ "\n{$this->get_test_css()}\n",
+ ),
+ ),
+ $filter->get_args()
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array ): array }>
+ */
+ public function data_inline_style_attributes_filter_changing_id(): array {
+ return array(
+ 'changed' => array(
+ static function ( array $attributes ): array {
+ $attributes['id'] = 'changed-inline-css';
+ return $attributes;
+ },
+ ),
+ 'removed' => array(
+ static function ( array $attributes ): array {
+ unset( $attributes['id'] );
+ return $attributes;
+ },
+ ),
+ 'lexical_variation' => array(
+ static function ( array $attributes ): array {
+ unset( $attributes['id'] );
+ $attributes['ID'] = 'changed-inline-css';
+ return $attributes;
+ },
+ ),
+ );
+ }
+
+ /**
+ * Tests that the attributes filter cannot change or remove the supplied ID.
+ *
+ * @ticket 51325
+ *
+ * @dataProvider data_inline_style_attributes_filter_changing_id
+ *
+ * @param Closure $filter Filter callback for the attributes.
+ */
+ public function test_inline_style_attributes_filter_cannot_change_id( Closure $filter ) {
+ $this->setExpectedIncorrectUsage( 'wp_get_inline_style_tag' );
+
+ add_filter( 'wp_inline_style_attributes', $filter );
+
+ $expected = <<<'HTML'
+
+
+ HTML;
+
+ $this->assertEqualHTML(
+ $expected,
+ wp_get_inline_style_tag( $this->get_test_css(), array( 'id' => 'test-inline-css' ) )
+ );
+ }
+
+ /**
+ * Tests that the attributes filter can add an ID when none is supplied.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter_can_add_id() {
+ add_filter(
+ 'wp_inline_style_attributes',
+ static function ( array $attributes ): array {
+ $attributes['id'] = 'added-inline-css';
+ return $attributes;
+ }
+ );
+
+ $expected = <<<'HTML'
+
+
+ HTML;
+
+ $this->assertEqualHTML( $expected, wp_get_inline_style_tag( $this->get_test_css() ) );
+ }
+
+ /**
+ * Tests that the attributes filter receives the CSS as printed, after any escaping.
+ *
+ * This allows a filter to compute a CSP hash which matches the printed STYLE contents.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_attributes_filter_receives_escaped_data() {
+ $filter = new MockAction();
+ add_filter( 'wp_inline_style_attributes', array( $filter, 'filter' ), 10, 2 );
+
+ $processor = new WP_HTML_Tag_Processor( wp_get_inline_style_tag( 'a::after { content: ""; }' ) );
+
+ $this->assertSame( 1, $filter->get_call_count(), 'Expected the filter to be called once.' );
+ $filtered_data = $filter->get_args()[0][1];
+ $this->assertIsString( $filtered_data, 'Expected the filtered data to be a string.' );
+ $this->assertTrue( $processor->next_tag( 'STYLE' ), 'Expected a STYLE tag.' );
+ $this->assertStringNotContainsString( '', $filtered_data, 'Expected the filtered data to be escaped.' );
+ $this->assertSame( $processor->get_modifiable_text(), $filtered_data );
+ }
+
+ /**
+ * Test the behavior of generated style tag attributes passed different values and types of values.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_tag_attribute_value_types() {
+ $expected = <<<'HTML'
+
+
+ HTML;
+
+ $this->assertEqualHTML(
+ $expected,
+ wp_get_inline_style_tag(
+ $this->get_test_css(),
+ array( // @phpstan-ignore argument.type
+ 'true' => true,
+ 'false' => false,
+ 'null' => null,
+ 'empty-string' => '',
+ '0-string' => '0',
+ '1-string' => '1',
+ '0-numeric' => 0,
+ '1-numeric' => 1,
+ )
+ )
+ );
+ }
+
+ /**
+ * Test the behavior of generated style tag repeated attributes.
+ *
+ * HTML will ignore case-insensitive repeated attributes. Ensure that the handling of input
+ * attributes aligns with expectations.
+ *
+ * @ticket 51325
+ */
+ public function test_inline_style_tag_repeat_attributes() {
+ $expected = <<<'HTML'
+
+
+ HTML;
+
+ $this->assertEqualHTML(
+ $expected,
+ wp_get_inline_style_tag(
+ $this->get_test_css(),
+ array(
+ 'test' => 'test-a',
+ 'tesT' => 'tesT-b',
+ 'teST' => 'teST-c',
+ 'tEST' => 'tEST-d',
+ 'TEST' => 'TEST-e',
+ )
+ )
+ );
+ }
+
+ /**
+ * Data provider for {@see self::test_inline_style_tag_normalizes_whitespace()}.
+ *
+ * @return array
+ */
+ public function data_inline_style_tag_whitespace(): array {
+ return array(
+ 'no whitespace' => array( 'a{}', "\na{}\n" ),
+ 'newlines' => array( "\n\na{}\n\n", "\na{}\n" ),
+ 'all CSS whitespace' => array( " \t\f\r\na{}\n\r\f\t ", "\na{}\n" ),
+ 'inner whitespace kept' => array( "\ta{}\n\n\tb{}\t", "\na{}\n\n\tb{}\n" ),
+ 'only whitespace' => array( " \t\f\r\n", "\n\n" ),
+ 'empty string' => array( '', "\n\n" ),
+ 'null from a filter' => array( null, "\n\n" ),
+ 'non-whitespace preserved' => array( "\u{00A0}a{}\u{00A0}", "\n\u{00A0}a{}\u{00A0}\n" ),
+ );
+ }
+
+ /**
+ * Tests that leading and trailing CSS whitespace is replaced by a single newline.
+ *
+ * @ticket 51325
+ *
+ * @dataProvider data_inline_style_tag_whitespace
+ *
+ * @param string|null $data CSS passed to the function.
+ * @param non-empty-string $expected Expected contents of the STYLE element.
+ */
+ public function test_inline_style_tag_normalizes_whitespace( ?string $data, string $expected ): void {
+ $processor = new WP_HTML_Tag_Processor( wp_get_inline_style_tag( $data ) ); // @phpstan-ignore argument.type
+
+ $this->assertTrue( $processor->next_tag( 'STYLE' ), 'Expected a STYLE tag.' );
+ $this->assertSame( $expected, $processor->get_modifiable_text() );
+ }
+
+ /**
+ * Gets test CSS code.
+ *
+ * @return non-falsy-string
+ */
+ private function get_test_css(): string {
+ return 'body { color: #123456; }';
+ }
+}
diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php
index 767d84e7a52a4..f0231e2df437f 100644
--- a/tests/phpunit/tests/template.php
+++ b/tests/phpunit/tests/template.php
@@ -1599,6 +1599,104 @@ public function data_wp_hoist_late_printed_styles(): array {
),
),
+ 'inline_style_attributes_filter_adds_hash' => array(
+ 'set_up' => static function () {
+ add_filter(
+ 'wp_inline_style_attributes',
+ static function ( array $attributes, string $data ): array {
+ $attributes['data-hash'] = hash( 'sha256', $data );
+ return $attributes;
+ },
+ 10,
+ 2
+ );
+ },
+ 'content' => $blocks_content,
+ 'inline_size_limit' => PHP_INT_MAX,
+ 'expected_styles' => array(
+ 'HEAD' => array_merge(
+ $early_common_styles,
+ array(
+ 'wp-block-library-inline-css',
+ 'wp-block-separator-inline-css',
+ 'classic-theme-styles-inline-css',
+ 'third-party-test-block-css',
+ 'custom-block-styles-css',
+ 'global-styles-inline-css',
+ ),
+ $common_at_wp_enqueue_scripts,
+ $common_late_in_head,
+ $common_late_in_body
+ ),
+ 'BODY' => array(),
+ ),
+ 'assert' => function ( string $buffer, string $filtered_buffer ) {
+ $processor = new WP_HTML_Tag_Processor( $filtered_buffer );
+ $found = false;
+ while ( $processor->next_tag( 'STYLE' ) ) {
+ $hash = $processor->get_attribute( 'data-hash' );
+ if ( null === $hash ) {
+ continue;
+ }
+ $id = $processor->get_attribute( 'id' );
+ $this->assertSame( hash( 'sha256', $processor->get_modifiable_text() ), $hash, "Expected the hash to match the printed CSS for the STYLE with ID: $id" );
+ if ( 'wp-block-library-inline-css' === $id ) {
+ $found = true;
+ }
+ }
+ $this->assertTrue( $found, 'Expected the hoisted wp-block-library inline style to have a hash.' );
+ },
+ ),
+
+ 'block_library_inline_style_attributes_preserved' => array(
+ 'set_up' => static function () {
+ // Add an attribute to the block library inline style before wp_hoist_late_printed_styles() rebuilds it.
+ add_filter(
+ 'wp_template_enhancement_output_buffer',
+ static function ( string $buffer ): string {
+ $processor = new WP_HTML_Tag_Processor( $buffer );
+ while ( $processor->next_tag( 'STYLE' ) ) {
+ if ( 'wp-block-library-inline-css' === $processor->get_attribute( 'id' ) ) {
+ $processor->set_attribute( 'data-preserved', 'yes' );
+ }
+ }
+ return $processor->get_updated_html();
+ },
+ 0
+ );
+ },
+ 'content' => $blocks_content,
+ 'inline_size_limit' => PHP_INT_MAX,
+ 'expected_styles' => array(
+ 'HEAD' => array_merge(
+ $early_common_styles,
+ array(
+ 'wp-block-library-inline-css',
+ 'wp-block-separator-inline-css',
+ 'classic-theme-styles-inline-css',
+ 'third-party-test-block-css',
+ 'custom-block-styles-css',
+ 'global-styles-inline-css',
+ ),
+ $common_at_wp_enqueue_scripts,
+ $common_late_in_head,
+ $common_late_in_body
+ ),
+ 'BODY' => array(),
+ ),
+ 'assert' => function ( string $buffer, string $filtered_buffer ) {
+ $processor = new WP_HTML_Tag_Processor( $filtered_buffer );
+ $found = false;
+ while ( $processor->next_tag( 'STYLE' ) ) {
+ if ( 'wp-block-library-inline-css' === $processor->get_attribute( 'id' ) ) {
+ $found = true;
+ $this->assertSame( 'yes', $processor->get_attribute( 'data-preserved' ), 'Expected the existing attribute to be preserved.' );
+ }
+ }
+ $this->assertTrue( $found, 'Expected the wp-block-library inline style to be present.' );
+ },
+ ),
+
'classic_theme_styles_omitted' => array(
'set_up' => static function () {
// Note that wp_enqueue_scripts is used instead of enqueue_block_assets because it runs again at the former action.
@@ -2089,7 +2187,7 @@ static function () {
// Create a simulated output buffer.
$buffer = '' . $head_output . '' . $content . '' . $footer_output . '';
- $global_styles_placeholder_regexp = '#