Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (286432 => 286433)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-02 17:36:50 UTC (rev 286433)
@@ -1,3 +1,19 @@
+2021-12-02 Antti Koivisto <[email protected]>
+
+ [:has() pseudo-class] Invalidation in non-subject position
+ https://bugs.webkit.org/show_bug.cgi?id=233758
+
+ Reviewed by Simon Fraser.
+
+ * web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-parent-position.html: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt: Added.
+ * web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html: Added.
+
2021-12-02 Andreu Botella <[email protected]>
File inputs in non-multipart form submissions show up as string values in the formdata event
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,73 @@
+
+PASS Initial color
+PASS add .test to previous_sibling
+PASS remove .test from previous_sibling
+PASS add .test to previous_sibling_child
+PASS remove .test from previous_sibling_child
+PASS add .test to previous_sibling_descendant
+PASS remove .test from previous_sibling_descendant
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to next_sibling
+PASS remove .test from next_sibling
+PASS add .test to next_sibling_child
+PASS remove .test from next_sibling_child
+PASS add .test to next_sibling_descendant
+PASS remove .test from next_sibling_descendant
+PASS insert element div.test before previous_sibling
+PASS remove element div.test before previous_sibling
+PASS insert element div.test before previous_sibling_child
+PASS remove element div.test before previous_sibling_child
+PASS insert element div.test before previous_sibling_descendant
+PASS remove element div.test before previous_sibling_descendant
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before next_sibling
+PASS remove element div.test before next_sibling
+PASS insert element div.test before next_sibling_child
+PASS remove element div.test before next_sibling_child
+PASS insert element div.test before next_sibling_descendant
+PASS remove element div.test before next_sibling_descendant
+PASS insert element div.test after previous_sibling
+PASS remove element div.test after previous_sibling
+PASS insert element div.test after previous_sibling_child
+PASS remove element div.test after previous_sibling_child
+PASS insert element div.test after previous_sibling_descendant
+PASS remove element div.test after previous_sibling_descendant
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after next_sibling
+PASS remove element div.test after next_sibling
+PASS insert element div.test after next_sibling_child
+PASS remove element div.test after next_sibling_child
+PASS insert element div.test after next_sibling_descendant
+PASS remove element div.test after next_sibling_descendant
+PASS insert tree div>div.test before previous_sibling
+PASS remove tree div>div.test before previous_sibling
+PASS insert tree div>div.test before previous_sibling_child
+PASS remove tree div>div.test before previous_sibling_child
+PASS insert tree div>div.test before previous_sibling_descendant
+PASS remove tree div>div.test before previous_sibling_descendant
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before next_sibling
+PASS remove tree div>div.test before next_sibling
+PASS insert tree div>div.test before next_sibling_child
+PASS remove tree div>div.test before next_sibling_child
+PASS insert tree div>div.test before next_sibling_descendant
+PASS remove tree div>div.test before next_sibling_descendant
+PASS insert tree div>div.test after previous_sibling
+PASS remove tree div>div.test after previous_sibling
+PASS insert tree div>div.test after previous_sibling_child
+PASS remove tree div>div.test after previous_sibling_child
+PASS insert tree div>div.test after previous_sibling_descendant
+PASS remove tree div>div.test after previous_sibling_descendant
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after next_sibling
+PASS remove tree div>div.test after next_sibling
+PASS insert tree div>div.test after next_sibling_child
+PASS remove tree div>div.test after next_sibling_child
+PASS insert tree div>div.test after next_sibling_descendant
+PASS remove tree div>div.test after next_sibling_descendant
+
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in adjacent position</title>
+<link rel="author" title="Antti Koivisto" href=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+div, main { color: grey }
+div:has(.test) + #subject { color: red }
+div:has(> .test) + #subject { color: green }
+div:has(~ .test) + #subject { color: yellow }
+div:has(+ .test) + #subject { color: blue }
+div:has(~ div .test) + #subject { color: purple }
+div:has(+ div .test) + #subject { color: pink }
+</style>
+
+<main id=main>
+ <div id=previous_sibling>
+ <div id=previous_sibling_child>
+ <div id=previous_sibling_descendant></div>
+ </div>
+ </div>
+ <div id=subject></div>
+ <div id=next_sibling>
+ <div id=next_sibling_child>
+ <div id=next_sibling_descendant></div>
+ </div>
+ </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+ test(function() {
+ assert_equals(getComputedStyle(subject).color, color);
+ }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+ element.classList.add('test');
+ testColor(`add .test to ${element.id}`, expectedColor);
+ element.classList.remove('test');
+ testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ beforeElement.before(newElement);
+ testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ afterElement.after(newElement);
+ testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ beforeElement.before(newElement);
+ testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ afterElement.after(newElement);
+ testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(previous_sibling, grey);
+testClassChange(previous_sibling_child, green);
+testClassChange(previous_sibling_descendant, red);
+testClassChange(subject, blue);
+testClassChange(next_sibling, yellow);
+testClassChange(next_sibling_child, purple);
+testClassChange(next_sibling_descendant, purple);
+
+testElementInsertionBefore(previous_sibling, grey);
+testElementInsertionBefore(previous_sibling_child, green);
+testElementInsertionBefore(previous_sibling_descendant, red);
+testElementInsertionBefore(subject, grey);
+testElementInsertionBefore(next_sibling, yellow);
+testElementInsertionBefore(next_sibling_child, purple);
+testElementInsertionBefore(next_sibling_descendant, purple);
+
+testElementInsertionAfter(previous_sibling, grey);
+testElementInsertionAfter(previous_sibling_child, green);
+testElementInsertionAfter(previous_sibling_descendant, red);
+testElementInsertionAfter(subject, yellow);
+testElementInsertionAfter(next_sibling, yellow);
+testElementInsertionAfter(next_sibling_child, purple);
+testElementInsertionAfter(next_sibling_descendant, purple);
+
+testTreeInsertionBefore(previous_sibling, grey);
+testTreeInsertionBefore(previous_sibling_child, red);
+testTreeInsertionBefore(previous_sibling_descendant, red);
+testTreeInsertionBefore(subject, green);
+testTreeInsertionBefore(next_sibling, purple);
+testTreeInsertionBefore(next_sibling_child, purple);
+testTreeInsertionBefore(next_sibling_descendant, purple);
+
+testTreeInsertionAfter(previous_sibling, green);
+testTreeInsertionAfter(previous_sibling_child, red);
+testTreeInsertionAfter(previous_sibling_descendant, red);
+testTreeInsertionAfter(subject, purple);
+testTreeInsertionAfter(next_sibling, purple);
+testTreeInsertionAfter(next_sibling_child, purple);
+testTreeInsertionAfter(next_sibling_descendant, purple);
+
+</script>
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,83 @@
+
+PASS Initial color
+PASS add .test to subject_ancestor
+PASS remove .test from subject_ancestor
+PASS add .test to subject_parent
+PASS remove .test from subject_parent
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to subject_child
+PASS remove .test from subject_child
+PASS add .test to subject_descendant
+PASS remove .test from subject_descendant
+PASS add .test to next_sibling
+PASS remove .test from next_sibling
+PASS add .test to next_sibling_child
+PASS remove .test from next_sibling_child
+PASS add .test to next_sibling_descendant
+PASS remove .test from next_sibling_descendant
+PASS insert element div.test before subject_ancestor
+PASS remove element div.test before subject_ancestor
+PASS insert element div.test before subject_parent
+PASS remove element div.test before subject_parent
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before subject_child
+PASS remove element div.test before subject_child
+PASS insert element div.test before subject_descendant
+PASS remove element div.test before subject_descendant
+PASS insert element div.test before next_sibling
+PASS remove element div.test before next_sibling
+PASS insert element div.test before next_sibling_child
+PASS remove element div.test before next_sibling_child
+PASS insert element div.test before next_sibling_descendant
+PASS remove element div.test before next_sibling_descendant
+PASS insert element div.test after subject_ancestor
+PASS remove element div.test after subject_ancestor
+PASS insert element div.test after subject_parent
+PASS remove element div.test after subject_parent
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after subject_child
+PASS remove element div.test after subject_child
+PASS insert element div.test after subject_descendant
+PASS remove element div.test after subject_descendant
+PASS insert element div.test after next_sibling
+PASS remove element div.test after next_sibling
+PASS insert element div.test after next_sibling_child
+PASS remove element div.test after next_sibling_child
+PASS insert element div.test after next_sibling_descendant
+PASS remove element div.test after next_sibling_descendant
+PASS insert tree div>div.test before subject_ancestor
+PASS remove tree div>div.test before subject_ancestor
+PASS insert tree div>div.test before subject_parent
+PASS remove tree div>div.test before subject_parent
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before subject_child
+PASS remove tree div>div.test before subject_child
+PASS insert tree div>div.test before subject_descendant
+PASS remove tree div>div.test before subject_descendant
+PASS insert tree div>div.test before next_sibling
+PASS remove tree div>div.test before next_sibling
+PASS insert tree div>div.test before next_sibling_child
+PASS remove tree div>div.test before next_sibling_child
+PASS insert tree div>div.test before next_sibling_descendant
+PASS remove tree div>div.test before next_sibling_descendant
+PASS insert tree div>div.test after subject_ancestor
+PASS remove tree div>div.test after subject_ancestor
+PASS insert tree div>div.test after subject_parent
+PASS remove tree div>div.test after subject_parent
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after subject_child
+PASS remove tree div>div.test after subject_child
+PASS insert tree div>div.test after subject_descendant
+PASS remove tree div>div.test after subject_descendant
+PASS insert tree div>div.test after next_sibling
+PASS remove tree div>div.test after next_sibling
+PASS insert tree div>div.test after next_sibling_child
+PASS remove tree div>div.test after next_sibling_child
+PASS insert tree div>div.test after next_sibling_descendant
+PASS remove tree div>div.test after next_sibling_descendant
+
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,157 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in ancestor position</title>
+<link rel="author" title="Antti Koivisto" href=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+div, main { color: grey }
+div:has(.test) #subject { color: red }
+div:has(> .test) #subject { color: green }
+div:has(~ .test) #subject { color: yellow }
+div:has(+ .test) #subject { color: blue }
+div:has(~ div .test) #subject { color: purple }
+div:has(+ div .test) #subject { color: pink }
+</style>
+
+<main id=main>
+ <div id=subject_ancestor>
+ <div id=subject_parent>
+ <div id=subject>
+ <div id=subject_child>
+ <div id=subject_descendant></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id=next_sibling>
+ <div id=next_sibling_child>
+ <div id=next_sibling_descendant></div>
+ </div>
+ </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+ test(function() {
+ assert_equals(getComputedStyle(subject).color, color);
+ }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+ element.classList.add('test');
+ testColor(`add .test to ${element.id}`, expectedColor);
+ element.classList.remove('test');
+ testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ beforeElement.before(newElement);
+ testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ afterElement.after(newElement);
+ testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ beforeElement.before(newElement);
+ testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ afterElement.after(newElement);
+ testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(subject_ancestor, grey);
+testClassChange(subject_parent, green);
+testClassChange(subject, green);
+testClassChange(subject_child, red);
+testClassChange(subject_descendant, red);
+testClassChange(next_sibling, blue);
+testClassChange(next_sibling_child, pink);
+testClassChange(next_sibling_descendant, pink);
+
+testElementInsertionBefore(subject_ancestor, grey);
+testElementInsertionBefore(subject_parent, green);
+testElementInsertionBefore(subject, green);
+testElementInsertionBefore(subject_child, red);
+testElementInsertionBefore(subject_descendant, red);
+testElementInsertionBefore(next_sibling, blue);
+testElementInsertionBefore(next_sibling_child, pink);
+testElementInsertionBefore(next_sibling_descendant, pink);
+
+testElementInsertionAfter(subject_ancestor, blue);
+testElementInsertionAfter(subject_parent, blue);
+testElementInsertionAfter(subject, green);
+testElementInsertionAfter(subject_child, red);
+testElementInsertionAfter(subject_descendant, red);
+testElementInsertionAfter(next_sibling, yellow);
+testElementInsertionAfter(next_sibling_child, pink);
+testElementInsertionAfter(next_sibling_descendant, pink);
+
+testTreeInsertionBefore(subject_ancestor, grey);
+testTreeInsertionBefore(subject_parent, red);
+testTreeInsertionBefore(subject, red);
+testTreeInsertionBefore(subject_child, red);
+testTreeInsertionBefore(subject_descendant, red);
+testTreeInsertionBefore(next_sibling, pink);
+testTreeInsertionBefore(next_sibling_child, pink);
+testTreeInsertionBefore(next_sibling_descendant, pink);
+
+testTreeInsertionAfter(subject_ancestor, pink);
+testTreeInsertionAfter(subject_parent, pink);
+testTreeInsertionAfter(subject, red);
+testTreeInsertionAfter(subject_child, red);
+testTreeInsertionAfter(subject_descendant, red);
+testTreeInsertionAfter(next_sibling, purple);
+testTreeInsertionAfter(next_sibling_child, pink);
+testTreeInsertionAfter(next_sibling_descendant, pink);
+
+</script>
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,53 @@
+
+PASS Initial color
+PASS add .test to subject_ancestor
+PASS remove .test from subject_ancestor
+PASS add .test to subject_parent
+PASS remove .test from subject_parent
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to subject_child
+PASS remove .test from subject_child
+PASS add .test to subject_descendant
+PASS remove .test from subject_descendant
+PASS insert element div.test before subject_ancestor
+PASS remove element div.test before subject_ancestor
+PASS insert element div.test before subject_parent
+PASS remove element div.test before subject_parent
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before subject_child
+PASS remove element div.test before subject_child
+PASS insert element div.test before subject_descendant
+PASS remove element div.test before subject_descendant
+PASS insert element div.test after subject_ancestor
+PASS remove element div.test after subject_ancestor
+PASS insert element div.test after subject_parent
+PASS remove element div.test after subject_parent
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after subject_child
+PASS remove element div.test after subject_child
+PASS insert element div.test after subject_descendant
+PASS remove element div.test after subject_descendant
+PASS insert tree div>div.test before subject_ancestor
+PASS remove tree div>div.test before subject_ancestor
+PASS insert tree div>div.test before subject_parent
+PASS remove tree div>div.test before subject_parent
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before subject_child
+PASS remove tree div>div.test before subject_child
+PASS insert tree div>div.test before subject_descendant
+PASS remove tree div>div.test before subject_descendant
+PASS insert tree div>div.test after subject_ancestor
+PASS remove tree div>div.test after subject_ancestor
+PASS insert tree div>div.test after subject_parent
+PASS remove tree div>div.test after subject_parent
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after subject_child
+PASS remove tree div>div.test after subject_child
+PASS insert tree div>div.test after subject_descendant
+PASS remove tree div>div.test after subject_descendant
+
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in parent position</title>
+<link rel="author" title="Antti Koivisto" href=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+div, main { color: grey }
+div:has(.test) > #subject { color: red }
+div:has(> .test) > #subject { color: green }
+div:has(~ .test) > #subject { color: yellow }
+div:has(+ .test) > #subject { color: blue }
+div:has(~ div .test) > #subject { color: purple }
+div:has(+ div .test) > #subject { color: pink }
+</style>
+
+<main id=main>
+ <div id=subject_ancestor>
+ <div id=subject_parent>
+ <div id=subject>
+ <div id=subject_child>
+ <div id=subject_descendant></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+ test(function() {
+ assert_equals(getComputedStyle(subject).color, color);
+ }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+ element.classList.add('test');
+ testColor(`add .test to ${element.id}`, expectedColor);
+ element.classList.remove('test');
+ testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ beforeElement.before(newElement);
+ testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ afterElement.after(newElement);
+ testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ beforeElement.before(newElement);
+ testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ afterElement.after(newElement);
+ testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(subject_ancestor, grey);
+testClassChange(subject_parent, grey);
+testClassChange(subject, green);
+testClassChange(subject_child, red);
+testClassChange(subject_descendant, red);
+
+testElementInsertionBefore(subject_ancestor, grey);
+testElementInsertionBefore(subject_parent, grey);
+testElementInsertionBefore(subject, green);
+testElementInsertionBefore(subject_child, red);
+testElementInsertionBefore(subject_descendant, red);
+
+testElementInsertionAfter(subject_ancestor, grey);
+testElementInsertionAfter(subject_parent, blue);
+testElementInsertionAfter(subject, green);
+testElementInsertionAfter(subject_child, red);
+testElementInsertionAfter(subject_descendant, red);
+
+testTreeInsertionBefore(subject_ancestor, grey);
+testTreeInsertionBefore(subject_parent, grey);
+testTreeInsertionBefore(subject, red);
+testTreeInsertionBefore(subject_child, red);
+testTreeInsertionBefore(subject_descendant, red);
+
+testTreeInsertionAfter(subject_ancestor, grey);
+testTreeInsertionAfter(subject_parent, pink);
+testTreeInsertionAfter(subject, red);
+testTreeInsertionAfter(subject_child, red);
+testTreeInsertionAfter(subject_descendant, red);
+
+</script>
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,73 @@
+
+PASS Initial color
+PASS add .test to previous_sibling
+PASS remove .test from previous_sibling
+PASS add .test to previous_sibling_child
+PASS remove .test from previous_sibling_child
+PASS add .test to previous_sibling_descendant
+PASS remove .test from previous_sibling_descendant
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to next_sibling
+PASS remove .test from next_sibling
+PASS add .test to next_sibling_child
+PASS remove .test from next_sibling_child
+PASS add .test to next_sibling_descendant
+PASS remove .test from next_sibling_descendant
+PASS insert element div.test before previous_sibling
+PASS remove element div.test before previous_sibling
+PASS insert element div.test before previous_sibling_child
+PASS remove element div.test before previous_sibling_child
+PASS insert element div.test before previous_sibling_descendant
+PASS remove element div.test before previous_sibling_descendant
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before next_sibling
+PASS remove element div.test before next_sibling
+PASS insert element div.test before next_sibling_child
+PASS remove element div.test before next_sibling_child
+PASS insert element div.test before next_sibling_descendant
+PASS remove element div.test before next_sibling_descendant
+PASS insert element div.test after previous_sibling
+PASS remove element div.test after previous_sibling
+PASS insert element div.test after previous_sibling_child
+PASS remove element div.test after previous_sibling_child
+PASS insert element div.test after previous_sibling_descendant
+PASS remove element div.test after previous_sibling_descendant
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after next_sibling
+PASS remove element div.test after next_sibling
+PASS insert element div.test after next_sibling_child
+PASS remove element div.test after next_sibling_child
+PASS insert element div.test after next_sibling_descendant
+PASS remove element div.test after next_sibling_descendant
+PASS insert tree div>div.test before previous_sibling
+PASS remove tree div>div.test before previous_sibling
+PASS insert tree div>div.test before previous_sibling_child
+PASS remove tree div>div.test before previous_sibling_child
+PASS insert tree div>div.test before previous_sibling_descendant
+PASS remove tree div>div.test before previous_sibling_descendant
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before next_sibling
+PASS remove tree div>div.test before next_sibling
+PASS insert tree div>div.test before next_sibling_child
+PASS remove tree div>div.test before next_sibling_child
+PASS insert tree div>div.test before next_sibling_descendant
+PASS remove tree div>div.test before next_sibling_descendant
+PASS insert tree div>div.test after previous_sibling
+PASS remove tree div>div.test after previous_sibling
+PASS insert tree div>div.test after previous_sibling_child
+PASS remove tree div>div.test after previous_sibling_child
+PASS insert tree div>div.test after previous_sibling_descendant
+PASS remove tree div>div.test after previous_sibling_descendant
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after next_sibling
+PASS remove tree div>div.test after next_sibling
+PASS insert tree div>div.test after next_sibling_child
+PASS remove tree div>div.test after next_sibling_child
+PASS insert tree div>div.test after next_sibling_descendant
+PASS remove tree div>div.test after next_sibling_descendant
+
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html (0 => 286433)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html 2021-12-02 17:36:50 UTC (rev 286433)
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in sibling position</title>
+<link rel="author" title="Antti Koivisto" href=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+div, main { color: grey }
+div:has(.test) ~ #subject { color: red }
+div:has(> .test) ~ #subject { color: green }
+div:has(~ .test) ~ #subject { color: yellow }
+div:has(+ .test) ~ #subject { color: blue }
+div:has(~ div .test) ~ #subject { color: purple }
+div:has(+ div .test) ~ #subject { color: pink }
+</style>
+
+<main id=main>
+ <div id=previous_sibling>
+ <div id=previous_sibling_child>
+ <div id=previous_sibling_descendant></div>
+ </div>
+ </div>
+ <div id=subject></div>
+ <div id=next_sibling>
+ <div id=next_sibling_child>
+ <div id=next_sibling_descendant></div>
+ </div>
+ </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+ test(function() {
+ assert_equals(getComputedStyle(subject).color, color);
+ }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+ element.classList.add('test');
+ testColor(`add .test to ${element.id}`, expectedColor);
+ element.classList.remove('test');
+ testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ beforeElement.before(newElement);
+ testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ newElement.classList.add('test')
+
+ afterElement.after(newElement);
+ testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ beforeElement.before(newElement);
+ testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+ const newElement = document.createElement('div');
+ const newChild = document.createElement('div');
+ newChild.classList.add('test');
+ newElement.appendChild(newChild);
+
+ afterElement.after(newElement);
+ testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+ newElement.remove();
+ testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(previous_sibling, grey);
+testClassChange(previous_sibling_child, green);
+testClassChange(previous_sibling_descendant, red);
+testClassChange(subject, blue);
+testClassChange(next_sibling, yellow);
+testClassChange(next_sibling_child, purple);
+testClassChange(next_sibling_descendant, purple);
+
+testElementInsertionBefore(previous_sibling, grey);
+testElementInsertionBefore(previous_sibling_child, green);
+testElementInsertionBefore(previous_sibling_descendant, red);
+testElementInsertionBefore(subject, blue);
+testElementInsertionBefore(next_sibling, yellow);
+testElementInsertionBefore(next_sibling_child, purple);
+testElementInsertionBefore(next_sibling_descendant, purple);
+
+testElementInsertionAfter(previous_sibling, blue);
+testElementInsertionAfter(previous_sibling_child, green);
+testElementInsertionAfter(previous_sibling_descendant, red);
+testElementInsertionAfter(subject, yellow);
+testElementInsertionAfter(next_sibling, yellow);
+testElementInsertionAfter(next_sibling_child, purple);
+testElementInsertionAfter(next_sibling_descendant, purple);
+
+testTreeInsertionBefore(previous_sibling, green);
+testTreeInsertionBefore(previous_sibling_child, red);
+testTreeInsertionBefore(previous_sibling_descendant, red);
+testTreeInsertionBefore(subject, pink);
+testTreeInsertionBefore(next_sibling, purple);
+testTreeInsertionBefore(next_sibling_child, purple);
+testTreeInsertionBefore(next_sibling_descendant, purple);
+
+testTreeInsertionAfter(previous_sibling, pink);
+testTreeInsertionAfter(previous_sibling_child, red);
+testTreeInsertionAfter(previous_sibling_descendant, red);
+testTreeInsertionAfter(subject, purple);
+testTreeInsertionAfter(next_sibling, purple);
+testTreeInsertionAfter(next_sibling_child, purple);
+testTreeInsertionAfter(next_sibling_descendant, purple);
+
+</script>
Modified: trunk/Source/WebCore/ChangeLog (286432 => 286433)
--- trunk/Source/WebCore/ChangeLog 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/ChangeLog 2021-12-02 17:36:50 UTC (rev 286433)
@@ -1,3 +1,37 @@
+2021-12-02 Antti Koivisto <[email protected]>
+
+ [:has() pseudo-class] Invalidation in non-subject position
+ https://bugs.webkit.org/show_bug.cgi?id=233758
+
+ Reviewed by Simon Fraser.
+
+ Invalidation for selectors like '.ancestor:has(.foo) #target'.
+
+ Tests: imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html
+ imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html
+ imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html
+ imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html
+
+ * style/ChildChangeInvalidation.cpp:
+ (WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement):
+ (WebCore::Style::needsTraversal):
+ (WebCore::Style::needsDescendantTraversal):
+ * style/RuleFeature.cpp:
+ (WebCore::Style::isSiblingOrSubject):
+ (WebCore::Style::isHasPseudoClassMatchElement):
+ (WebCore::Style::computeHasPseudoClassMatchElement):
+ (WebCore::Style::computeSubSelectorMatchElement):
+
+ Use new MatchElement::HasNonSubject as a catch-all for cases where :has() is in a non-subject position.
+ In the future this can be optimized better by computing both the :has match element and the overall
+ match element separately.
+
+ * style/RuleFeature.h:
+ * style/StyleInvalidator.cpp:
+ (WebCore::Style::Invalidator::invalidateStyleWithMatchElement):
+
+ Worst-case invalidation.
+
2021-12-02 Andreu Botella <[email protected]>
File inputs in non-multipart form submissions show up as string values in the formdata event
Modified: trunk/Source/WebCore/style/ChildChangeInvalidation.cpp (286432 => 286433)
--- trunk/Source/WebCore/style/ChildChangeInvalidation.cpp 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/ChildChangeInvalidation.cpp 2021-12-02 17:36:50 UTC (rev 286433)
@@ -75,8 +75,11 @@
for (auto& invalidationRuleSet : *invalidationRuleSets) {
if (!isHasPseudoClassMatchElement(invalidationRuleSet.matchElement))
continue;
- if (isDescendant && invalidationRuleSet.matchElement != MatchElement::HasDescendant)
- continue;
+ if (isDescendant) {
+ // Elements deeper in the tree can't affect anything except when :has() selector uses descendant combinator.
+ if (invalidationRuleSet.matchElement != MatchElement::HasDescendant && invalidationRuleSet.matchElement != MatchElement::HasNonSubject)
+ continue;
+ }
Invalidator::addToMatchElementRuleSets(matchElementRuleSets, invalidationRuleSet);
}
};
@@ -113,11 +116,15 @@
return true;
if (features.usesMatchElement(MatchElement::HasSiblingDescendant))
return true;
+ if (features.usesMatchElement(MatchElement::HasNonSubject))
+ return true;
return features.usesMatchElement(MatchElement::HasSibling) && childChange.previousSiblingElement;
};
static bool needsDescendantTraversal(const RuleFeatureSet& features)
{
+ if (features.usesMatchElement(MatchElement::HasNonSubject))
+ return true;
return features.usesMatchElement(MatchElement::HasDescendant) || features.usesMatchElement(MatchElement::HasSiblingDescendant);
};
Modified: trunk/Source/WebCore/style/RuleFeature.cpp (286432 => 286433)
--- trunk/Source/WebCore/style/RuleFeature.cpp 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/RuleFeature.cpp 2021-12-02 17:36:50 UTC (rev 286433)
@@ -53,6 +53,7 @@
case MatchElement::HasChild:
case MatchElement::HasDescendant:
case MatchElement::HasSiblingDescendant:
+ case MatchElement::HasNonSubject:
return false;
}
ASSERT_NOT_REACHED();
@@ -66,6 +67,7 @@
case MatchElement::HasDescendant:
case MatchElement::HasSibling:
case MatchElement::HasSiblingDescendant:
+ case MatchElement::HasNonSubject:
return true;
default:
return false;
@@ -157,6 +159,7 @@
case MatchElement::HasDescendant:
case MatchElement::HasSibling:
case MatchElement::HasSiblingDescendant:
+ case MatchElement::HasNonSubject:
case MatchElement::Host:
ASSERT_NOT_REACHED();
break;
@@ -176,8 +179,11 @@
if (type == CSSSelector::PseudoClassHost)
return MatchElement::Host;
- if (type == CSSSelector::PseudoClassHas)
+ if (type == CSSSelector::PseudoClassHas) {
+ if (matchElement != MatchElement::Subject)
+ return MatchElement::HasNonSubject;
return computeHasPseudoClassMatchElement(childSelector);
+ }
}
if (selector.match() == CSSSelector::PseudoElement) {
// Similarly for ::slotted().
Modified: trunk/Source/WebCore/style/RuleFeature.h (286432 => 286433)
--- trunk/Source/WebCore/style/RuleFeature.h 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/RuleFeature.h 2021-12-02 17:36:50 UTC (rev 286433)
@@ -36,6 +36,7 @@
class RuleData;
+// FIXME: Has* values should be separated so we could describe both the :has() argument and its position in the selector.
enum class MatchElement : uint8_t {
Subject,
Parent,
@@ -49,6 +50,7 @@
HasDescendant,
HasSibling,
HasSiblingDescendant,
+ HasNonSubject, // FIXME: This is a catch-all for cases where :has() is in non-subject position.
Host
};
constexpr unsigned matchElementCount = static_cast<unsigned>(MatchElement::Host) + 1;
Modified: trunk/Source/WebCore/style/StyleInvalidator.cpp (286432 => 286433)
--- trunk/Source/WebCore/style/StyleInvalidator.cpp 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/StyleInvalidator.cpp 2021-12-02 17:36:50 UTC (rev 286433)
@@ -346,6 +346,11 @@
}
break;
}
+ case MatchElement::HasNonSubject: {
+ SelectorMatchingState selectorMatchingState;
+ invalidateStyleForDescendants(*element.document().documentElement(), &selectorMatchingState);
+ break;
+ }
case MatchElement::Host:
invalidateInShadowTreeIfNeeded(element);
break;