From c28ea1d3ef169474bc67031afc86845b5438b0b6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 14:24:31 +0100 Subject: [PATCH 01/10] Add "area", "br", "embed", "img", "keygen", "wbr" tag rules --- .../html-api/class-wp-html-processor.php | 21 +++++++++++--- .../tests/html-api/wpHtmlProcessor.php | 5 ---- .../html-api/wpHtmlProcessorSemanticRules.php | 29 +++++++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) 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 cce26a60c5350..11617df3b8430 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -934,12 +934,29 @@ private function step_in_body() { $this->run_adoption_agency_algorithm(); return true; + + /* + * > An end tag whose tag name is "br" + * > Parse error. Drop the attributes from the token, and act as described in the next + * > entry; i.e. act as if this was a "br" start tag token with no attributes, rather + * > than the end tag token that it actually is. + */ + case '-BR': + $this->last_error = self::ERROR_UNSUPPORTED; + throw new WP_HTML_Unsupported_Exception( "Closing BR tags require unimplemented special handling." ); + /* * > A start tag whose tag name is one of: "area", "br", "embed", "img", "keygen", "wbr" */ + case '+AREA': + case '+BR': + case '+EMBED': case '+IMG': + case '+KEYGEN': + case '+WBR': $this->reconstruct_active_formatting_elements(); $this->insert_html_element( $this->state->current_token ); + $this->state->frameset_ok = false; return true; } @@ -966,13 +983,11 @@ private function step_in_body() { case 'BASEFONT': case 'BGSOUND': case 'BODY': - case 'BR': case 'CAPTION': case 'COL': case 'COLGROUP': case 'DD': case 'DT': - case 'EMBED': case 'FORM': case 'FRAME': case 'FRAMESET': @@ -981,7 +996,6 @@ private function step_in_body() { case 'HTML': case 'IFRAME': case 'INPUT': - case 'KEYGEN': case 'LI': case 'LINK': case 'LISTING': @@ -1021,7 +1035,6 @@ private function step_in_body() { case 'TR': case 'TRACK': case 'UL': - case 'WBR': case 'XMP': $this->last_error = self::ERROR_UNSUPPORTED; throw new WP_HTML_Unsupported_Exception( "Cannot process {$tag_name} element." ); diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor.php b/tests/phpunit/tests/html-api/wpHtmlProcessor.php index d9f1357b5c66f..2e331d25b621e 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessor.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessor.php @@ -159,16 +159,13 @@ public function test_step_in_body_fails_on_unsupported_tags( $tag_name ) { public function data_unsupported_special_in_body_tags() { return array( 'APPLET' => array( 'APPLET' ), - 'AREA' => array( 'AREA' ), 'BASE' => array( 'BASE' ), 'BASEFONT' => array( 'BASEFONT' ), 'BGSOUND' => array( 'BGSOUND' ), 'BODY' => array( 'BODY' ), - 'BR' => array( 'BR' ), 'CAPTION' => array( 'CAPTION' ), 'COL' => array( 'COL' ), 'COLGROUP' => array( 'COLGROUP' ), - 'EMBED' => array( 'EMBED' ), 'FORM' => array( 'FORM' ), 'FRAME' => array( 'FRAME' ), 'FRAMESET' => array( 'FRAMESET' ), @@ -177,7 +174,6 @@ public function data_unsupported_special_in_body_tags() { 'HTML' => array( 'HTML' ), 'IFRAME' => array( 'IFRAME' ), 'INPUT' => array( 'INPUT' ), - 'KEYGEN' => array( 'KEYGEN' ), 'LINK' => array( 'LINK' ), 'LISTING' => array( 'LISTING' ), 'MARQUEE' => array( 'MARQUEE' ), @@ -214,7 +210,6 @@ public function data_unsupported_special_in_body_tags() { 'TITLE' => array( 'TITLE' ), 'TR' => array( 'TR' ), 'TRACK' => array( 'TRACK' ), - 'WBR' => array( 'WBR' ), 'XMP' => array( 'XMP' ), ); } diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php index c1adf9a71a3f8..c33d066544f59 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php @@ -376,4 +376,33 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element $this->assertSame( 'DIV', $p->get_tag(), "Expected to find DIV element, but found {$p->get_tag()} instead." ); $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'DIV' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting: SPAN should be closed and DIV should be its sibling.' ); } + + /** + * Verifies that when "in body" and encountering a br close tag `
`: + * + * > An end tag whose tag name is "br" + * > Parse error. Drop the attributes from the token, and act as described in the next entry; + * > i.e. act as if this was a "br" start tag token with no attributes, rather than the end + * > tag token that it actually is. + * + * @covers WP_HTML_Processor::step_in_body + * + * @ticket 58907 + * + * @since 6.4.0 + */ + public function test_br_close_tag_special_behavior() { + $this->markTestIncomplete( 'BR end tag special handling is unimplemented' ); + + $p = WP_HTML_Processor::create_fragment( '
' ); + + $this->assertTrue( $p->next_tag( 'BR' ), 'No BR tag found.' ); + $this->assertNull( $p->get_attribute_names_with_prefix( '' ), 'BR end tag had attributes.' ); + $this->assertFalse( $p->is_tag_closer(), '
is treated as a BR start tag.' ); + + $this->assertFalse( $p->set_attribute( 'new-attribute', 'added' ), 'BR end tag becomes an opener' ); + $this->assertCount( 1, $p->get_attribute_names_with_prefix( '' ), 'Tag should have 1 attribute.' ); + $this->assertSame( 'added', $p->get_attribute( 'new-attribute' ), 'Tag did not set attribute value correctly.' ); + $this->assertSame( '
', $p->get_updated_html(), 'Tag HTML was not updated correctly.' ); + } } From 2a80cd2458276c33a80db69e9da78ee2165a65d4 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 14:34:20 +0100 Subject: [PATCH 02/10] Test tweaks --- .../tests/html-api/wpHtmlProcessorSemanticRules.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php index c33d066544f59..7c8cf49fb2c63 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php @@ -378,7 +378,7 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element } /** - * Verifies that when "in body" and encountering a br close tag `
`: + * Verifies that when "in body" and encountering a BR end tag `
`: * * > An end tag whose tag name is "br" * > Parse error. Drop the attributes from the token, and act as described in the next entry; @@ -391,15 +391,14 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element * * @since 6.4.0 */ - public function test_br_close_tag_special_behavior() { + public function test_br_end_tag_special_behavior() { $this->markTestIncomplete( 'BR end tag special handling is unimplemented' ); $p = WP_HTML_Processor::create_fragment( '
' ); - $this->assertTrue( $p->next_tag( 'BR' ), 'No BR tag found.' ); + $this->assertTrue( $p->next_tag(), 'No BR tag found.' ); + $this->assertFalse( $p->is_tag_closer(), '
should not be treated as an end tag.' ); $this->assertNull( $p->get_attribute_names_with_prefix( '' ), 'BR end tag had attributes.' ); - $this->assertFalse( $p->is_tag_closer(), '
is treated as a BR start tag.' ); - $this->assertFalse( $p->set_attribute( 'new-attribute', 'added' ), 'BR end tag becomes an opener' ); $this->assertCount( 1, $p->get_attribute_names_with_prefix( '' ), 'Tag should have 1 attribute.' ); $this->assertSame( 'added', $p->get_attribute( 'new-attribute' ), 'Tag did not set attribute value correctly.' ); From 41f95272f511b8b592c82dd93fc0a223056e1ddc Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 14:51:58 +0100 Subject: [PATCH 03/10] Mention ticket, add unsupported test --- .../html-api/wpHtmlProcessorSemanticRules.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php index 7c8cf49fb2c63..2dd60370e0dfe 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php @@ -385,9 +385,33 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element * > i.e. act as if this was a "br" start tag token with no attributes, rather than the end * > tag token that it actually is. * + * When this handling is implemented, this test should be removed and + * `test_br_end_tag_special_behavior` should not be marked incomplete. + * * @covers WP_HTML_Processor::step_in_body * - * @ticket 58907 + * @ticket 60283 + * + * @since 6.4.0 + */ + public function test_br_end_tag_unsupported() { + $p = WP_HTML_Processor::create_fragment( '
' ); + + $this->assertFalse( $p->next_tag(), 'Found a BR tag that should not be handled.' ); + $this->assertSame( WP_HTML_Processor::ERROR_UNSUPPORTED, $p->get_last_error() ); + } + + /** + * Verifies that when "in body" and encountering a BR end tag `
`: + * + * > An end tag whose tag name is "br" + * > Parse error. Drop the attributes from the token, and act as described in the next entry; + * > i.e. act as if this was a "br" start tag token with no attributes, rather than the end + * > tag token that it actually is. + * + * @covers WP_HTML_Processor::step_in_body + * + * @ticket 60283 * * @since 6.4.0 */ From 839a715df5e57f28228e2345cc0b722988e0b8fe Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 16:10:59 +0100 Subject: [PATCH 04/10] Add unnestable test --- .../tests/html-api/wpHtmlProcessor.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor.php b/tests/phpunit/tests/html-api/wpHtmlProcessor.php index 2e331d25b621e..ccac0a67d060b 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessor.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessor.php @@ -132,6 +132,46 @@ public function test_fails_to_reconstruct_formatting_elements() { $this->assertFalse( $p->next_tag( 'EM' ), 'Should have aborted before finding second EM as it required reconstructing the first EM.' ); } + /** + * Ensure non-nesting tags do not nest + * + * @ticket 60283 + * + * @covers WP_HTML_Processor::step_in_body + * + * @dataProvider data_unnestable_tags + */ + public function test_cannot_nest_unnestable_tags( $tag ) { + $p = WP_HTML_Processor::create_fragment( "{$tag}{$tag}" ); + + // We should have this structure: + // + // + // <$TAG /> + // <$TAG /> + // + // + // + // The breadcrumbs should be: + // HTML > BODY > $TAG + + $this->assertTrue( $p->next_tag(), "Could not find first {$tag}." ); + $this->assertCount( 3, $p->get_breadcrumbs(), "First {$tag} was not nested correctly." ); + $this->assertTrue( $p->next_tag(), "Could not find second {$tag}." ); + $this->assertCount( 3, $p->get_breadcrumbs(), "Second {$tag} was not nested correctly." ); + } + + public function data_unnestable_tags() { + return array( + 'AREA' => array( '' ), + 'BR' => array( '
' ), + 'EMBED' => array( '' ), + 'IMG' => array( '' ), + 'KEYGEN' => array( '' ), + 'WBR' => array( '' ), + ); + } + /** * Ensures that special handling of unsupported tags is cleaned up * as handling is implemented. Otherwise there's risk of leaving special From 4a50cc1a32046ea5abcf9ce5407d60cbf8140951 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 16:36:31 +0100 Subject: [PATCH 05/10] Fix void nesting tests --- .../html-api/class-wp-html-processor.php | 8 ++++- .../tests/html-api/wpHtmlProcessor.php | 36 ++++++++++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) 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 11617df3b8430..aeb41bb41eac4 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -1698,7 +1698,13 @@ public static function is_void( $tag_name ) { 'META' === $tag_name || 'SOURCE' === $tag_name || 'TRACK' === $tag_name || - 'WBR' === $tag_name + 'WBR' === $tag_name || + + // Obsolete + // + // This does not appear in https://html.spec.whatwg.org/#void-elements + // but it was a void tag and browsers treat it as such. + 'KEYGEN' === $tag_name ); } diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor.php b/tests/phpunit/tests/html-api/wpHtmlProcessor.php index ccac0a67d060b..b8d81fd95c868 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessor.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessor.php @@ -138,11 +138,12 @@ public function test_fails_to_reconstruct_formatting_elements() { * @ticket 60283 * * @covers WP_HTML_Processor::step_in_body + * @covers WP_HTML_Processor::is_void * - * @dataProvider data_unnestable_tags + * @dataProvider data_void_tags */ - public function test_cannot_nest_unnestable_tags( $tag ) { - $p = WP_HTML_Processor::create_fragment( "{$tag}{$tag}" ); + public function test_cannot_nest_void_tags( $tag ) { + $p = WP_HTML_Processor::create_fragment( "{$tag}
" ); // We should have this structure: // @@ -155,19 +156,38 @@ public function test_cannot_nest_unnestable_tags( $tag ) { // The breadcrumbs should be: // HTML > BODY > $TAG - $this->assertTrue( $p->next_tag(), "Could not find first {$tag}." ); - $this->assertCount( 3, $p->get_breadcrumbs(), "First {$tag} was not nested correctly." ); - $this->assertTrue( $p->next_tag(), "Could not find second {$tag}." ); - $this->assertCount( 3, $p->get_breadcrumbs(), "Second {$tag} was not nested correctly." ); + $result = $p->next_tag(); + + if ( WP_HTML_Processor::ERROR_UNSUPPORTED === $p->get_last_error() ) { + $this->markTestSkipped( "{$tag} is unsupported." ); + } + + $this->assertTrue( $result, "Could not find first {$tag}." ); + $this->assertCount( 3, $p->get_breadcrumbs(), "{$tag} was not nested correctly." ); + $this->assertTrue( $p->next_tag( 'DIV' ), "Could not find
tag." ); + $this->assertCount( 3, $p->get_breadcrumbs(), "Following
was not nested correctly." ); } - public function data_unnestable_tags() { + /** + * Data provider. + * + * @return array[] + */ + public function data_void_tags() { return array( 'AREA' => array( '' ), + 'BASE' => array( '' ), 'BR' => array( '
' ), + 'COL' => array( '' ), 'EMBED' => array( '' ), + 'HR' => array( '
' ), 'IMG' => array( '' ), + 'INPUT' => array( '' ), 'KEYGEN' => array( '' ), + 'LINK' => array( '' ), + 'META' => array( '' ), + 'SOURCE' => array( '' ), + 'TRACK' => array( '' ), 'WBR' => array( '' ), ); } From fd26b12f5e64ff8a2a5547d3d2acceb5038b5485 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 16:38:57 +0100 Subject: [PATCH 06/10] Fix lints --- src/wp-includes/html-api/class-wp-html-processor.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 aeb41bb41eac4..e0a75765b470d 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -934,7 +934,6 @@ private function step_in_body() { $this->run_adoption_agency_algorithm(); return true; - /* * > An end tag whose tag name is "br" * > Parse error. Drop the attributes from the token, and act as described in the next @@ -943,7 +942,7 @@ private function step_in_body() { */ case '-BR': $this->last_error = self::ERROR_UNSUPPORTED; - throw new WP_HTML_Unsupported_Exception( "Closing BR tags require unimplemented special handling." ); + throw new WP_HTML_Unsupported_Exception( 'Closing BR tags require unimplemented special handling.' ); /* * > A start tag whose tag name is one of: "area", "br", "embed", "img", "keygen", "wbr" From 94e0ff8489cf1b533303b8166bdf4f43a20d4fd3 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 16:39:51 +0100 Subject: [PATCH 07/10] Fix lints --- tests/phpunit/tests/html-api/wpHtmlProcessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor.php b/tests/phpunit/tests/html-api/wpHtmlProcessor.php index b8d81fd95c868..e3dd535110763 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessor.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessor.php @@ -164,8 +164,8 @@ public function test_cannot_nest_void_tags( $tag ) { $this->assertTrue( $result, "Could not find first {$tag}." ); $this->assertCount( 3, $p->get_breadcrumbs(), "{$tag} was not nested correctly." ); - $this->assertTrue( $p->next_tag( 'DIV' ), "Could not find
tag." ); - $this->assertCount( 3, $p->get_breadcrumbs(), "Following
was not nested correctly." ); + $this->assertTrue( $p->next_tag( 'DIV' ), 'Could not find
tag.' ); + $this->assertCount( 3, $p->get_breadcrumbs(), 'Following
was not nested correctly.' ); } /** From 8e3b3f29f9aba20f1ea654ef906cd8ea7e313e57 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 18 Jan 2024 17:12:01 +0100 Subject: [PATCH 08/10] Remove implemented tags from unsupported list --- tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php b/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php index 15d38d6f70c6c..a7253ebb26eb1 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php @@ -162,15 +162,12 @@ public function test_fails_when_encountering_unsupported_tag( $html ) { public function data_unsupported_elements() { $unsupported_elements = array( 'APPLET', // Deprecated. - 'AREA', 'BASE', 'BGSOUND', // Deprecated; self-closing if self-closing flag provided, otherwise normal. 'BODY', - 'BR', 'CAPTION', 'COL', 'COLGROUP', - 'EMBED', 'FORM', 'FRAME', 'FRAMESET', @@ -179,7 +176,6 @@ public function data_unsupported_elements() { 'HTML', 'IFRAME', 'INPUT', - 'KEYGEN', // Deprecated; void. 'LINK', 'LISTING', // Deprecated, use PRE instead. 'MARQUEE', // Deprecated. @@ -214,7 +210,6 @@ public function data_unsupported_elements() { 'TITLE', 'TR', 'TRACK', - 'WBR', 'XMP', // Deprecated, use PRE instead. ); From 6f6e09bc1e64250f90daf358b2272b22ca6e79f8 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 19 Jan 2024 13:01:42 -0700 Subject: [PATCH 09/10] Update tests, code styling, comments. --- .../html-api/class-wp-html-processor.php | 19 ++-- .../tests/html-api/wpHtmlProcessor.php | 88 ++++++++++++------- .../html-api/wpHtmlProcessorSemanticRules.php | 39 ++------ 3 files changed, 67 insertions(+), 79 deletions(-) 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 e96e9d57d4fb0..34443a50bb5dd 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -102,17 +102,17 @@ * - Containers: ADDRESS, BLOCKQUOTE, DETAILS, DIALOG, DIV, FOOTER, HEADER, MAIN, MENU, SPAN, SUMMARY. * - Custom elements: All custom elements are supported. :) * - Form elements: BUTTON, DATALIST, FIELDSET, LABEL, LEGEND, METER, PROGRESS, SEARCH. - * - Formatting elements: B, BIG, CODE, EM, FONT, I, SMALL, STRIKE, STRONG, TT, U. + * - Formatting elements: B, BIG, CODE, EM, FONT, I, SMALL, STRIKE, STRONG, TT, U, WBR. * - Heading elements: H1, H2, H3, H4, H5, H6, HGROUP. * - Links: A. * - Lists: DD, DL, DT, LI, OL, LI. - * - Media elements: AUDIO, CANVAS, FIGCAPTION, FIGURE, IMG, MAP, PICTURE, VIDEO. - * - Paragraph: P. - * - Phrasing elements: ABBR, BDI, BDO, CITE, DATA, DEL, DFN, INS, MARK, OUTPUT, Q, SAMP, SUB, SUP, TIME, VAR. + * - Media elements: AUDIO, CANVAS, EMBED, FIGCAPTION, FIGURE, IMG, MAP, PICTURE, VIDEO. + * - Paragraph: BR, P. + * - Phrasing elements: AREA, ABBR, BDI, BDO, CITE, DATA, DEL, DFN, INS, MARK, OUTPUT, Q, SAMP, SUB, SUP, TIME, VAR. * - Sectioning elements: ARTICLE, ASIDE, HR, NAV, SECTION. * - Templating elements: SLOT. * - Text decoration: RUBY. - * - Deprecated elements: ACRONYM, BLINK, CENTER, DIR, ISINDEX, MULTICOL, NEXTID, SPACER. + * - Deprecated elements: ACRONYM, BLINK, CENTER, DIR, ISINDEX, KEYGEN, MULTICOL, NEXTID, SPACER. * * ### Supported markup * @@ -1704,16 +1704,11 @@ public static function is_void( $tag_name ) { 'IMG' === $tag_name || 'INPUT' === $tag_name || 'LINK' === $tag_name || + 'KEYGEN' === $tag_name || // Obsolete but still treated as void. 'META' === $tag_name || 'SOURCE' === $tag_name || 'TRACK' === $tag_name || - 'WBR' === $tag_name || - - // Obsolete - // - // This does not appear in https://html.spec.whatwg.org/#void-elements - // but it was a void tag and browsers treat it as such. - 'KEYGEN' === $tag_name + 'WBR' === $tag_name ); } diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor.php b/tests/phpunit/tests/html-api/wpHtmlProcessor.php index a995f33f3ecfc..c556d7e36a76f 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessor.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessor.php @@ -133,7 +133,7 @@ public function test_fails_to_reconstruct_formatting_elements() { } /** - * Ensure non-nesting tags do not nest + * Ensure non-nesting tags do not nest. * * @ticket 60283 * @@ -141,31 +141,51 @@ public function test_fails_to_reconstruct_formatting_elements() { * @covers WP_HTML_Processor::is_void * * @dataProvider data_void_tags + * + * @param string $tag_name Name of void tag under test. */ - public function test_cannot_nest_void_tags( $tag ) { - $p = WP_HTML_Processor::create_fragment( "{$tag}
" ); + public function test_cannot_nest_void_tags( $tag_name ) { + $processor = WP_HTML_Processor::create_fragment( "<{$tag_name}>
" ); - // We should have this structure: - // - // - // <$TAG /> - // <$TAG /> - // - // - // - // The breadcrumbs should be: - // HTML > BODY > $TAG + /* + * This HTML represents the same as the following HTML, + * assuming that it were provided `` as the tag: + * + * + * + * + *
+ * + * + */ - $result = $p->next_tag(); + $found_tag = $processor->next_tag(); - if ( WP_HTML_Processor::ERROR_UNSUPPORTED === $p->get_last_error() ) { - $this->markTestSkipped( "{$tag} is unsupported." ); + if ( WP_HTML_Processor::ERROR_UNSUPPORTED === $processor->get_last_error() ) { + $this->markTestSkipped( "Tag {$tag_name} is not supported." ); } - $this->assertTrue( $result, "Could not find first {$tag}." ); - $this->assertCount( 3, $p->get_breadcrumbs(), "{$tag} was not nested correctly." ); - $this->assertTrue( $p->next_tag( 'DIV' ), 'Could not find
tag.' ); - $this->assertCount( 3, $p->get_breadcrumbs(), 'Following
was not nested correctly.' ); + $this->assertTrue( + $found_tag, + "Could not find first {$tag_name}." + ); + + $this->assertSame( + array( 'HTML', 'BODY', $tag_name ), + $processor->get_breadcrumbs(), + 'Found incorrect nesting of first element.' + ); + + $this->assertTrue( + $processor->next_tag(), + 'Should have found the DIV as the second tag.' + ); + + $this->assertSame( + array( 'HTML', 'BODY', 'DIV' ), + $processor->get_breadcrumbs(), + "DIV should have been a sibling of the {$tag_name}." + ); } /** @@ -175,20 +195,20 @@ public function test_cannot_nest_void_tags( $tag ) { */ public function data_void_tags() { return array( - 'AREA' => array( '' ), - 'BASE' => array( '' ), - 'BR' => array( '
' ), - 'COL' => array( '' ), - 'EMBED' => array( '' ), - 'HR' => array( '
' ), - 'IMG' => array( '' ), - 'INPUT' => array( '' ), - 'KEYGEN' => array( '' ), - 'LINK' => array( '' ), - 'META' => array( '' ), - 'SOURCE' => array( '' ), - 'TRACK' => array( '' ), - 'WBR' => array( '' ), + 'AREA' => array( 'AREA' ), + 'BASE' => array( 'BASE' ), + 'BR' => array( 'BR' ), + 'COL' => array( 'COL' ), + 'EMBED' => array( 'EMBED' ), + 'HR' => array( 'HR' ), + 'IMG' => array( 'IMG' ), + 'INPUT' => array( 'INPUT' ), + 'KEYGEN' => array( 'KEYGEN' ), + 'LINK' => array( 'LINK' ), + 'META' => array( 'META' ), + 'SOURCE' => array( 'SOURCE' ), + 'TRACK' => array( 'TRACK' ), + 'WBR' => array( 'WBR' ), ); } diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php index e3b1458ee0a95..8397c41194cde 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php @@ -394,21 +394,22 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element } /** - * Verifies that when "in body" and encountering a BR end tag `
`: + * Ensures that support isn't accidentally partially added for the closing BR tag `
`. + * + * This tag closer has special rules and support shouldn't be added without implementing full support. * * > An end tag whose tag name is "br" * > Parse error. Drop the attributes from the token, and act as described in the next entry; * > i.e. act as if this was a "br" start tag token with no attributes, rather than the end * > tag token that it actually is. * - * When this handling is implemented, this test should be removed and - * `test_br_end_tag_special_behavior` should not be marked incomplete. + * When this handling is implemented, this test should be removed and `test_br_end_tag_special_behavior` + * should not be marked incomplete. It's not incorporated into the existing unsupported tag behavior test + * because the opening tag is supported; only the closing tag isn't. * * @covers WP_HTML_Processor::step_in_body * * @ticket 60283 - * - * @since 6.4.0 */ public function test_br_end_tag_unsupported() { $p = WP_HTML_Processor::create_fragment( '
' ); @@ -416,32 +417,4 @@ public function test_br_end_tag_unsupported() { $this->assertFalse( $p->next_tag(), 'Found a BR tag that should not be handled.' ); $this->assertSame( WP_HTML_Processor::ERROR_UNSUPPORTED, $p->get_last_error() ); } - - /** - * Verifies that when "in body" and encountering a BR end tag `
`: - * - * > An end tag whose tag name is "br" - * > Parse error. Drop the attributes from the token, and act as described in the next entry; - * > i.e. act as if this was a "br" start tag token with no attributes, rather than the end - * > tag token that it actually is. - * - * @covers WP_HTML_Processor::step_in_body - * - * @ticket 60283 - * - * @since 6.4.0 - */ - public function test_br_end_tag_special_behavior() { - $this->markTestIncomplete( 'BR end tag special handling is unimplemented' ); - - $p = WP_HTML_Processor::create_fragment( '
' ); - - $this->assertTrue( $p->next_tag(), 'No BR tag found.' ); - $this->assertFalse( $p->is_tag_closer(), '
should not be treated as an end tag.' ); - $this->assertNull( $p->get_attribute_names_with_prefix( '' ), 'BR end tag had attributes.' ); - $this->assertFalse( $p->set_attribute( 'new-attribute', 'added' ), 'BR end tag becomes an opener' ); - $this->assertCount( 1, $p->get_attribute_names_with_prefix( '' ), 'Tag should have 1 attribute.' ); - $this->assertSame( 'added', $p->get_attribute( 'new-attribute' ), 'Tag did not set attribute value correctly.' ); - $this->assertSame( '
', $p->get_updated_html(), 'Tag HTML was not updated correctly.' ); - } } From 15658e26c2933efaf50022d74f32c6a65e1b1f32 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 19 Jan 2024 14:24:37 -0700 Subject: [PATCH 10/10] Remove superfluous mention of being "incomplete" in test comment. --- .../phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php index 8397c41194cde..c0bd9d9c75084 100644 --- a/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php +++ b/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php @@ -403,9 +403,9 @@ public function test_in_body_any_other_end_tag_with_unclosed_non_special_element * > i.e. act as if this was a "br" start tag token with no attributes, rather than the end * > tag token that it actually is. * - * When this handling is implemented, this test should be removed and `test_br_end_tag_special_behavior` - * should not be marked incomplete. It's not incorporated into the existing unsupported tag behavior test - * because the opening tag is supported; only the closing tag isn't. + * When this handling is implemented, this test should be removed. It's not incorporated + * into the existing unsupported tag behavior test because the opening tag is supported; + * only the closing tag isn't. * * @covers WP_HTML_Processor::step_in_body *