Title: [281295] trunk
Revision
281295
Author
[email protected]
Date
2021-08-19 22:15:47 -0700 (Thu, 19 Aug 2021)

Log Message

[:has() pseudo-class] Basic support
https://bugs.webkit.org/show_bug.cgi?id=228894

Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/css/selectors/has-basic-expected.txt:
* web-platform-tests/css/selectors/has-relative-argument-expected.txt:
* web-platform-tests/css/selectors/parsing/parse-has-expected.txt:
* web-platform-tests/dom/nodes/Element-closest-expected.txt:

Source/WebCore:

This patch adds basic support for :has() pseudo-class, https://drafts.csswg.org/selectors/#has-pseudo.
The initial implementation is very inefficient. There is no support for invalidation yet.

The feature is disabled by default.

* css/CSSSelector.cpp:
(WebCore::CSSSelector::selectorText const):

Serialization.

* css/CSSSelector.h:
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne const):

Selector matching using nested SelectorChecker.

* css/SelectorPseudoClassAndCompatibilityElementMap.in:
* css/parser/CSSParserContext.cpp:
(WebCore::operator==):
(WebCore::add):
* css/parser/CSSParserContext.h:
* css/parser/CSSParserSelector.h:
(WebCore::CSSParserSelector::setPseudoClassType):
* css/parser/CSSSelectorParser.cpp:
(WebCore::CSSSelectorParser::consumeForgivingSelectorList):

Add a template version of the forgiving parsing function.

(WebCore::CSSSelectorParser::consumeForgivingComplexSelectorList):

Use it for complex selector lists.

(WebCore::CSSSelectorParser::consumeForgivingRelativeSelectorList):

And the new relative selector lists.

(WebCore::CSSSelectorParser::consumeRelativeSelector):

Parse relative selectors like "> foo".

(WebCore::CSSSelectorParser::consumePseudo):
(WebCore::CSSSelectorParser::consumeComplexForgivingSelectorList): Deleted.
* css/parser/CSSSelectorParser.h:
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::addPseudoClassType):

No compiler support yet.

Source/WTF:

* Scripts/Preferences/WebPreferencesExperimental.yaml:

Add off-by-default HasPseudoClassEnabled preference value.

Modified Paths

Diff

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (281294 => 281295)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2021-08-20 05:15:47 UTC (rev 281295)
@@ -1,3 +1,15 @@
+2021-08-19  Antti Koivisto  <[email protected]>
+
+        [:has() pseudo-class] Basic support
+        https://bugs.webkit.org/show_bug.cgi?id=228894
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/css/selectors/has-basic-expected.txt:
+        * web-platform-tests/css/selectors/has-relative-argument-expected.txt:
+        * web-platform-tests/css/selectors/parsing/parse-has-expected.txt:
+        * web-platform-tests/dom/nodes/Element-closest-expected.txt:
+
 2021-08-19  Chris Dumez  <[email protected]>
 
         Implement Crypto.randomUUID()

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/has-basic-expected.txt (281294 => 281295)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/has-basic-expected.txt	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/has-basic-expected.txt	2021-08-20 05:15:47 UTC (rev 281295)
@@ -1,25 +1,25 @@
 
