Skip to content

Commit d130c92

Browse files
committed
Fix newlines disappearing inside PRE,TEXTAREA elements
Add serialize + normalize tests Add more PRE tests Fix newlines disappearing inside PRE,TEXTAREA elements Handline PRE,LISTING lints Simpler fix Remove overly-specific tests Add LISTING tests Add explanatory comment lints Remove accidental import
1 parent 5930f08 commit d130c92

2 files changed

Lines changed: 83 additions & 0 deletions

File tree

src/wp-includes/html-api/class-wp-html-processor.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,32 @@ public function serialize_token(): string {
14121412

14131413
$html .= '>';
14141414

1415+
/*
1416+
* The HTML parser strips a leading newline immediately after the start
1417+
* tag of TEXTAREA, PRE, and LISTING elements. When serializing, prepend
1418+
* a leading newline to ensure the semantic HTML content is preserved.
1419+
*
1420+
* For example, `<pre>\n\nX</pre>` must not become `<pre>\nX</pre>` because its content
1421+
* has changed. However, `<pre>X</pre>` and `<pre>\nX</pre>` are _equivalent_.
1422+
*
1423+
* > A start tag whose tag name is "textarea"
1424+
* > …
1425+
* > If the next token is a U+000A LINE FEED (LF) character token, then ignore
1426+
* > that token and move on to the next one. (Newlines at the start of textarea
1427+
* > elements are ignored as an authoring convenience.)
1428+
*
1429+
* > A start tag whose tag name is one of: "pre", "listing"
1430+
* > …
1431+
* > If the next token is a U+000A LINE FEED (LF) character token, then ignore
1432+
* > that token and move on to the next one. (Newlines at the start of pre blocks
1433+
* > are ignored as an authoring convenience.)
1434+
*
1435+
* @see https://html.spec.whatwg.org/multipage/parsing.html
1436+
*/
1437+
if ( $tag_name === 'TEXTAREA' || $tag_name === 'PRE' || $tag_name === 'LISTING' ) {
1438+
$html .= "\n";
1439+
}
1440+
14151441
// Flush out self-contained elements.
14161442
if ( $in_html && in_array( $tag_name, array( 'IFRAME', 'NOEMBED', 'NOFRAMES', 'SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE', 'XMP' ), true ) ) {
14171443
$text = $this->get_modifiable_text();

tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,61 @@ public static function data_provider_serialize_doctype() {
321321
'Double quotes in system ID' => array( '<!DOCTYPE html SYSTEM \'"quoted"\'\>', '<!DOCTYPE html SYSTEM \'"quoted"\'>' ),
322322
);
323323
}
324+
325+
/**
326+
* @ticket TBD
327+
*
328+
* @dataProvider data_provider_normalize_special_leading_newline_cases
329+
*/
330+
public function test_normalize_special_leading_newline_handling( string $input, string $expected ) {
331+
$normalized = WP_HTML_Processor::normalize( $input );
332+
$this->assertEqualHTML( $expected, $normalized );
333+
$normalized_twice = WP_HTML_Processor::normalize( $normalized );
334+
$this->assertEqualHTML( $expected, $normalized_twice );
335+
}
336+
337+
public static function data_provider_normalize_special_leading_newline_cases() {
338+
return array(
339+
'Leading newline in PRE' => array(
340+
"<pre>\nline 1\nline 2</pre>",
341+
"<pre>line 1\nline 2</pre>",
342+
),
343+
'Double leading newline in PRE' => array(
344+
"<pre>\n\nline 2\nline 3</pre>",
345+
"<pre>\n\nline 2\nline 3</pre>",
346+
),
347+
'Multiple text nodes inside PRE' => array(
348+
"<pre>\nline 1<!--comment--> still line 1</pre>",
349+
'<pre>line 1<!--comment--> still line 1</pre>',
350+
),
351+
'Multiple text nodes inside PRE with leading newlines' => array(
352+
"<pre>\n\nline 2<!--comment--> still line 2</pre>",
353+
"<pre>\n\nline 2<!--comment--> still line 2</pre>",
354+
),
355+
'Leading newline in LISTING' => array(
356+
"<listing>\nline 1\nline 2</listing>",
357+
"<listing>line 1\nline 2</listing>",
358+
),
359+
'Double leading newline in LISTING' => array(
360+
"<listing>\n\nline 2\nline 3</listing>",
361+
"<listing>\n\nline 2\nline 3</listing>",
362+
),
363+
'Multiple text nodes inside LISTING' => array(
364+
"<listing>\nline 1<!--comment--> still line 1</listing>",
365+
'<listing>line 1<!--comment--> still line 1</listing>',
366+
),
367+
'Multiple text nodes inside LISTING with leading newlines' => array(
368+
"<listing>\n\nline 2<!--comment--> still line 2</listing>",
369+
"<listing>\n\nline 2<!--comment--> still line 2</listing>",
370+
),
371+
'Leading newline in TEXTAREA' => array(
372+
"<textarea>\nline 1\nline 2</textarea>",
373+
"<textarea>line 1\nline 2</textarea>",
374+
),
375+
'Double leading newline in TEXTAREA' => array(
376+
"<textarea>\n\nline 2\nline 3</textarea>",
377+
"<textarea>\n\nline 2\nline 3</textarea>",
378+
),
379+
);
380+
}
324381
}

0 commit comments

Comments
 (0)