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: