Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Working and tested
Browse files Browse the repository at this point in the history
sirreal committed Nov 28, 2024

Verified

This commit was signed with the committer’s verified signature.
sirreal Jon Surrell
1 parent 8d92994 commit 370539b
Showing 2 changed files with 113 additions and 37 deletions.
83 changes: 48 additions & 35 deletions src/wp-includes/html-api/class-wp-css-selectors.php
Original file line number Diff line number Diff line change
@@ -117,21 +117,31 @@ private static function parse( string $input ) {
$input = str_replace( array( "\r", "\f" ), "\n", $input );
$input = str_replace( "\0", "\u{FFFD}", $input );

$length = strlen( $input );
$selectors = array();

$offset = 0;

while ( $offset < $length ) {
$selector = WP_CSS_ID_Selector::parse( $input, $offset );
if ( null !== $selector ) {
$selectors[] = $selector;
}
$selector = WP_CSS_Complex_Selector::parse( $input, $offset );
if ( null === $selector ) {
return null;
}
if ( count( $selectors ) ) {
return new WP_CSS_Selector_List( $selectors );
WP_CSS_Selector_Parser::parse_whitespace( $input, $offset );

$selectors = array( $selector );
while ( $offset < strlen( $input ) ) {
// Each loop should stop on a `,` selector list delimiter.
if ( ',' !== $input[ $offset ] ) {
return null;
}
++$offset;
WP_CSS_Selector_Parser::parse_whitespace( $input, $offset );
$selector = WP_CSS_Complex_Selector::parse( $input, $offset );
if ( null === $selector ) {
return null;
}
$selectors[] = $selector;
WP_CSS_Selector_Parser::parse_whitespace( $input, $offset );
}
return null;

return new WP_CSS_Selector_List( $selectors );
}
}

@@ -145,7 +155,7 @@ public static function parse( string $input, int &$offset );
abstract class WP_CSS_Selector_Parser implements IWP_CSS_Selector_Parser {
const UTF8_MAX_CODEPOINT_VALUE = 0x10FFFF;

protected static function parse_whitespace( string $input, int &$offset ): bool {
public static function parse_whitespace( string $input, int &$offset ): bool {
$length = strspn( $input, " \t\r\n\f", $offset );
$advanced = $length > 0;
$offset += $length;
@@ -938,35 +948,38 @@ public static function parse( string $input, int &$offset ): ?self {

$found_whitespace = self::parse_whitespace( $input, $updated_offset );
while ( $updated_offset < strlen( $input ) ) {
switch ( $input[ $updated_offset ] ) {
case self::COMBINATOR_CHILD:
case self::COMBINATOR_NEXT_SIBLING:
case self::COMBINATOR_SUBSEQUENT_SIBLING:
if (
self::COMBINATOR_CHILD === $input[ $updated_offset ] ||
self::COMBINATOR_NEXT_SIBLING === $input[ $updated_offset ] ||
self::COMBINATOR_SUBSEQUENT_SIBLING === $input[ $updated_offset ]
) {
$combinator = $input[ $updated_offset ];
++$updated_offset;
self::parse_whitespace( $input, $updated_offset );
break;

default:
/*
* Whitespace is a descendant combinator.
* Either whitespace was found and we're on a selector,
* or we've failed to find any combinator and parsing is complete.
*/
if ( ! $found_whitespace ) {
break 2;
}
$combinator = self::COMBINATOR_DESCENDANT;
// Failure to find a selector here is a parse error
$selector = WP_CSS_Selector::parse( $input, $updated_offset );
// Failure to find a selector is a parse error.
if ( null === $selector ) {
return null;
}
$selectors[] = $combinator;
$selectors[] = $selector;
} elseif ( ! $found_whitespace ) {
break;
} else {

/*
* Whitespace is ambiguous, it could be a descendant combinator or
* insignificant whitespace.
*/
$selector = WP_CSS_Selector::parse( $input, $updated_offset );
if ( null === $selector ) {
break;
}
$selectors[] = self::COMBINATOR_DESCENDANT;
$selectors[] = $selector;
}
// Here we've found a combinator and need another selector.
$selector = WP_CSS_Selector::parse( $input, $updated_offset );
// Failure to find a selector is a parse error.
if ( null === $selector ) {
return null;
}
$selectors[] = $combinator;
$selectors[] = $selector;
$found_whitespace = self::parse_whitespace( $input, $updated_offset );
}
$offset = $updated_offset;
67 changes: 65 additions & 2 deletions tests/phpunit/tests/html-api/wpCssSelectors.php
Original file line number Diff line number Diff line change
@@ -357,15 +357,24 @@ public function test_parse_selector() {
$this->assertSame( ' > .child', substr( $input, $offset ) );
}

/**
* @ticket TBD
*/
public function test_parse_empty_selector() {
$input = '';
$offset = 0;
$result = WP_CSS_Selector::parse( $input, $offset );
$this->assertNull( $result );
}

/**
* @ticket TBD
*/
public function test_parse_complex_selector() {
$input = 'el.foo#bar[baz=quux] > .child, rest';
$input = 'el.foo#bar[baz=quux] > .child , rest';
$offset = 0;
$sel = WP_CSS_Complex_Selector::parse( $input, $offset );

var_dump( $sel );
$this->assertSame( 3, count( $sel->selectors ) );
$this->assertNotNull( $sel->selectors[0]->type_selector );
$this->assertSame( 3, count( $sel->selectors[0]->subclass_selectors ) );
@@ -376,4 +385,58 @@ public function test_parse_complex_selector() {

$this->assertSame( ', rest', substr( $input, $offset ) );
}

/**
* @ticket TBD
*/
public function test_parse_invalid_complex_selector() {
$input = 'el.foo#bar[baz=quux] > , rest';
$offset = 0;
$result = WP_CSS_Complex_Selector::parse( $input, $offset );
$this->assertNull( $result );
}

public function test_parse_empty_complex_selector() {
$input = '';
$offset = 0;
$result = WP_CSS_Complex_Selector::parse( $input, $offset );
$this->assertNull( $result );
}


/**
* @ticket TBD
*/
public function test_parse_selector_list() {
$input = 'el.foo#bar[baz=quux] .descendent , rest';
$result = WP_CSS_Selector_List::from_selectors( $input );
$this->assertNotNull( $result );
}

/**
* @ticket TBD
*/
public function test_parse_invalid_selector_list() {
$input = 'el,,';
$result = WP_CSS_Selector_List::from_selectors( $input );
$this->assertNull( $result );
}

/**
* @ticket TBD
*/
public function test_parse_invalid_selector_list2() {
$input = 'el!';
$result = WP_CSS_Selector_List::from_selectors( $input );
$this->assertNull( $result );
}

/**
* @ticket TBD
*/
public function test_parse_empty_selector_list() {
$input = " \t \t\n\r\f";
$result = WP_CSS_Selector_List::from_selectors( $input );
$this->assertNull( $result );
}
}

0 comments on commit 370539b

Please sign in to comment.