HTML API: Add has_self_closing_flag() to Tag Processor.

In this patch we're adding `has_self_closing_flag()` to the HTML Tag Processor.
This exposes whether a currently-matched tag contains the self-closing flag `/`.

This information is critical for the evolution of the HTML API in order
to track and parse HTML structure, specifically, knowing whether an
HTML foreign element is self-closing or not.

Props dmsnell, zieladam.
Fixes #58009.

git-svn-id: https://develop.svn.wordpress.org/trunk@55619 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Bernie Reiter 2023-04-04 10:04:45 +00:00
parent b98b5e59d7
commit ba4709cdc2
2 changed files with 75 additions and 0 deletions

View File

@ -1759,6 +1759,31 @@ class WP_HTML_Tag_Processor {
return strtoupper( $tag_name );
}
/**
* Indicates if the currently matched tag contains the self-closing flag.
*
* No HTML elements ought to have the self-closing flag and for those, the self-closing
* flag will be ignored. For void elements this is benign because they "self close"
* automatically. For non-void HTML elements though problems will appear if someone
* intends to use a self-closing element in place of that element with an empty body.
* For HTML foreign elements and custom elements the self-closing flag determines if
* they self-close or not.
*
* This function does not determine if a tag is self-closing,
* but only if the self-closing flag is present in the syntax.
*
* @since 6.3.0
*
* @return bool Whether the currently matched tag contains the self-closing flag.
*/
public function has_self_closing_flag() {
if ( ! $this->tag_name_starts_at ) {
return false;
}
return '/' === $this->html[ $this->tag_ends_at - 1 ];
}
/**
* Indicates if the current tag token is a tag closer.
*

View File

@ -51,6 +51,56 @@ class Tests_HtmlApi_wpHtmlTagProcessor extends WP_UnitTestCase {
$this->assertSame( 'DIV', $p->get_tag(), 'Accessing an existing tag name did not return "div"' );
}
/**
* @ticket 58009
*
* @covers WP_HTML_Tag_Processor::has_self_closing_flag
*
* @dataProvider data_has_self_closing_flag
*
* @param string $html Input HTML whose first tag might contain the self-closing flag `/`.
* @param bool $flag_is_set Whether the input HTML's first tag contains the self-closing flag.
*/
public function test_has_self_closing_flag_matches_input_html( $html, $flag_is_set ) {
$p = new WP_HTML_Tag_Processor( $html );
$p->next_tag( array( 'tag_closers' => 'visit' ) );
if ( $flag_is_set ) {
$this->assertTrue( $p->has_self_closing_flag(), 'Did not find the self-closing tag when it was present.' );
} else {
$this->assertFalse( $p->has_self_closing_flag(), 'Found the self-closing tag when it was absent.' );
}
}
/**
* Data provider. HTML tags which might have a self-closing flag, and an indicator if they do.
*
* @return array[]
*/
public function data_has_self_closing_flag() {
return array(
// These should not have a self-closer, and will leave an element un-closed if it's assumed they are self-closing.
'Self-closing flag on non-void HTML element' => array( '<div />', true ),
'No self-closing flag on non-void HTML element' => array( '<div>', false ),
// These should not have a self-closer, but are benign when used because the elements are void.
'Self-closing flag on void HTML element' => array( '<img />', true ),
'No self-closing flag on void HTML element' => array( '<img>', false ),
'Self-closing flag on void HTML element without spacing' => array( '<img/>', true ),
// These should not have a self-closer, but as part of a tag closer they are entirely ignored.
'Self-closing flag on tag closer' => array( '</textarea />', true ),
'No self-closing flag on tag closer' => array( '</textarea>', false ),
// These can and should have self-closers, and will leave an element un-closed if it's assumed they aren't self-closing.
'Self-closing flag on a foreign element' => array( '<circle />', true ),
'No self-closing flag on a foreign element' => array( '<circle>', false ),
// These involve syntax peculiarities.
'Self-closing flag after extra spaces' => array( '<div />', true ),
'Self-closing flag after attribute' => array( '<div id=test/>', true ),
'Self-closing flag after quoted attribute' => array( '<div id="test"/>', true ),
'Self-closing flag after boolean attribute' => array( '<div enabled/>', true ),
'Boolean attribute that looks like a self-closer' => array( '<div / >', false ),
);
}
/**
* @ticket 56299
*