Skip to content
Permalink
Browse files
Fix invalidation with scope breaking :is/not()
https://bugs.webkit.org/show_bug.cgi?id=241098

Reviewed by Alan Bujtas.

Selector like :has(:is(foo bar)) can be affected by mutations outside the :has scope.

* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/child-indexed-pseudo-classes-in-has-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/child-indexed-pseudo-classes-in-has.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/empty-pseudo-in-has-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/empty-pseudo-in-has.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/is-pseudo-containing-complex-in-has-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/is-pseudo-containing-complex-in-has.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/link-pseudo-in-has-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/link-pseudo-in-has.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/not-pseudo-containing-complex-in-has-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/not-pseudo-containing-complex-in-has.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/typed-child-indexed-pseudo-classes-in-has-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/typed-child-indexed-pseudo-classes-in-has.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/w3c-import.log:

Reimport selector invalidation tests.

* Source/WebCore/style/ChildChangeInvalidation.cpp:
(WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement):
(WebCore::Style::needsDescendantTraversal):
* Source/WebCore/style/RuleFeature.cpp:
(WebCore::Style::isSiblingOrSubject):
(WebCore::Style::isHasPseudoClassMatchElement):
(WebCore::Style::computeNextMatchElement):
(WebCore::Style::computeHasPseudoClassMatchElement):
(WebCore::Style::computeSubSelectorMatchElement):
(WebCore::Style::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):

Detect the case and fall back to very wide invalidation (same a non-subject :has() for now).

* Source/WebCore/style/RuleFeature.h:
(WebCore::Style::RuleFeatureSet::usesHasPseudoClass const):
* Source/WebCore/style/StyleInvalidator.cpp:
(WebCore::Style::Invalidator::invalidateStyleWithMatchElement):

Canonical link: https://commits.webkit.org/251130@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295035 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
anttijk committed May 30, 2022
1 parent 784afb3 commit 4704ce8f0d8079b2acdaf9241a0aa46547132f99
Show file tree
Hide file tree
Showing 21 changed files with 1,499 additions and 282 deletions.
@@ -0,0 +1,68 @@