-FAIL :has(#a) matches expected elements The string did not match the expected pattern.
-FAIL :has(.ancestor) matches expected elements The string did not match the expected pattern.
-FAIL :has(.target) matches expected elements The string did not match the expected pattern.
-FAIL :has(.descendant) matches expected elements The string did not match the expected pattern.
-FAIL .parent:has(.target) matches expected elements The string did not match the expected pattern.
-FAIL :has(.sibling ~ .target) matches expected elements The string did not match the expected pattern.
-FAIL .parent:has(.sibling ~ .target) matches expected elements The string did not match the expected pattern.
-FAIL :has(:is(.target ~ .sibling .descendant)) matches expected elements The string did not match the expected pattern.
-FAIL .parent:has(:is(.target ~ .sibling .descendant)) matches expected elements The string did not match the expected pattern.
-FAIL .sibling:has(.descendant) ~ .target matches expected elements The string did not match the expected pattern.
-FAIL :has(.sibling:has(.descendant) ~ .target) matches expected elements The string did not match the expected pattern.
-FAIL :has(.sibling:has(.descendant) ~ .target) ~ .parent > .descendant matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope .target) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope > .parent) matches expected elements The string did not match the expected pattern.
-FAIL :has(> .target) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope > .target) matches expected elements The string did not match the expected pattern.
-FAIL :has(+ #h) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope + #h) matches expected elements The string did not match the expected pattern.
-FAIL .parent:has(~ #h) matches expected elements The string did not match the expected pattern.
-FAIL .parent:has(:scope ~ #h) matches expected elements The string did not match the expected pattern.
-FAIL .sibling:has(.descendant) matches expected element The string did not match the expected pattern.
-FAIL closest(.ancestor:has(.descendant)) returns expected element The string did not match the expected pattern.
-FAIL :has(.target ~ .sibling .descendant) matches expectedly The string did not match the expected pattern.
+PASS :has(#a) matches expected elements
+PASS :has(.ancestor) matches expected elements
+PASS :has(.target) matches expected elements
+PASS :has(.descendant) matches expected elements
+PASS .parent:has(.target) matches expected elements
+PASS :has(.sibling ~ .target) matches expected elements
+PASS .parent:has(.sibling ~ .target) matches expected elements
+PASS :has(:is(.target ~ .sibling .descendant)) matches expected elements
+PASS .parent:has(:is(.target ~ .sibling .descendant)) matches expected elements
+PASS .sibling:has(.descendant) ~ .target matches expected elements
+PASS :has(.sibling:has(.descendant) ~ .target) matches expected elements
+PASS :has(.sibling:has(.descendant) ~ .target) ~ .parent > .descendant matches expected elements
+PASS :has(:scope .target) matches expected elements
+PASS :has(:scope > .parent) matches expected elements
+PASS :has(> .target) matches expected elements
+PASS :has(:scope > .target) matches expected elements
+PASS :has(+ #h) matches expected elements
+PASS :has(:scope + #h) matches expected elements
+PASS .parent:has(~ #h) matches expected elements
+PASS .parent:has(:scope ~ #h) matches expected elements
+PASS .sibling:has(.descendant) matches expected element
+PASS closest(.ancestor:has(.descendant)) returns expected element
+PASS :has(.target ~ .sibling .descendant) matches expectedly
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/has-relative-argument-expected.txt (281294 => 281295)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/has-relative-argument-expected.txt	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/has-relative-argument-expected.txt	2021-08-20 05:15:47 UTC (rev 281295)
@@ -1,129 +1,129 @@
 
-FAIL .x:has(:scope .a) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope .a .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .a) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .a .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .a) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .a .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .a ~ .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .a) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .a .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .a + .b > .c) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .a + .b .c) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .a + .b > .c) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .a + .b .c) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .a) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .a > .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .a .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .a + .b) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .a + .b > .c) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .a + .b .c) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(.d .e) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(.d .e) .f matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope .d .e) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope .d .e) .f matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .d) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope > .d) .f matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .d ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .d ~ .e) ~ .f matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .d ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .d ~ .e) ~ .f matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .d .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope .d .e) .f matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .d) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope > .d) .f matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .d ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ .d ~ .e) ~ .f matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .d ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + .d ~ .e) ~ .f matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .d .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x .d .e) .f matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .d) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x > .d) .f matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .d ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ .d ~ .e) ~ .f matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .d ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + .d ~ .e) ~ .f matches expected elements The string did not match the expected pattern.
-FAIL .y:has(:scope > .g .h) matches expected elements The string did not match the expected pattern.
-FAIL .y:has(:scope .g .h) matches expected elements The string did not match the expected pattern.
-FAIL .y:has(:scope > .g .h) .i matches expected elements The string did not match the expected pattern.
-FAIL .y:has(:scope .g .h) .i matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .y:has(:scope > .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .y:has(:scope .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .y:has(:scope > .g .h) .i) ~ .j matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope + .y:has(:scope .g .h) .i) ~ .j matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .y:has(:scope > .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(:scope ~ .y:has(:scope .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(.y:scope > .g .h) matches expected elements The string did not match the expected pattern.
-FAIL :has(.y:scope .g .h) matches expected elements The string did not match the expected pattern.
-FAIL :has(.y:scope > .g .h) .i matches expected elements The string did not match the expected pattern.
-FAIL :has(.y:scope .g .h) .i matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + :has(.y:scope > .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + :has(.y:scope .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + :has(.y:scope > .g .h) .i) ~ .j matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope + :has(.y:scope .g .h) .i) ~ .j matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ :has(.y:scope > .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(.x:scope ~ :has(.y:scope .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.y > .g .h) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.y .g .h) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.y > .g .h) .i matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.y .g .h) .i matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + :has(:scope.y > .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + :has(:scope.y .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + :has(:scope.y > .g .h) .i) ~ .j matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x + :has(:scope.y .g .h) .i) ~ .j matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ :has(:scope.y > .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope.x ~ :has(:scope.y .g .h) .i) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(.d :scope .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.d .x:scope .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.d :scope.x .e) matches expected elements The string did not match the expected pattern.
-FAIL .x:has(.d ~ :scope ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.d ~ .x:scope ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(.d ~ :scope.x ~ .e) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope .d :scope) matches expected elements The string did not match the expected pattern.
-FAIL :has(:scope ~ .d ~ :scope) matches expected elements The string did not match the expected pattern.
+PASS .x:has(:scope .a) matches expected elements
+PASS .x:has(:scope .a > .b) matches expected elements
+PASS .x:has(:scope .a .b) matches expected elements
+PASS .x:has(:scope .a + .b) matches expected elements
+PASS .x:has(:scope .a ~ .b) matches expected elements
+PASS :has(.x:scope .a) matches expected elements
+PASS :has(.x:scope .a > .b) matches expected elements
+PASS :has(.x:scope .a .b) matches expected elements
+PASS :has(.x:scope .a + .b) matches expected elements
+PASS :has(.x:scope .a ~ .b) matches expected elements
+PASS :has(:scope.x .a) matches expected elements
+PASS :has(:scope.x .a > .b) matches expected elements
+PASS :has(:scope.x .a .b) matches expected elements
+PASS :has(:scope.x .a + .b) matches expected elements
+PASS :has(:scope.x .a ~ .b) matches expected elements
+PASS .x:has(:scope > .a) matches expected elements
+PASS .x:has(:scope > .a > .b) matches expected elements
+PASS .x:has(:scope > .a .b) matches expected elements
+PASS .x:has(:scope > .a + .b) matches expected elements
+PASS .x:has(:scope > .a ~ .b) matches expected elements
+PASS :has(.x:scope > .a) matches expected elements
+PASS :has(.x:scope > .a > .b) matches expected elements
+PASS :has(.x:scope > .a .b) matches expected elements
+PASS :has(.x:scope > .a + .b) matches expected elements
+PASS :has(.x:scope > .a ~ .b) matches expected elements
+PASS :has(:scope.x > .a) matches expected elements
+PASS :has(:scope.x > .a > .b) matches expected elements
+PASS :has(:scope.x > .a .b) matches expected elements
+PASS :has(:scope.x > .a + .b) matches expected elements
+PASS :has(:scope.x > .a ~ .b) matches expected elements
+PASS .x:has(:scope + .a) matches expected elements
+PASS .x:has(:scope + .a > .b) matches expected elements
+PASS .x:has(:scope + .a .b) matches expected elements
+PASS .x:has(:scope + .a + .b) matches expected elements
+PASS .x:has(:scope + .a ~ .b) matches expected elements
+PASS :has(.x:scope + .a) matches expected elements
+PASS :has(.x:scope + .a > .b) matches expected elements
+PASS :has(.x:scope + .a .b) matches expected elements
+PASS :has(.x:scope + .a + .b) matches expected elements
+PASS :has(.x:scope + .a ~ .b) matches expected elements
+PASS :has(:scope.x + .a) matches expected elements
+PASS :has(:scope.x + .a > .b) matches expected elements
+PASS :has(:scope.x + .a .b) matches expected elements
+PASS :has(:scope.x + .a + .b) matches expected elements
+PASS :has(:scope.x + .a ~ .b) matches expected elements
+PASS .x:has(:scope ~ .a) matches expected elements
+PASS .x:has(:scope ~ .a > .b) matches expected elements
+PASS .x:has(:scope ~ .a .b) matches expected elements
+PASS .x:has(:scope ~ .a + .b) matches expected elements
+PASS .x:has(:scope ~ .a + .b > .c) matches expected elements
+PASS .x:has(:scope ~ .a + .b .c) matches expected elements
+PASS :has(.x:scope ~ .a) matches expected elements
+PASS :has(.x:scope ~ .a > .b) matches expected elements
+PASS :has(.x:scope ~ .a .b) matches expected elements
+PASS :has(.x:scope ~ .a + .b) matches expected elements
+PASS :has(.x:scope ~ .a + .b > .c) matches expected elements
+PASS :has(.x:scope ~ .a + .b .c) matches expected elements
+PASS :has(:scope.x ~ .a) matches expected elements
+PASS :has(:scope.x ~ .a > .b) matches expected elements
+PASS :has(:scope.x ~ .a .b) matches expected elements
+PASS :has(:scope.x ~ .a + .b) matches expected elements
+PASS :has(:scope.x ~ .a + .b > .c) matches expected elements
+PASS :has(:scope.x ~ .a + .b .c) matches expected elements
+PASS .x:has(.d .e) matches expected elements
+PASS .x:has(.d .e) .f matches expected elements
+PASS .x:has(:scope .d .e) matches expected elements
+PASS .x:has(:scope .d .e) .f matches expected elements
+PASS .x:has(:scope > .d) matches expected elements
+PASS .x:has(:scope > .d) .f matches expected elements
+PASS .x:has(:scope ~ .d ~ .e) matches expected elements
+PASS .x:has(:scope ~ .d ~ .e) ~ .f matches expected elements
+PASS .x:has(:scope + .d ~ .e) matches expected elements
+PASS .x:has(:scope + .d ~ .e) ~ .f matches expected elements
+PASS :has(.x:scope .d .e) matches expected elements
+PASS :has(.x:scope .d .e) .f matches expected elements
+PASS :has(.x:scope > .d) matches expected elements
+PASS :has(.x:scope > .d) .f matches expected elements
+PASS :has(.x:scope ~ .d ~ .e) matches expected elements
+PASS :has(.x:scope ~ .d ~ .e) ~ .f matches expected elements
+PASS :has(.x:scope + .d ~ .e) matches expected elements
+PASS :has(.x:scope + .d ~ .e) ~ .f matches expected elements
+PASS :has(:scope.x .d .e) matches expected elements
+PASS :has(:scope.x .d .e) .f matches expected elements
+PASS :has(:scope.x > .d) matches expected elements
+PASS :has(:scope.x > .d) .f matches expected elements
+PASS :has(:scope.x ~ .d ~ .e) matches expected elements
+PASS :has(:scope.x ~ .d ~ .e) ~ .f matches expected elements
+PASS :has(:scope.x + .d ~ .e) matches expected elements
+PASS :has(:scope.x + .d ~ .e) ~ .f matches expected elements
+PASS .y:has(:scope > .g .h) matches expected elements
+PASS .y:has(:scope .g .h) matches expected elements
+PASS .y:has(:scope > .g .h) .i matches expected elements
+PASS .y:has(:scope .g .h) .i matches expected elements
+PASS .x:has(:scope + .y:has(:scope > .g .h) .i) matches expected elements
+PASS .x:has(:scope + .y:has(:scope .g .h) .i) matches expected elements
+PASS .x:has(:scope + .y:has(:scope > .g .h) .i) ~ .j matches expected elements
+PASS .x:has(:scope + .y:has(:scope .g .h) .i) ~ .j matches expected elements
+PASS .x:has(:scope ~ .y:has(:scope > .g .h) .i) matches expected elements
+PASS .x:has(:scope ~ .y:has(:scope .g .h) .i) matches expected elements
+PASS :has(.y:scope > .g .h) matches expected elements
+PASS :has(.y:scope .g .h) matches expected elements
+PASS :has(.y:scope > .g .h) .i matches expected elements
+PASS :has(.y:scope .g .h) .i matches expected elements
+PASS :has(.x:scope + :has(.y:scope > .g .h) .i) matches expected elements
+PASS :has(.x:scope + :has(.y:scope .g .h) .i) matches expected elements
+PASS :has(.x:scope + :has(.y:scope > .g .h) .i) ~ .j matches expected elements
+PASS :has(.x:scope + :has(.y:scope .g .h) .i) ~ .j matches expected elements
+PASS :has(.x:scope ~ :has(.y:scope > .g .h) .i) matches expected elements
+PASS :has(.x:scope ~ :has(.y:scope .g .h) .i) matches expected elements
+PASS :has(:scope.y > .g .h) matches expected elements
+PASS :has(:scope.y .g .h) matches expected elements
+PASS :has(:scope.y > .g .h) .i matches expected elements
+PASS :has(:scope.y .g .h) .i matches expected elements
+PASS :has(:scope.x + :has(:scope.y > .g .h) .i) matches expected elements
+PASS :has(:scope.x + :has(:scope.y .g .h) .i) matches expected elements
+PASS :has(:scope.x + :has(:scope.y > .g .h) .i) ~ .j matches expected elements
+PASS :has(:scope.x + :has(:scope.y .g .h) .i) ~ .j matches expected elements
+PASS :has(:scope.x ~ :has(:scope.y > .g .h) .i) matches expected elements
+PASS :has(:scope.x ~ :has(:scope.y .g .h) .i) matches expected elements
+PASS .x:has(.d :scope .e) matches expected elements
+PASS :has(.d .x:scope .e) matches expected elements
+PASS :has(.d :scope.x .e) matches expected elements
+PASS .x:has(.d ~ :scope ~ .e) matches expected elements
+PASS :has(.d ~ .x:scope ~ .e) matches expected elements
+PASS :has(.d ~ :scope.x ~ .e) matches expected elements
+PASS :has(:scope .d :scope) matches expected elements
+PASS :has(:scope ~ .d ~ :scope) matches expected elements
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/parsing/parse-has-expected.txt (281294 => 281295)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/parsing/parse-has-expected.txt	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/parsing/parse-has-expected.txt	2021-08-20 05:15:47 UTC (rev 281295)
@@ -1,41 +1,41 @@
 
-FAIL ":has(a)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(#a)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(.a)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has([a])" should be a valid selector The string did not match the expected pattern.
-FAIL ":has([a=\"b\"])" should be a valid selector The string did not match the expected pattern.
-FAIL ":has([a|=\"b\"])" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(:hover)" should be a valid selector The string did not match the expected pattern.
-FAIL "*:has(.a)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(.b)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(:scope .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(.a:scope .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(.a .b:scope)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(> .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(:scope > .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(.a:scope > .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(> .a .b:scope)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(~ .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(:scope ~ .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(.a:scope ~ .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(~ .a .b:scope)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(+ .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(:scope + .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(.a:scope + .b)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(+ .a .b:scope)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(:scope .b :scope)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(.b) .c" should be a valid selector The string did not match the expected pattern.
-FAIL ".a .b:has(.c)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a .b:has(.c .d)" should be a valid selector The string did not match the expected pattern.
-FAIL ".a .b:has(.c .d) .e" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(.b:has(.c))" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(.b:is(.c .d))" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(.b:is(.c:has(.d) .e))" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:is(.b:has(.c) .d)" should be a valid selector assert_equals: serialization should be canonical expected ".a:is(.b:has(.c) .d)" but got ".a:is()"
-FAIL ".a:not(:has(.b))" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(:not(.b))" should be a valid selector The string did not match the expected pattern.
-FAIL ".a:has(.b):has(.c)" should be a valid selector The string did not match the expected pattern.
-FAIL "*|*:has(*)" should be a valid selector The string did not match the expected pattern.
-FAIL ":has(*|*)" should be a valid selector The string did not match the expected pattern.
+PASS ":has(a)" should be a valid selector
+PASS ":has(#a)" should be a valid selector
+PASS ":has(.a)" should be a valid selector
+PASS ":has([a])" should be a valid selector
+PASS ":has([a=\"b\"])" should be a valid selector
+PASS ":has([a|=\"b\"])" should be a valid selector
+PASS ":has(:hover)" should be a valid selector
+PASS "*:has(.a)" should be a valid selector
+PASS ".a:has(.b)" should be a valid selector
+PASS ".a:has(:scope .b)" should be a valid selector
+PASS ":has(.a:scope .b)" should be a valid selector
+PASS ":has(.a .b:scope)" should be a valid selector
+PASS ".a:has(> .b)" should be a valid selector
+PASS ".a:has(:scope > .b)" should be a valid selector
+PASS ":has(.a:scope > .b)" should be a valid selector
+PASS ":has(> .a .b:scope)" should be a valid selector
+PASS ".a:has(~ .b)" should be a valid selector
+PASS ".a:has(:scope ~ .b)" should be a valid selector
+PASS ":has(.a:scope ~ .b)" should be a valid selector
+PASS ":has(~ .a .b:scope)" should be a valid selector
+PASS ".a:has(+ .b)" should be a valid selector
+PASS ".a:has(:scope + .b)" should be a valid selector
+PASS ":has(.a:scope + .b)" should be a valid selector
+PASS ":has(+ .a .b:scope)" should be a valid selector
+PASS ".a:has(:scope .b :scope)" should be a valid selector
+PASS ".a:has(.b) .c" should be a valid selector
+PASS ".a .b:has(.c)" should be a valid selector
+PASS ".a .b:has(.c .d)" should be a valid selector
+PASS ".a .b:has(.c .d) .e" should be a valid selector
+PASS ".a:has(.b:has(.c))" should be a valid selector
+PASS ".a:has(.b:is(.c .d))" should be a valid selector
+PASS ".a:has(.b:is(.c:has(.d) .e))" should be a valid selector
+PASS ".a:is(.b:has(.c) .d)" should be a valid selector
+PASS ".a:not(:has(.b))" should be a valid selector
+PASS ".a:has(:not(.b))" should be a valid selector
+PASS ".a:has(.b):has(.c)" should be a valid selector
+PASS "*|*:has(*)" should be a valid selector
+PASS ":has(*|*)" should be a valid selector
 PASS ".a:has()" should be an invalid selector
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/dom/nodes/Element-closest-expected.txt (281294 => 281295)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/dom/nodes/Element-closest-expected.txt	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/dom/nodes/Element-closest-expected.txt	2021-08-20 05:15:47 UTC (rev 281295)
@@ -27,5 +27,5 @@
 PASS Element.closest with context node 'test4' and selector ':scope'
 PASS Element.closest with context node 'test4' and selector 'select > :scope'
 PASS Element.closest with context node 'test4' and selector 'div > :scope'
-FAIL Element.closest with context node 'test4' and selector ':has(> :scope)' The string did not match the expected pattern.
+FAIL Element.closest with context node 'test4' and selector ':has(> :scope)' assert_equals: :has(> :scope) expected "test3" but got ""
 

Modified: trunk/Source/WTF/ChangeLog (281294 => 281295)


--- trunk/Source/WTF/ChangeLog	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WTF/ChangeLog	2021-08-20 05:15:47 UTC (rev 281295)
@@ -1,3 +1,14 @@
+2021-08-19  Antti Koivisto  <[email protected]>
+
+        [:has() pseudo-class] Basic support
+        https://bugs.webkit.org/show_bug.cgi?id=228894
+
+        Reviewed by Simon Fraser.
+
+        * Scripts/Preferences/WebPreferencesExperimental.yaml:
+
+        Add off-by-default HasPseudoClassEnabled preference value.
+
 2021-08-19  Myles C. Maxfield  <[email protected]>
 
         [Cocoa] Stop treating the system font as a non-variable font

Modified: trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml (281294 => 281295)


--- trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml	2021-08-20 05:15:47 UTC (rev 281295)
@@ -473,6 +473,18 @@
     WebKit:
       default: false
 
+HasPseudoClassEnabled:
+  type: bool
+  humanReadableName: ":has() pseudo-class"
+  humanReadableDescription: "Enable :has() pseudo-class"
+  defaultValue:
+    WebKitLegacy:
+      default: false
+    WebKit:
+      default: false
+    WebCore:
+      default: false
+
 HighlightAPIEnabled:
   type: bool
   humanReadableName: "Highlight API"

Modified: trunk/Source/WebCore/ChangeLog (281294 => 281295)


--- trunk/Source/WebCore/ChangeLog	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/ChangeLog	2021-08-20 05:15:47 UTC (rev 281295)
@@ -1,3 +1,58 @@
+2021-08-19  Antti Koivisto  <[email protected]>
+
+        [:has() pseudo-class] Basic support
+        https://bugs.webkit.org/show_bug.cgi?id=228894
+
+        Reviewed by Simon Fraser.
+
+        This patch adds basic support for :has() pseudo-class, https://drafts.csswg.org/selectors/#has-pseudo.
+        The initial implementation is very inefficient. There is no support for invalidation yet.
+
+        The feature is disabled by default.
+
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::selectorText const):
+
+        Serialization.
+
+        * css/CSSSelector.h:
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOne const):
+
+        Selector matching using nested SelectorChecker.
+
+        * css/SelectorPseudoClassAndCompatibilityElementMap.in:
+        * css/parser/CSSParserContext.cpp:
+        (WebCore::operator==):
+        (WebCore::add):
+        * css/parser/CSSParserContext.h:
+        * css/parser/CSSParserSelector.h:
+        (WebCore::CSSParserSelector::setPseudoClassType):
+        * css/parser/CSSSelectorParser.cpp:
+        (WebCore::CSSSelectorParser::consumeForgivingSelectorList):
+
+        Add a template version of the forgiving parsing function.
+
+        (WebCore::CSSSelectorParser::consumeForgivingComplexSelectorList):
+
+        Use it for complex selector lists.
+
+        (WebCore::CSSSelectorParser::consumeForgivingRelativeSelectorList):
+
+        And the new relative selector lists.
+
+        (WebCore::CSSSelectorParser::consumeRelativeSelector):
+
+        Parse relative selectors like "> foo".
+
+        (WebCore::CSSSelectorParser::consumePseudo):
+        (WebCore::CSSSelectorParser::consumeComplexForgivingSelectorList): Deleted.
+        * css/parser/CSSSelectorParser.h:
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::addPseudoClassType):
+
+        No compiler support yet.
+
 2021-08-19  Myles C. Maxfield  <[email protected]>
 
         The fast text codepath does not handle run initial advances

