Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/wp-includes/html-api/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -3337,7 +3337,34 @@ public function has_self_closing_flag(): bool {
* <figure />
* ^ this appears one character before the end of the closing ">".
*/
return '/' === $this->html[ $this->token_starts_at + $this->token_length - 2 ];
$self_closing_flag_at = $this->token_starts_at + $this->token_length - 2;
if ( '/' !== $this->html[ $self_closing_flag_at ] ) {
return false;
}

foreach ( $this->attributes as $attribute ) {
$attribute_ends_at = $attribute->start + $attribute->length;
if (
$self_closing_flag_at >= $attribute->start &&
$self_closing_flag_at < $attribute_ends_at
) {
return false;
}
}

foreach ( $this->duplicate_attributes ?? array() as $duplicate_attributes ) {
foreach ( $duplicate_attributes as $attribute ) {
$attribute_ends_at = $attribute->start + $attribute->length;
if (
$self_closing_flag_at >= $attribute->start &&
$self_closing_flag_at < $attribute_ends_at
) {
return false;
}
}
}

return true;
}

/**
Expand Down
32 changes: 32 additions & 0 deletions tests/phpunit/tests/html-api/wpHtmlProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,38 @@ public function test_expects_closer_foreign_content_self_closing() {
$this->assertTrue( $processor->expects_closer() );
}

/**
* Ensures a trailing slash in an unquoted attribute value does not close foreign content.
*
* @ticket 61576
*/
public function test_trailing_slash_in_unquoted_attribute_value_does_not_self_close_foreign_content() {
$processor = WP_HTML_Processor::create_fragment( '<math><mi disabled=abc/>text</math>' );

$this->assertTrue( $processor->next_tag( 'MI' ), 'Could not find MI tag: check test setup.' );
$this->assertSame(
'abc/',
$processor->get_attribute( 'disabled' ),
'Trailing slash in unquoted attribute value should belong to the attribute value.'
);
$this->assertFalse(
$processor->has_self_closing_flag(),
'Trailing slash in unquoted attribute value should not be interpreted as a self-closing flag.'
);
$this->assertTrue(
$processor->expects_closer(),
'MI with a trailing slash in an unquoted attribute value should still expect a closer.'
);

$this->assertTrue( $processor->next_token(), 'Could not find text following MI tag: check test setup.' );
$this->assertSame( '#text', $processor->get_token_name(), 'Should have found the text node following the MI tag.' );
$this->assertSame(
array( 'HTML', 'BODY', 'MATH', 'MI', '#text' ),
$processor->get_breadcrumbs(),
'Text following the MI tag should remain inside the MI element.'
);
}

/**
* Ensures that expects_closer works for void-like elements in foreign content.
*
Expand Down
27 changes: 26 additions & 1 deletion tests/phpunit/tests/html-api/wpHtmlTagProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,38 @@ public static function data_has_self_closing_flag() {
'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 attribute' => array( '<div id=test />', true ),
'Slash inside unquoted attribute value' => array( '<div id=test/>', false ),
'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 ),
);
}

/**
* Ensures a trailing slash in an unquoted attribute value is part of the value.
*
* @ticket 61576
*
* @covers WP_HTML_Tag_Processor::get_attribute
* @covers WP_HTML_Tag_Processor::has_self_closing_flag
*/
public function test_trailing_slash_in_unquoted_attribute_value_is_not_self_closing_flag() {
$processor = new WP_HTML_Tag_Processor( '<mi disabled=abc/>text' );
$this->assertTrue( $processor->next_tag(), 'Could not find MI tag: check test setup.' );

$this->assertSame(
'abc/',
$processor->get_attribute( 'disabled' ),
'Trailing slash in unquoted attribute value should belong to the attribute value.'
);

$this->assertFalse(
$processor->has_self_closing_flag(),
'Trailing slash in unquoted attribute value should not be interpreted as a self-closing flag.'
);
}

/**
* @ticket 56299
*
Expand Down
Loading