PASS Initial colors: #only_child
PASS Initial colors: #first_child
PASS Initial colors: #last_child
PASS Initial colors: #nth_child_3n_1
PASS Initial colors: #nth_child_3n_2
PASS Initial colors: #nth_child_3n
PASS Prepend #div1.green: #only_child
PASS Prepend #div1.green: #first_child
PASS Prepend #div1.green: #last_child
PASS Prepend #div1.green: #nth_child_3n_1
PASS Prepend #div1.green: #nth_child_3n_2
PASS Prepend #div1.green: #nth_child_3n
FAIL Prepend #div2.yellow: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
PASS Prepend #div2.yellow: #first_child
PASS Prepend #div2.yellow: #last_child
PASS Prepend #div2.yellow: #nth_child_3n_1
FAIL Prepend #div2.yellow: #nth_child_3n_2 assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
PASS Prepend #div2.yellow: #nth_child_3n
FAIL Prepend #div3.orange: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
PASS Prepend #div3.orange: #first_child
PASS Prepend #div3.orange: #last_child
PASS Prepend #div3.orange: #nth_child_3n_1
FAIL Prepend #div3.orange: #nth_child_3n_2 assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)"
FAIL Prepend #div3.orange: #nth_child_3n assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
FAIL Prepend #div4: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
FAIL Prepend #div4: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Prepend #div4: #last_child
FAIL Prepend #div4: #nth_child_3n_1 assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 165, 0)"
FAIL Prepend #div4: #nth_child_3n_2 assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
FAIL Prepend #div4: #nth_child_3n assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)"
FAIL Prepend #div5: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
FAIL Prepend #div5: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Prepend #div5: #last_child
FAIL Prepend #div5: #nth_child_3n_1 assert_equals: expected "rgb(255, 255, 0)" but got "rgb(255, 165, 0)"
FAIL Prepend #div5: #nth_child_3n_2 assert_equals: expected "rgb(0, 128, 0)" but got "rgb(128, 128, 128)"
FAIL Prepend #div5: #nth_child_3n assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
FAIL Remove #div1: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
FAIL Remove #div1: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Remove #div1: #last_child
FAIL Remove #div1: #nth_child_3n_1 assert_equals: expected "rgb(255, 255, 0)" but got "rgb(255, 165, 0)"
PASS Remove #div1: #nth_child_3n_2
FAIL Remove #div1: #nth_child_3n assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
FAIL Remove #div2: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
FAIL Remove #div2: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Remove #div2: #last_child
PASS Remove #div2: #nth_child_3n_1
PASS Remove #div2: #nth_child_3n_2
FAIL Remove #div2: #nth_child_3n assert_equals: expected "rgb(255, 165, 0)" but got "rgb(128, 128, 128)"
FAIL Remove #div3: #only_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 0, 0)"
FAIL Remove #div3: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Remove #div3: #last_child
PASS Remove #div3: #nth_child_3n_1
PASS Remove #div3: #nth_child_3n_2
PASS Remove #div3: #nth_child_3n
PASS Remove #div4: #only_child
FAIL Remove #div4: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Remove #div4: #last_child
PASS Remove #div4: #nth_child_3n_1
PASS Remove #div4: #nth_child_3n_2
PASS Remove #div4: #nth_child_3n
PASS Remove #div5: #only_child
FAIL Remove #div5: #first_child assert_equals: expected "rgb(128, 128, 128)" but got "rgb(255, 165, 0)"
PASS Remove #div5: #last_child
PASS Remove #div5: #nth_child_3n_1
PASS Remove #div5: #nth_child_3n_2
PASS Remove #div5: #nth_child_3n

@@ -0,0 +1,116 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Selectors Invalidation: child-indexed pseudo classes in :has() argument</title>
<link rel="author" title="Byungwoo Lee" href="blee@igalia.com">
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#container ~ div { color: grey }
#container:has(:only-child) ~ #only_child { color: red }
#container:has(.orange:first-child) ~ #first_child { color: orange }
#container:has(.yellow:first-child) ~ #first_child { color: yellow }
#container:has(.green:first-child) ~ #first_child { color: green }
#container:has(.orange:last-child) ~ #last_child { color: orange }
#container:has(.yellow:last-child) ~ #last_child { color: yellow }
#container:has(.green:last-child) ~ #last_child { color: green }
#container:has(.orange:nth-child(3n)) ~ #nth_child_3n { color: orange }
#container:has(.yellow:nth-child(3n)) ~ #nth_child_3n { color: yellow }
#container:has(.green:nth-child(3n)) ~ #nth_child_3n { color: green }
#container:has(.orange:nth-child(3n+1)) ~ #nth_child_3n_1 { color: orange }
#container:has(.yellow:nth-child(3n+1)) ~ #nth_child_3n_1 { color: yellow }
#container:has(.green:nth-child(3n+1)) ~ #nth_child_3n_1 { color: green }
#container:has(.orange:nth-child(3n+2)) ~ #nth_child_3n_2 { color: orange }
#container:has(.yellow:nth-child(3n+2)) ~ #nth_child_3n_2 { color: yellow }
#container:has(.green:nth-child(3n+2)) ~ #nth_child_3n_2 { color: green }
#container:has(.orange:nth-child(3n)) ~ #nth_child_3n { color: orange }
#container:has(.yellow:nth-child(3n)) ~ #nth_child_3n { color: yellow }
#container:has(.green:nth-child(3n)) ~ #nth_child_3n { color: green }
</style>
<div id="container">
</div>
<div id="only_child"></div>
<div id="first_child"></div>
<div id="last_child"></div>
<div id="nth_child_3n_1"></div>
<div id="nth_child_3n_2"></div>
<div id="nth_child_3n"></div>
<script>
const grey = "rgb(128, 128, 128)";
const red = "rgb(255, 0, 0)";
const orange = "rgb(255, 165, 0)";
const yellow = "rgb(255, 255, 0)";
const green = "rgb(0, 128, 0)";

