diff --git a/src/wp-includes/html-api/class-wp-css-selectors.php b/src/wp-includes/html-api/class-wp-css-selectors.php
index 45a2f78d94fd5..bc28cfaa4f20e 100644
--- a/src/wp-includes/html-api/class-wp-css-selectors.php
+++ b/src/wp-includes/html-api/class-wp-css-selectors.php
@@ -1033,11 +1033,47 @@ private static function parse_subclass_selector( string $input, int &$offset ) {
*/
final class WP_CSS_Complex_Selector extends WP_CSS_Selector_Parser {
public function matches( WP_HTML_Processor $processor ): bool {
- // @todo this can throw on parse.
- if ( count( $this->selectors ) > 1 ) {
- throw new Exception( 'Combined complex selectors are not supported yet.' );
+ if ( count( $this->selectors ) === 1 ) {
+ return $this->selectors[0]->matches( $processor );
+ }
+
+ // First selector must match this location.
+ if ( ! $this->selectors[0]->matches( $processor ) ) {
+ return false;
+ }
+
+ $breadcrumbs = array_slice( array_reverse( $processor->get_breadcrumbs() ), 1 );
+ $selectors = array_slice( $this->selectors, 1 );
+ return $this->explore_matches( $selectors, $breadcrumbs );
+ }
+
+ /**
+ * This only looks at breadcrumbs and can therefore only support type selectors.
+ *
+ * @param array $selectors
+ */
+ private function explore_matches( array $selectors, array $breadcrumbs ): bool {
+ if ( array() === $selectors ) {
+ return true;
+ }
+ if ( array() === $breadcrumbs ) {
+ return false;
+ }
+
+ $combinator = $selectors[0];
+ $selector = $selectors[1];
+
+ switch ( $combinator ) {
+ case self::COMBINATOR_CHILD:
+ if ( '*' === $selector->type_selector->ident || strcasecmp( $breadcrumbs[0], $selector->type_selector->ident ) === 0 ) {
+ return $this->explore_matches( array_slice( $selectors, 2 ), array_slice( $breadcrumbs, 1 ) );
+ }
+ return $this->explore_matches( $selectors, array_slice( $breadcrumbs, 1 ) );
+
+ case self::COMBINATOR_DESCENDANT:
+ default:
+ throw new Exception( "Combinator '{$combinator}' is not supported yet." );
}
- return $this->selectors[0]->matches( $processor );
}
const COMBINATOR_CHILD = '>';
@@ -1047,12 +1083,15 @@ public function matches( WP_HTML_Processor $processor ): bool {
/**
* even indexes are WP_CSS_Selector, odd indexes are string combinators.
+ * In reverse order to match the current element and then work up the tree.
+ * Any non-final selector is a type selector.
+ *
* @var array
*/
public $selectors = array();
private function __construct( array $selectors ) {
- $this->selectors = $selectors;
+ $this->selectors = array_reverse( $selectors );
}
public static function parse( string $input, int &$offset ): ?self {