Skip to content

Commit 6b45967

Browse files
committed
Move class constant into Tag Processor, match with it, and add tests
1 parent f93ef13 commit 6b45967

File tree

4 files changed

+115
-22
lines changed

4 files changed

+115
-22
lines changed

src/wp-includes/html-api/class-wp-html-open-elements.php

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public function current_node() {
106106
*
107107
* @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope
108108
*
109-
* @param string $tag_name Name of tag check, or the class constant HEADING_ELEMENTS to specify H1-H6.
109+
* @param string $tag_name Name of tag check, or WP_HTML_Tag_Processor::H1_H6_ELEMENTS for any H1 - H6.
110110
* @param string[] $termination_list List of elements that terminate the search.
111111
* @return bool Whether the element was found in a specific scope.
112112
*/
@@ -117,7 +117,7 @@ public function has_element_in_specific_scope( $tag_name, $termination_list ) {
117117
}
118118

119119
if (
120-
self::HEADING_ELEMENTS === $tag_name &&
120+
WP_HTML_Tag_Processor::H1_H6_ELEMENTS === $tag_name &&
121121
in_array( $node->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
122122
) {
123123
return true;
@@ -271,15 +271,15 @@ public function pop() {
271271
* @see WP_HTML_Open_Elements::pop
272272
*
273273
* @param string $tag_name Name of tag that needs to be popped off of the stack of open elements,
274-
* or the class constant HEADING_ELEMENTS to specify any of H1-H6.
274+
* or WP_HTML_Tag_Processor::H1_H6_ELEMENTS to specify any of H1 - H6.
275275
* @return bool Whether a tag of the given name was found and popped off of the stack of open elements.
276276
*/
277277
public function pop_until( $tag_name ) {
278278
foreach ( $this->walk_up() as $item ) {
279279
$this->pop();
280280

281281
if (
282-
self::HEADING_ELEMENTS === $tag_name &&
282+
WP_HTML_Tag_Processor::H1_H6_ELEMENTS === $tag_name &&
283283
in_array( $item->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
284284
) {
285285
return true;
@@ -444,15 +444,4 @@ public function after_element_pop( $item ) {
444444
break;
445445
}
446446
}
447-
448-
/**
449-
* Represents the collection of H1-H6 elements.
450-
*
451-
* @since 6.5.0
452-
*
453-
* @see has_element_in_scope()
454-
*
455-
* @var string
456-
*/
457-
const HEADING_ELEMENTS = 'heading-elements';
458447
}

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -733,11 +733,7 @@ private function step_in_body() {
733733
case '-H4':
734734
case '-H5':
735735
case '-H6':
736-
if (
737-
! $this->state->stack_of_open_elements->has_element_in_scope(
738-
WP_HTML_Open_Elements::HEADING_ELEMENTS
739-
)
740-
) {
736+
if ( ! $this->state->stack_of_open_elements->has_element_in_scope( WP_HTML_Tag_Processor::H1_H6_ELEMENTS ) ) {
741737
/*
742738
* This is a parse error; ignore the token.
743739
*
@@ -752,7 +748,7 @@ private function step_in_body() {
752748
// @TODO: Record parse error: this error doesn't impact parsing.
753749
}
754750

755-
$this->state->stack_of_open_elements->pop_until( WP_HTML_Open_Elements::HEADING_ELEMENTS );
751+
$this->state->stack_of_open_elements->pop_until( WP_HTML_Tag_Processor::H1_H6_ELEMENTS );
756752
return true;
757753

758754
/*

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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,7 @@ private function parse_query( $query ) {
24342434
* Checks whether a given tag and its attributes match the search criteria.
24352435
*
24362436
* @since 6.2.0
2437+
* @since 6.5.0 Allows matching any of the H1 - H6 tag names with the WP_HTML_Tag_Processor::H1_H6_ELEMENTS constant.
24372438
*
24382439
* @return bool Whether the given tag and its attribute match the search criteria.
24392440
*/
@@ -2442,8 +2443,31 @@ private function matches() {
24422443
return false;
24432444
}
24442445

2446+
24452447
// Does the tag name match the requested tag name in a case-insensitive manner?
2446-
if ( null !== $this->sought_tag_name ) {
2448+
if ( self::H1_H6_ELEMENTS === $this->sought_tag_name ) {
2449+
/*
2450+
* H1 through H6 are special because they act like one tag
2451+
* name but are distinct. It's common enough to want to stop
2452+
* at any of them without knowing in advance which one to
2453+
* look for; the class constant aids this by representing
2454+
* the entire set of six elements: H1, H2, H3, H4, H5, H6.
2455+
*/
2456+
2457+
if ( 2 !== $this->tag_name_length ) {
2458+
return false;
2459+
}
2460+
2461+
$c = $this->html[ $this->tag_name_starts_at ];
2462+
if ( 'h' !== $c && 'H' !== $c ) {
2463+
return false;
2464+
}
2465+
2466+
$c = $this->html[ $this->tag_name_starts_at + 1 ];
2467+
if ( '1' !== $c && '2' !== $c && '3' !== $c && '4' !== $c && '5' !== $c && '6' !== $c ) {
2468+
return false;
2469+
}
2470+
} elseif ( null !== $this->sought_tag_name ) {
24472471
/*
24482472
* String (byte) length lookup is fast. If they aren't the
24492473
* same length then they can't be the same string values.
@@ -2480,4 +2504,17 @@ private function matches() {
24802504

24812505
return true;
24822506
}
2507+
2508+
// Class constants that would otherwise be distracting if found at the top of the document.
2509+
2510+
/**
2511+
* Represents the collection of H1-H6 elements.
2512+
*
2513+
* @since 6.5.0
2514+
*
2515+
* @see has_element_in_scope()
2516+
*
2517+
* @var string
2518+
*/
2519+
const H1_H6_ELEMENTS = 'Match on any of H1, H2, H3, H4, H5, H6';
24832520
}

tests/phpunit/tests/html-api/wpHtmlTagProcessor.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,77 @@ public function test_next_tag_matches_decoded_class_names() {
509509
$this->assertTrue( $p->next_tag( array( 'class_name' => '<egg>' ) ), 'Failed to find tag with HTML-encoded class name.' );
510510
}
511511

512+
/**
513+
* Ensures that the H1_H6_ELEMENTS constant leads to matches for H1 through H6 elements.
514+
*
515+
* @ticket {TICKET_NUMBER}
516+
*
517+
* @covers WP_HTML_Tag_Processor::next_tag
518+
*
519+
* @dataProvider data_h_tag_names
520+
*
521+
* @param string $h_tag_name One of H1 through H6, case-insensitive.
522+
*/
523+
public function test_next_tag_matches_h1_through_h6_with_the_class_constant( $h_tag_name ) {
524+
$p = new WP_HTML_Tag_Processor( "<div><img><{$h_tag_name}></div>" );
525+
526+
$this->assertTrue( $p->next_tag( WP_HTML_Tag_Processor::H1_H6_ELEMENTS ), "Failed to find {$h_tag_name} tag opener." );
527+
}
528+
529+
/**
530+
* Data provider.
531+
*
532+
* @return array[]
533+
*/
534+
public function data_h_tag_names() {
535+
return array(
536+
'H1' => array( 'H1' ),
537+
'H2' => array( 'H2' ),
538+
'H3' => array( 'H3' ),
539+
'H4' => array( 'H4' ),
540+
'H5' => array( 'H5' ),
541+
'H6' => array( 'H6' ),
542+
);
543+
}
544+
545+
/**
546+
* Ensures that the H1_H6_ELEMENTS constant doesn't lead to matches on
547+
* tag names that look similar to H1 - H6 but aren't those elements.
548+
*
549+
* @ticket {TICKET_NUMBER}
550+
*
551+
* @covers WP_HTML_Tag_Processor::next_tag
552+
*
553+
* @dataProvider data_invalid_h_tag_names
554+
*
555+
* @param string $invalid_h_tag_name Tag names that look like H1 through H6 but are not those tag names.
556+
*/
557+
public function test_next_tag_does_not_match_invalid_h_elements_with_the_class_constant( $invalid_h_tag_name ) {
558+
$p = new WP_HTML_Tag_Processor( "<div><img><{$invalid_h_tag_name}></div>" );
559+
560+
$this->assertFalse( $p->next_tag( WP_HTML_Tag_Processor::H1_H6_ELEMENTS ), "Found {$p->get_tag()} when looking for {$invalid_h_tag_name} element and should have found nothing." );
561+
}
562+
563+
/**
564+
* Data provider.
565+
*
566+
* @return array[].
567+
*/
568+
public function data_invalid_h_tag_names() {
569+
return array(
570+
'H0' => array( 'H0' ),
571+
'H7' => array( 'H7' ),
572+
'H13' => array( 'H13' ),
573+
574+
/*
575+
* Preserve the FULLWIDTH DIGIT SIX key because PHPUnit interprets '6' as
576+
* a numeric array item and reports "data set 0" instead of "6".
577+
*/
578+
'' => array( '6' ),
579+
'H4-CUSTOM' => array( 'H4-CUSTOM' ),
580+
);
581+
}
582+
512583
/**
513584
* @ticket 56299
514585
* @ticket 57852

0 commit comments

Comments
 (0)