function testColors(test_name,
only_child_color,
first_child_color,
last_child_color,
nth_child_3n_1_color,
nth_child_3n_2_color,
nth_child_3n_color) {
test(function() {
assert_equals(getComputedStyle(only_child).color, only_child_color);
}, test_name + ": #only_child");
test(function() {
assert_equals(getComputedStyle(first_child).color, first_child_color);
}, test_name + ": #first_child");
test(function() {
assert_equals(getComputedStyle(last_child).color, last_child_color);
}, test_name + ": #last_child");
test(function() {
assert_equals(getComputedStyle(nth_child_3n_1).color, nth_child_3n_1_color);
}, test_name + ": #nth_child_3n_1");
test(function() {
assert_equals(getComputedStyle(nth_child_3n_2).color, nth_child_3n_2_color);
}, test_name + ": #nth_child_3n_2");
test(function() {
assert_equals(getComputedStyle(nth_child_3n).color, nth_child_3n_color);
}, test_name + ": #nth_child_3n");
}

testColors("Initial colors", grey, grey, grey, grey, grey, grey);

let div1 = document.createElement("div");
div1.id = "div1";
div1.classList.add("green");
container.insertBefore(div1, container.firstChild);
testColors("Prepend #div1.green", red, green, green, green, grey, grey);

let div2 = document.createElement("div");
div2.id = "div2";
div2.classList.add("yellow");
container.insertBefore(div2, container.firstChild);
testColors("Prepend #div2.yellow", grey, yellow, green, yellow, green, grey);

let div3 = document.createElement("div");
div3.id = "div3";
div3.classList.add("orange");
container.insertBefore(div3, container.firstChild);
testColors("Prepend #div3.orange", grey, orange, green, orange, yellow, green);

let div4 = document.createElement("div");
div4.id = "div4";
container.insertBefore(div4, container.firstChild);
testColors("Prepend #div4", grey, grey, green, green, orange, yellow);

let div5 = document.createElement("div");
div5.id = "div5";
container.insertBefore(div5, container.firstChild);
testColors("Prepend #div5", grey, grey, green, yellow, green, orange);

div1.remove();
testColors("Remove #div1", grey, grey, yellow, yellow, grey, orange);

div2.remove();
testColors("Remove #div2", grey, grey, orange, grey, grey, orange);

div3.remove();
testColors("Remove #div3", grey, grey, grey, grey, grey, grey);

div4.remove();
testColors("Remove #div4", red, grey, grey, grey, grey, grey);

div5.remove();
testColors("Remove #div5", grey, grey, grey, grey, grey, grey);
</script>
@@ -0,0 +1,5 @@

PASS Empty #subject
PASS Insert div#child to #subject
PASS Insert div to div.#child

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Selectors Invalidation: :empty in :has() argument</title>
<link rel="author" title="Byungwoo Lee" href="blee@igalia.com">
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#subject { color: red }
#subject:has(:empty) { color: green }
#subject:has(:not(:empty)) { color: blue }
</style>
<div id="subject"></div>
<script>
const red = 'rgb(255, 0, 0)';
const green = 'rgb(0, 128, 0)';
const blue = 'rgb(0, 0, 255)';

function testColor(test_name, color) {
test(function() {
assert_equals(getComputedStyle(subject).color, color);
}, test_name);
}

testColor("Empty #subject", red);

let child = document.createElement("div");
child.id = "child";
subject.appendChild(child);

testColor("Insert div#child to #subject", green);

child.appendChild(document.createElement("div"));

testColor("Insert div to div.#child", blue);

</script>

0 comments on commit 4704ce8

Please sign in to comment.