Modified: trunk/Source/WebCore/css/CSSSelector.cpp (281294 => 281295)


--- trunk/Source/WebCore/css/CSSSelector.cpp	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/CSSSelector.cpp	2021-08-20 05:15:47 UTC (rev 281295)
@@ -508,6 +508,11 @@
                 builder.append(":future");
                 break;
 #endif
+            case CSSSelector::PseudoClassHas:
+                builder.append(":has(");
+                cs->selectorList()->buildSelectorsText(builder);
+                builder.append(')');
+                break;
 #if ENABLE(ATTACHMENT_ELEMENT)
             case CSSSelector::PseudoClassHasAttachment:
                 builder.append(":has-attachment");
@@ -642,6 +647,9 @@
             case CSSSelector::PseudoClassScope:
                 builder.append(":scope");
                 break;
+            case CSSSelector::PseudoClassRelativeScope:
+                // Just remove the space from the start to generate a relative selector string like in ":has(> foo)".
+                return rightSide.substring(1);
             case CSSSelector::PseudoClassSingleButton:
                 builder.append(":single-button");
                 break;

Modified: trunk/Source/WebCore/css/CSSSelector.h (281294 => 281295)


--- trunk/Source/WebCore/css/CSSSelector.h	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/CSSSelector.h	2021-08-20 05:15:47 UTC (rev 281295)
@@ -138,10 +138,12 @@
             PseudoClassNot,
             PseudoClassRoot,
             PseudoClassScope,
