diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php index 10f3ee3e2dd0f..5f15da5383f34 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -1468,8 +1468,8 @@ public function serialize_token(): string { /* * The HTML parser strips a leading newline immediately after the start - * tag of TEXTAREA, PRE, and LISTING elements. When serializing, prepend - * a leading newline to ensure the semantic HTML content is preserved. + * tag of TEXTAREA, PRE, and LISTING elements in HTML content. When serializing, + * prepend a leading newline to ensure the semantic HTML content is preserved. * * For example, `
\n\nX
` must not become `
\nX
` because its content * has changed. However, `
X
` and `
\nX
` are _equivalent_. @@ -1488,7 +1488,7 @@ public function serialize_token(): string { * * @see https://html.spec.whatwg.org/multipage/parsing.html */ - if ( 'TEXTAREA' === $tag_name || 'PRE' === $tag_name || 'LISTING' === $tag_name ) { + if ( $in_html && ( 'TEXTAREA' === $tag_name || 'PRE' === $tag_name || 'LISTING' === $tag_name ) ) { $html .= "\n"; } diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php b/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php index d9d7d7c13394a..e332ec12a0a91 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php @@ -463,21 +463,30 @@ public static function data_provider_serialize_doctype() { } /** - * Ensures that leading newlines in PRE, LISTING, and TEXTAREA elements are preserved upon normalization, - * and that normalization is idempotent in these cases. + * Ensures that leading newlines in PRE, LISTING, and TEXTAREA elements are normalized + * according to their parsing namespace, and that normalization is idempotent in these cases. * * @ticket 64607 * * @dataProvider data_provider_normalize_special_leading_newline_cases * * @param string $input HTML input containing leading newlines in PRE, LISTING, or TEXTAREA elements. - * @param string $expected Expected output after normalization, which should preserve leading newlines. + * @param string $expected Expected exact output after normalization. */ public function test_normalize_special_leading_newline_handling( string $input, string $expected ) { $normalized = WP_HTML_Processor::normalize( $input ); - $this->assertEqualHTML( $expected, $normalized ); + + /* + * Byte equality pins normalize()'s serialized form; HTML equality verifies + * semantic equivalence. This distinction matters because HTML parsing ignores + * one leading LF after PRE, LISTING, and TEXTAREA start tags. + */ + $this->assertSame( $expected, $normalized ); + $this->assertEqualHTML( $input, $normalized ); + $normalized_twice = WP_HTML_Processor::normalize( $normalized ); - $this->assertEqualHTML( $expected, $normalized_twice ); + $this->assertSame( $expected, $normalized_twice ); + $this->assertEqualHTML( $normalized, $normalized_twice ); } /** @@ -653,13 +662,13 @@ public static function data_provider_normalized_fuzzer_cases_that_should_be_idem /** * Data provider. * - * @return array[] + * @return array */ - public static function data_provider_normalize_special_leading_newline_cases() { + public static function data_provider_normalize_special_leading_newline_cases(): array { return array( 'Leading newline in PRE' => array( "
\nline 1\nline 2
", - "
line 1\nline 2
", + "
\nline 1\nline 2
", ), 'Double leading newline in PRE' => array( "
\n\nline 2\nline 3
", @@ -667,7 +676,7 @@ public static function data_provider_normalize_special_leading_newline_cases() { ), 'Multiple text nodes inside PRE' => array( "
\nline 1 still line 1
", - '
line 1 still line 1
', + "
\nline 1 still line 1
", ), 'Multiple text nodes inside PRE with leading newlines' => array( "
\n\nline 2 still line 2
", @@ -675,7 +684,7 @@ public static function data_provider_normalize_special_leading_newline_cases() { ), 'Leading newline in LISTING' => array( "\nline 1\nline 2", - "line 1\nline 2", + "\nline 1\nline 2", ), 'Double leading newline in LISTING' => array( "\n\nline 2\nline 3", @@ -683,7 +692,7 @@ public static function data_provider_normalize_special_leading_newline_cases() { ), 'Multiple text nodes inside LISTING' => array( "\nline 1 still line 1", - 'line 1 still line 1', + "\nline 1 still line 1", ), 'Multiple text nodes inside LISTING with leading newlines' => array( "\n\nline 2 still line 2", @@ -691,12 +700,28 @@ public static function data_provider_normalize_special_leading_newline_cases() { ), 'Leading newline in TEXTAREA' => array( "", - "", + "", ), 'Double leading newline in TEXTAREA' => array( "", "", ), + 'Foreign MathML TEXTAREA does not ignore leading newlines' => array( + '', + '', + ), + 'Foreign MathML TEXTAREA preserves leading newline' => array( + "", + "", + ), + 'Foreign SVG TEXTAREA does not ignore leading newlines' => array( + '', + '', + ), + 'Foreign SVG TEXTAREA preserves leading newline' => array( + "", + "", + ), ); } }