@@ -48,6 +48,7 @@ import org.htmlunit.cssparser.parser.condition.ClassCondition;
4848import org.htmlunit.cssparser.parser.condition.Condition;
4949import org.htmlunit.cssparser.parser.condition.IdCondition;
5050import org.htmlunit.cssparser.parser.condition.LangCondition;
51+ import org.htmlunit.cssparser.parser.condition.HasPseudoClassCondition;
5152import org.htmlunit.cssparser.parser.condition.IsPseudoClassCondition;
5253import org.htmlunit.cssparser.parser.condition.NotPseudoClassCondition;
5354import org.htmlunit.cssparser.parser.condition.OneOfAttributeCondition;
@@ -63,6 +64,7 @@ import org.htmlunit.cssparser.parser.selector.DirectAdjacentSelector;
6364import org.htmlunit.cssparser.parser.selector.ElementSelector;
6465import org.htmlunit.cssparser.parser.selector.GeneralAdjacentSelector;
6566import org.htmlunit.cssparser.parser.selector.PseudoElementSelector;
67+ import org.htmlunit.cssparser.parser.selector.RelativeSelector;
6668import org.htmlunit.cssparser.parser.selector.Selector;
6769import org.htmlunit.cssparser.parser.selector.SelectorList;
6870import org.htmlunit.cssparser.parser.selector.SelectorListImpl;
@@ -427,6 +429,7 @@ TOKEN_MGR_DECLS :
427429| < FUNCTION_LCH: ("ok")? "lch" <LROUND> >
428430
429431| < FUNCTION_IS: "is" <LROUND> >
432+ | < FUNCTION_HAS: "has" <LROUND> >
430433
431434| < CUSTOM_PROPERTY_NAME: < MINUS > <MINUS > <NMSTART> ( <NMCHAR> )* >
432435
@@ -1006,6 +1009,19 @@ char combinator() :
10061009 { return c; }
10071010}
10081011
1012+ char combinatorWithoutWhitespace() :
1013+ {
1014+ char c = ' ';
1015+ }
1016+ {
1017+ (
1018+ <PLUS> { c='+'; } ( <S> )*
1019+ | <GREATER> { c='>'; } ( <S> )*
1020+ | <TILDE> { c='~'; } ( <S> )*
1021+ )
1022+ { return c; }
1023+ }
1024+
10091025//
10101026// unary_operator
10111027// : '-' | PLUS
@@ -1093,6 +1109,27 @@ SelectorList selectorList() :
10931109 }
10941110}
10951111
1112+ SelectorList relativeSelectorList() :
1113+ {
1114+ SelectorListImpl selList = new SelectorListImpl();
1115+ Selector sel;
1116+ char comb;
1117+ }
1118+ {
1119+ comb = combinatorWithoutWhitespace()
1120+ sel = selector() { selList.setLocator(sel.getLocator()); }
1121+ ( <COMMA> ( <S> )*
1122+ { selList.add(new RelativeSelector(comb, sel)); }
1123+
1124+ comb = combinatorWithoutWhitespace()
1125+ sel = selector() { selList.setLocator(sel.getLocator()); }
1126+ )*
1127+ {
1128+ selList.add(new RelativeSelector(comb, sel));
1129+ return selList;
1130+ }
1131+ }
1132+
10961133//
10971134// selector
10981135// : simple_selector_sequence [ combinator simple_selector_sequence ]*
@@ -1367,6 +1404,7 @@ Object pseudo(boolean pseudoElementFound) :
13671404 String function;
13681405 boolean doubleColon = false;
13691406 SelectorList selectorList;
1407+ SelectorList relativeSelectorList;
13701408 Locator locator;
13711409}
13721410{
@@ -1412,6 +1450,17 @@ Object pseudo(boolean pseudoElementFound) :
14121450 }
14131451 )
14141452 |
1453+ (
1454+ t = <FUNCTION_HAS> { function = unescape(t.image, false); }
1455+ ( <S> )*
1456+ relativeSelectorList = relativeSelectorList()
1457+ <RROUND>
1458+ {
1459+ if (pseudoElementFound) { throw toCSSParseException("duplicatePseudo", new String[] { function + relativeSelectorList + ")" }, locator); }
1460+ return new HasPseudoClassCondition(relativeSelectorList, locator, doubleColon);
1461+ }
1462+ )
1463+ |
14151464 (
14161465 t = <FUNCTION_LANG> { function = unescape(t.image, false); }
14171466 ( <S> )*
0 commit comments