+            PseudoClassRelativeScope, // Like :scope but for internal use with relative selectors like :has(> foo).
             PseudoClassWindowInactive,
             PseudoClassCornerPresent,
             PseudoClassDecrement,
             PseudoClassIncrement,
+            PseudoClassHas,
             PseudoClassHorizontal,
             PseudoClassVertical,
             PseudoClassStart,

Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (281294 => 281295)


--- trunk/Source/WebCore/css/SelectorChecker.cpp	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp	2021-08-20 05:15:47 UTC (rev 281295)
@@ -45,6 +45,7 @@
 #include "SelectorCheckerTestFunctions.h"
 #include "ShadowRoot.h"
 #include "Text.h"
+#include "TypedElementDescendantIterator.h"
 
 namespace WebCore {
 
@@ -828,6 +829,32 @@
                     matchType = localMatchType;
                 return hasMatchedAnything;
             }
+        case CSSSelector::PseudoClassHas: {
+            // FIXME: This is the worst possible implementation in terms of performance.
+            auto checkRelative = [&](auto& elementToCheck) {
+                for (auto* subselector = selector.selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
+                    SelectorChecker selectorChecker(element.document());
+                    CheckingContext selectorCheckingContext(SelectorChecker::Mode::ResolvingStyle);
+                    selectorCheckingContext.scope = &element;
+                    if (selectorChecker.match(*subselector, elementToCheck, selectorCheckingContext))
+                        return true;
+                }
+                return false;
+            };
+            for (auto& descendant : descendantsOfType<Element>(element)) {
+                if (checkRelative(descendant))
+                    return true;
+            }
+            for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
+                if (checkRelative(*sibling))
+                    return true;
+                for (auto& descendant : descendantsOfType<Element>(*sibling)) {
+                    if (checkRelative(descendant))
+                        return true;
+                }
+            }
+            return false;
+        }
         case CSSSelector::PseudoClassPlaceholderShown:
             if (is<HTMLTextFormControlElement>(element)) {
                 addStyleRelation(checkingContext, element, Style::Relation::Unique);
@@ -1038,7 +1065,8 @@
             return matchesPastCuePseudoClass(element);
 #endif
 
-        case CSSSelector::PseudoClassScope: {
+        case CSSSelector::PseudoClassScope:
+        case CSSSelector::PseudoClassRelativeScope: {
             const Node* contextualReferenceNode = !checkingContext.scope ? element.document().documentElement() : checkingContext.scope;
             if (&element == contextualReferenceNode)
                 return true;

Modified: trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in (281294 => 281295)


--- trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in	2021-08-20 05:15:47 UTC (rev 281295)
@@ -33,6 +33,7 @@
 focus
 focus-visible
 focus-within
+has
 #if ENABLE(ATTACHMENT_ELEMENT)
 has-attachment
 #endif

Modified: trunk/Source/WebCore/css/parser/CSSParserContext.cpp (281294 => 281295)


--- trunk/Source/WebCore/css/parser/CSSParserContext.cpp	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/parser/CSSParserContext.cpp	2021-08-20 05:15:47 UTC (rev 281295)
@@ -94,6 +94,7 @@
 #endif
     , useLegacyBackgroundSizeShorthandBehavior { document.settings().useLegacyBackgroundSizeShorthandBehavior() }
     , focusVisibleEnabled { document.settings().focusVisibleEnabled() }
+    , hasPseudoClassEnabled { document.settings().hasPseudoClassEnabled() }
 #if ENABLE(ATTACHMENT_ELEMENT)
     , attachmentEnabled { RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled() }
 #endif
@@ -137,6 +138,7 @@
 #endif
         && a.useLegacyBackgroundSizeShorthandBehavior == b.useLegacyBackgroundSizeShorthandBehavior
         && a.focusVisibleEnabled == b.focusVisibleEnabled
+        && a.hasPseudoClassEnabled == b.hasPseudoClassEnabled
 #if ENABLE(ATTACHMENT_ELEMENT)
         && a.attachmentEnabled == b.attachmentEnabled
 #endif
@@ -174,11 +176,12 @@
 #endif
         | context.useLegacyBackgroundSizeShorthandBehavior  << 20
         | context.focusVisibleEnabled                       << 21
+        | context.hasPseudoClassEnabled                     << 22
 #if ENABLE(ATTACHMENT_ELEMENT)
-        | context.attachmentEnabled                         << 22
+        | context.attachmentEnabled                         << 23
 #endif
-        | context.overflowClipEnabled                       << 23
-        | context.mode                                      << 24; // This is multiple bits, so keep it last.
+        | context.overflowClipEnabled                       << 24
+        | context.mode                                      << 25; // This is multiple bits, so keep it last.
     add(hasher, context.baseURL, context.charset, bits);
 }
 

Modified: trunk/Source/WebCore/css/parser/CSSParserContext.h (281294 => 281295)


--- trunk/Source/WebCore/css/parser/CSSParserContext.h	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/parser/CSSParserContext.h	2021-08-20 05:15:47 UTC (rev 281295)
@@ -81,6 +81,7 @@
 #endif
     bool useLegacyBackgroundSizeShorthandBehavior { false };
     bool focusVisibleEnabled { false };
+    bool hasPseudoClassEnabled { false };
 
     // RuntimeEnabledFeatures.
 #if ENABLE(ATTACHMENT_ELEMENT)

Modified: trunk/Source/WebCore/css/parser/CSSParserSelector.h (281294 => 281295)


--- trunk/Source/WebCore/css/parser/CSSParserSelector.h	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/parser/CSSParserSelector.h	2021-08-20 05:15:47 UTC (rev 281295)
@@ -60,6 +60,7 @@
     const CSSSelectorList* selectorList() const { return m_selector->selectorList(); }
     
     void setPseudoElementType(CSSSelector::PseudoElementType type) { m_selector->setPseudoElementType(type); }
+    void setPseudoClassType(CSSSelector::PseudoClassType type) { m_selector->setPseudoClassType(type); }
 
     void adoptSelectorVector(Vector<std::unique_ptr<CSSParserSelector>>&&);
     void setArgumentList(std::unique_ptr<Vector<AtomString>>);

Modified: trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp (281294 => 281295)


--- trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp	2021-08-20 05:15:47 UTC (rev 281295)
@@ -30,6 +30,7 @@
 #include "config.h"
 #include "CSSSelectorParser.h"
 
+#include "RuntimeEnabledFeatures.h"
 #include <memory>
 #include <wtf/OptionSet.h>
 #include <wtf/SetForScope.h>
@@ -97,7 +98,8 @@
     return CSSSelectorList { WTFMove(selectorList) };
 }
 
-CSSSelectorList CSSSelectorParser::consumeComplexForgivingSelectorList(CSSParserTokenRange& range)
+template<typename ConsumeSelector>
+CSSSelectorList CSSSelectorParser::consumeForgivingSelectorList(CSSParserTokenRange& range, ConsumeSelector&& consumeSelector)
 {
     if (m_failedParsing)
         return { };
@@ -105,7 +107,7 @@
     Vector<std::unique_ptr<CSSParserSelector>> selectorList;
 
     auto consumeForgiving = [&] {
-        auto selector = consumeComplexSelector(range);
+        auto selector = consumeSelector(range);
 
         if (m_failedParsing) {
             selector = { };
@@ -133,6 +135,20 @@
     return CSSSelectorList { WTFMove(selectorList) };
 }
 
+CSSSelectorList CSSSelectorParser::consumeForgivingComplexSelectorList(CSSParserTokenRange& range)
+{
+    return consumeForgivingSelectorList(range, [&](CSSParserTokenRange& range) {
+        return consumeComplexSelector(range);
+    });
+}
+
+CSSSelectorList CSSSelectorParser::consumeForgivingRelativeSelectorList(CSSParserTokenRange& range)
+{
+    return consumeForgivingSelectorList(range, [&](CSSParserTokenRange& range) {
+        return consumeRelativeSelector(range);
+    });
+}
+
 bool CSSSelectorParser::supportsComplexSelector(CSSParserTokenRange range, const CSSParserContext& context)
 {
     range.consumeWhitespace();
@@ -247,6 +263,43 @@
     return selector;
 }
 
+std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeRelativeSelector(CSSParserTokenRange& range)
+{
+    auto scopeCombinator = consumeCombinator(range);
+
+    if (scopeCombinator == CSSSelector::Subselector)
+        scopeCombinator = CSSSelector::DescendantSpace;
+
+    auto selector = consumeComplexSelector(range);
+    if (!selector)
+        return nullptr;
+
+    auto hasScopePseudoClass = [](auto& selector) {
+        return selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassScope;
+    };
+
+    bool hasExplicitScope = hasScopePseudoClass(*selector);
+
+    auto* end = selector.get();
+    while (end->tagHistory()) {
+        end = end->tagHistory();
+        if (hasScopePseudoClass(*end))
+            hasExplicitScope = true;
+    }
+
+    // If the selector doesn't have an explicit :scope on the left, add an implicit one.
+    if (!hasExplicitScope || scopeCombinator != CSSSelector::DescendantSpace) {
+        auto scopeSelector = makeUnique<CSSParserSelector>();
+        scopeSelector->setMatch(CSSSelector::PseudoClass);
+        scopeSelector->setPseudoClassType(CSSSelector::PseudoClassRelativeScope);
+
+        end->setRelation(scopeCombinator);
+        end->setTagHistory(WTFMove(scopeSelector));
+    }
+
+    return selector;
+}
+
 static bool isScrollbarPseudoClass(CSSSelector::PseudoClassType pseudo)
 {
     switch (pseudo) {
@@ -587,6 +640,8 @@
                 return nullptr;
             if (!m_context.focusVisibleEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassFocusVisible)
                 return nullptr;
+            if (!m_context.hasPseudoClassEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassHas)
+                return nullptr;
 #if ENABLE(ATTACHMENT_ELEMENT)
             if (!m_context.attachmentEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassHasAttachment)
                 return nullptr;
@@ -672,7 +727,7 @@
             SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
             DisallowPseudoElementsScope scope(*this);
             auto selectorList = makeUnique<CSSSelectorList>();
-            *selectorList = consumeComplexForgivingSelectorList(block);
+            *selectorList = consumeForgivingComplexSelectorList(block);
             if (!block.atEnd())
                 return nullptr;
             selector->setSelectorList(WTFMove(selectorList));
@@ -687,6 +742,15 @@
             selector->setSelectorList(WTFMove(selectorList));
             return selector;
         }
+        case CSSSelector::PseudoClassHas: {
+            DisallowPseudoElementsScope scope(*this);
+            auto selectorList = makeUnique<CSSSelectorList>();
+            *selectorList = consumeForgivingRelativeSelectorList(block);
+            if (selectorList->isEmpty() || !block.atEnd())
+                return nullptr;
+            selector->setSelectorList(WTFMove(selectorList));
+            return selector;
+        }
 #if ENABLE(CSS_SELECTORS_LEVEL4)
         case CSSSelector::PseudoClassDir:
         case CSSSelector::PseudoClassRole: {

Modified: trunk/Source/WebCore/css/parser/CSSSelectorParser.h (281294 => 281295)


--- trunk/Source/WebCore/css/parser/CSSSelectorParser.h	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/css/parser/CSSSelectorParser.h	2021-08-20 05:15:47 UTC (rev 281295)
@@ -52,11 +52,15 @@
     static bool supportsComplexSelector(CSSParserTokenRange, const CSSParserContext&);
 
 private:
-    CSSSelectorList consumeComplexForgivingSelectorList(CSSParserTokenRange&);
+    template<typename ConsumeSelector> CSSSelectorList consumeForgivingSelectorList(CSSParserTokenRange&, ConsumeSelector&&);
+
+    CSSSelectorList consumeForgivingComplexSelectorList(CSSParserTokenRange&);
+    CSSSelectorList consumeForgivingRelativeSelectorList(CSSParserTokenRange&);
     CSSSelectorList consumeCompoundSelectorList(CSSParserTokenRange&);
 
     std::unique_ptr<CSSParserSelector> consumeComplexSelector(CSSParserTokenRange&);
     std::unique_ptr<CSSParserSelector> consumeCompoundSelector(CSSParserTokenRange&);
+    std::unique_ptr<CSSParserSelector> consumeRelativeSelector(CSSParserTokenRange&);
 
     // This doesn't include element names, since they're handled specially.
     std::unique_ptr<CSSParserSelector> consumeSimpleSelector(CSSParserTokenRange&);

Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (281294 => 281295)


--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp	2021-08-20 04:53:20 UTC (rev 281294)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp	2021-08-20 05:15:47 UTC (rev 281295)
@@ -899,6 +899,8 @@
     case CSSSelector::PseudoClassNthOfType:
     case CSSSelector::PseudoClassNthLastOfType:
     case CSSSelector::PseudoClassDrag:
+    case CSSSelector::PseudoClassHas:
+    case CSSSelector::PseudoClassRelativeScope:
 #if ENABLE(CSS_SELECTORS_LEVEL4)
     case CSSSelector::PseudoClassDir:
     case CSSSelector::PseudoClassRole:
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to