Skip to content

Commit 2f5741f

Browse files
byung-woochromium-wpt-export-bot
authored andcommitted
Fix :has() invalidation bug when removing anchor element's siblings
This CL fixes a :has() invalidation bug when the following conditions are met: 1. A style rule uses a :has() pseudo class. The :has() test result is affected by the anchor element's relationship to its sibling element at fixed distance. (e.g. '.a:has(+ .b) {}') 2. The :has() pseudo class was tested on an anchor element and it didn't matched. 3. If a sibling of the anchor element is removed, the :has() will match the anchor element. (e.g. '<div class=a></div><div id=target></div><div class=b></div>') 4. Remove a sibling of the anchor element so that the :has() matches the anchor element. (e.g. 'target.remove();') For the removal, StyleEngine have to schedule :has() invalidation even if the removed element doesn't have any identifier stored in RuleFeatureSet. But it is not efficient to schedule :has() invalidation for every element removal. To avoid unnecessary :has() invalidation, StyleEngine checks whether its parent has the 'ChildrenAffectedByDirectAdjacentRules' flag set or not. Currently, the SelectorChecker sets the flag only when it consumes a direct adjacent combinator(+). This works most cases but it doesn't work in this case (condition #2) because the SelectorChecker stops the :has() argument selector matching before consuming the direct adjacent combinator. Due to this, the parent of the anchor element doesn't have the 'ChildrenAffectedByDirectAdjacentRules' flag set and the StyleEngine doesn't schedule the :has() invalidation for the removal. To fix the error, when the SelectorChecker tests a :has() pseudo class on an anchor element and the :has() is affected by the anchor element's relationship to a sibling at fixed distance, the SelectorChecker sets the flag of the parent to indicate that StyleEngine need to schedule :has() invalidation whenever any child of the element is removed. Bug: 1480643 Change-Id: I5ec2e3c1db2773020368415f68bca1503367e669 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4864627 Commit-Queue: Byungwoo Lee <[email protected]> Reviewed-by: Rune Lillesveen <[email protected]> Cr-Commit-Position: refs/heads/main@{#1198137}
1 parent f97de03 commit 2f5741f

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<title>:has() invalidation for sibling insertion and removal</title>
4+
<link rel="author" title="Byungwoo Lee" href="mailto:[email protected]">
5+
<script src="/resources/testharness.js"></script>
6+
<script src="/resources/testharnessreport.js"></script>
7+
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
8+
<style>
9+
div, main { color: grey }
10+
#subject1:has(+ #sibling1_1 + #sibling1_2) { color: red }
11+
#subject2:has(+ #sibling2_2) { color: green }
12+
#subject3:has(+ #sibling3_1 + #sibling3_2 > #siblingchild3_2_1) { color: blue }
13+
#subject4:has(+ #sibling4_2 > #siblingchild4_2_1) { color: yellow }
14+
#subject5:has(+ #sibling5_1 + #sibling5_2) { color: red }
15+
#subject6:has(+ #sibling6_2) { color: green }
16+
#subject7:has(+ #sibling7_1 + #sibling7_2 > #siblingchild7_2_1) { color: blue }
17+
#subject8:has(+ #sibling8_2 > #siblingchild8_2_1) { color: yellow }
18+
#subject9:has(+ #sibling9_1 + #sibling9_2 ~ #sibling9_3) { color: red }
19+
#subject10:has(+ #sibling10_2 ~ #sibling10_3) { color: green }
20+
#subject11:has(+ #sibling11_1 + #sibling11_2 ~ #sibling11_3 > #siblingchild11_3_1) { color: blue }
21+
#subject12:has(+ #sibling12_2 ~ #sibling12_3 > #siblingchild12_3_1) { color: yellow }
22+
</style>
23+
24+
<main id="main">
25+
<div id="parent1">
26+
<div id="subject1"></div>
27+
<div id="sibling1_2"></div>
28+
<div id="sibling1_3"></div>
29+
</div>
30+
<div id="parent2">
31+
<div id="subject2"></div>
32+
<div id="sibling2_1"></div>
33+
<div id="sibling2_2"></div>
34+
<div id="sibling2_3""></div>
35+
</div>
36+
<div id="parent3">
37+
<div id="subject3"></div>
38+
<div id="sibling3_2">
39+
<div id="siblingchild3_2_1"></div>
40+
</div>
41+
<div id="sibling3_3"></div>
42+
</div>
43+
<div id="parent4">
44+
<div id="subject4"></div>
45+
<div id="sibling4_1"></div>
46+
<div id="sibling4_2">
47+
<div id="siblingchild4_2_1"></div>
48+
</div>
49+
<div id="sibling4_3"></div>
50+
</div>
51+
<div id="parent5">
52+
<div id="subject5"></div>
53+
<div id="sibling5_1"></div>
54+
<div id="sibling5_2"></div>
55+
<div id="sibling5_3""></div>
56+
</div>
57+
<div id="parent6">
58+
<div id="subject6"></div>
59+
<div id="sibling6_2"></div>
60+
<div id="sibling6_3"></div>
61+
</div>
62+
<div id="parent7">
63+
<div id="subject7"></div>
64+
<div id="sibling7_1"></div>
65+
<div id="sibling7_2">
66+
<div id="siblingchild7_2_1"></div>
67+
</div>
68+
<div id="sibling7_3"></div>
69+
<div id="parent8">
70+
<div id="subject8"></div>
71+
<div id="sibling8_2">
72+
<div id="siblingchild8_2_1"></div>
73+
</div>
74+
<div id="sibling8_3"></div>
75+
</div>
76+
<div id="parent9">
77+
<div id="subject9"></div>
78+
<div id="sibling9_2"></div>
79+
<div id="sibling9_3"></div>
80+
<div id="sibling9_4"></div>
81+
</div>
82+
<div id="parent10">
83+
<div id="subject10"></div>
84+
<div id="sibling10_1"></div>
85+
<div id="sibling10_2"></div>
86+
<div id="sibling10_3""></div>
87+
<div id="sibling10_4""></div>
88+
</div>
89+
<div id="parent11">
90+
<div id="subject11"></div>
91+
<div id="sibling11_2"></div>
92+
<div id="sibling11_3">
93+
<div id="siblingchild11_3_1"></div>
94+
</div>
95+
<div id="sibling11_4"></div>
96+
</div>
97+
<div id="parent12">
98+
<div id="subject12"></div>
99+
<div id="sibling12_1"></div>
100+
<div id="sibling12_2"></div>
101+
<div id="sibling12_3">
102+
<div id="siblingchild12_3_1"></div>
103+
</div>
104+
<div id="sibling12_4"></div>
105+
</div>
106+
</main>
107+
<script>
108+
109+
const grey = 'rgb(128, 128, 128)';
110+
const red = 'rgb(255, 0, 0)';
111+
const green = 'rgb(0, 128, 0)';
112+
const blue = 'rgb(0, 0, 255)';
113+
const yellow = 'rgb(255, 255, 0)';
114+
115+
function testColor(test_name, element, color) {
116+
test(function() {
117+
assert_equals(getComputedStyle(element).color, color);
118+
}, test_name);
119+
}
120+
121+
function insertBefore(parent, reference, child_id) {
122+
var child = document.createElement("div");
123+
child.id = child_id;
124+
parent.insertBefore(child, reference);
125+
}
126+
127+
testColor(`subject1: initial color should be ${grey}`, subject1, grey);
128+
insertBefore(parent1, sibling1_2, "sibling1_1");
129+
testColor(`subject1: color after #sibling1_1 inserted should be ${red}`,
130+
subject1, red);
131+
132+
testColor(`subject2: initial color should be ${grey}`, subject2, grey);
133+
sibling2_1.remove();
134+
testColor(`subject2: color after #sibling2_1 removed should be ${green}`,
135+
subject2, green);
136+
137+
testColor(`subject3: initial color should be ${grey}`, subject3, grey);
138+
insertBefore(parent3, sibling3_2, "sibling3_1");
139+
testColor(`subject3: color after #sibling3_1 inserted should be ${blue}`,
140+
subject3, blue);
141+
142+
testColor(`subject4: initial color should be ${grey}`, subject4, grey);
143+
sibling4_1.remove();
144+
testColor(`subject4: color after #sibling4_1 removed should be ${yellow}`,
145+
subject4, yellow);
146+
147+
testColor(`subject5: initial color should be ${red}`, subject5, red);
148+
sibling5_1.remove();
149+
testColor(`subject5: color after #sibling5_1 removed should be ${grey}`,
150+
subject5, grey);
151+
152+
testColor(`subject6: initial color should be ${green}`, subject6, green);
153+
insertBefore(parent6, sibling6_2, "sibling6_1");
154+
testColor(`subject6: color after #sibling6_1 inserted should be ${grey}`,
155+
subject6, grey);
156+
157+
testColor(`subject7: initial color should be ${blue}`, subject7, blue);
158+
sibling7_1.remove();
159+
testColor(`subject7: color after #sibling7_1 removed should be ${grey}`,
160+
subject7, grey);
161+
162+
testColor(`subject8: initial color should be ${yellow}`, subject8, yellow);
163+
insertBefore(parent8, sibling8_2, "sibling8_1");
164+
testColor(`subject8: color after #sibling8_1 inserted should be ${grey}`,
165+
subject8, grey);
166+
167+
testColor(`subject9: initial color should be ${grey}`, subject9, grey);
168+
insertBefore(parent9, sibling9_2, "sibling9_1");
169+
testColor(`subject9: color after #sibling9_1 inserted should be ${red}`,
170+
subject1, red);
171+
172+
testColor(`subject10: initial color should be ${grey}`, subject10, grey);
173+
sibling10_1.remove();
174+
testColor(`subject10: color after #sibling10_1 removed should be ${green}`,
175+
subject10, green);
176+
177+
testColor(`subject11: initial color should be ${grey}`, subject11, grey);
178+
insertBefore(parent11, sibling11_2, "sibling11_1");
179+
testColor(`subject11: color after #sibling11_1 inserted should be ${blue}`,
180+
subject11, blue);
181+
182+
testColor(`subject12: initial color should be ${grey}`, subject12, grey);
183+
sibling12_1.remove();
184+
testColor(`subject12: color after #sibling12_1 removed should be ${yellow}`,
185+
subject12, yellow);
186+
</script>

0 commit comments

Comments
 (0)