1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
|
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Selectors Invalidation: :is() 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>
div { color: grey }
.red:has(#descendant:is(.a_has_scope .b)) { color: red }
.orangered:has(#descendant:is(.a_descendant .b)) #descendant { color: orangered }
.darkred:has(#descendant:is(.a_indirect_next .b)) ~ #indirect_next { color: darkred }
.pink:has(#descendant:is(.a_indirect_next_child .b)) ~ #indirect_next #indirect_next_child { color: pink }
.green:has(#descendant:is(.p + .c_has_scope ~ .d .e)) { color: green }
.lightgreen:has(#descendant:is(.p + .c_descendant ~ .d .e)) #descendant { color: lightgreen }
.darkgreen:has(#descendant:is(.p + .c_indirect_next ~ .d .e)) ~ #indirect_next { color: darkgreen }
.yellowgreen:has(#descendant:is(.p + .c_indirect_next_child ~ .d .e)) ~ #indirect_next #indirect_next_child { color: yellowgreen }
.blue:has(~ #indirect_next:is(.p + .f_has_scope ~ .g)) { color: blue }
.skyblue:has(~ #indirect_next:is(.p + .f_descendant ~ .g)) #descendant { color: skyblue }
.lightblue:has(~ #indirect_next:is(.p + .f_indirect_next ~ .g)) ~ #indirect_next { color: lightblue }
.darkblue:has(~ #indirect_next:is(.p + .f_indirect_next_child ~ .g)) ~ #indirect_next #indirect_next_child { color: darkblue }
.yellow:has(~ #indirect_next:is(.h_has_scope .i)) { color: yellow }
.ivory:has(~ #indirect_next:is(.h_descendant .i)) #descendant { color: ivory }
.greenyellow:has(~ #indirect_next:is(.h_indirect_next .i)) ~ #indirect_next { color: greenyellow }
.khaki:has(~ #indirect_next:is(.h_indirect_next_child .i)) ~ #indirect_next #indirect_next_child { color: khaki }
.purple:has(~ #indirect_next:is(.p + .j_has_scope ~ .k .l)) { color: purple }
.violet:has(~ #indirect_next:is(.p + .j_descendant ~ .k .l)) #descendant { color: violet }
.orchid:has(~ #indirect_next:is(.p + .j_indirect_next ~ .k .l)) ~ #indirect_next { color: orchid }
.plum:has(~ #indirect_next:is(.p + .j_indirect_next_child ~ .k .l)) ~ #indirect_next #indirect_next_child { color: plum }
.orange:has(#descendant:is(:is(.m, .n) .o)) { color: orange }
</style>
<div>
<div class="p"></div>
<div id="parent_previous"></div>
<div id="parent" class="d k">
<div class="p"></div>
<div id="previous"></div>
<div class="p"></div>
<div id="has_scope" class="d">
<div class="p"></div>
<div id="child_previous"></div>
<div id="child" class="d">
<div id="descendant" class="b e o"></div>
</div>
</div>
<div class="p"></div>
<div id="direct_next"></div>
<div id="indirect_next" class="g i l">
<div id="indirect_next_child"></div>
</div>
</div>
</div>
<script>
const grey = "rgb(128, 128, 128)";
const red = "rgb(255, 0, 0)";
const orangered = "rgb(255, 69, 0)";
const darkred = "rgb(139, 0, 0)";
const pink = "rgb(255, 192, 203)";
const green = "rgb(0, 128, 0)";
const lightgreen = "rgb(144, 238, 144)";
const darkgreen = "rgb(0, 100, 0)";
const yellowgreen = "rgb(154, 205, 50)";
const blue = "rgb(0, 0, 255)";
const skyblue = "rgb(135, 206, 235)";
const lightblue = "rgb(173, 216, 230)";
const darkblue = "rgb(0, 0, 139)";
const yellow = "rgb(255, 255, 0)";
const ivory = "rgb(255, 255, 240)";
const greenyellow = "rgb(173, 255, 47)";
const khaki = "rgb(240, 230, 140)";
const purple = "rgb(128, 0, 128)";
const violet = "rgb(238, 130, 238)";
const orchid = "rgb(218, 112, 214)";
const plum = "rgb(221, 160, 221)";
const orange = "rgb(255, 165, 0)";
function addClass(element, class_name) {
element.classList.add(class_name);
}
function removeClass(element, class_name) {
element.classList.remove(class_name);
}
function testClassChange(operation, class_name, element_id,
selector, matches_result,
subject_id, subject_color) {
let element = document.getElementById(element_id);
assert_equals(element ? element.id : "", element_id);
let subject = document.getElementById(subject_id);
assert_equals(subject ? subject.id : "", subject_id);
let message_prefix = [
"[", selector, "]",
["#", element.id, ".classList.",
(operation == addClass ? "add" : "remove"),
"('", class_name, "')"].join(""),
": "].join(" ");
operation(element, class_name);
test(function() {
assert_equals(subject.matches(selector), matches_result);
}, message_prefix + "check matches (" + matches_result + ")");
test(function() {
assert_equals(getComputedStyle(subject).color, subject_color);
}, message_prefix + "check #" + subject_id + " color");
}
function testSiblingInsertionRemoval(class_name, insert_before_id, selector,
subject_id,
insertion_matches_result,
insertion_subject_color,
removal_matches_result,
removal_subject_color) {
let insert_before = document.getElementById(insert_before_id);
assert_equals(insert_before ? insert_before.id : "", insert_before_id);
let parent = insert_before.parentElement;
let subject = document.getElementById(subject_id);
assert_equals(subject ? subject.id : "", subject_id);
let message_prefix = [
"[", selector, "]",
["insert/remove .",
class_name, " before #", insert_before.id, ")"].join(""),
": "].join(" ");
let div = document.createElement("div");
div.classList.add(class_name);
parent.insertBefore(div, insert_before);
test(function() {
assert_equals(subject.matches(selector), insertion_matches_result);
}, message_prefix + "(insertion) check matches (" +
insertion_matches_result + ")");
test(function() {
assert_equals(getComputedStyle(subject).color, insertion_subject_color);
}, message_prefix + "(insertion) check #" + subject_id + " color");
div.remove();
test(function() {
assert_equals(subject.matches(selector), removal_matches_result);
}, message_prefix + "(removal) check matches (" +
removal_matches_result + ")");
test(function() {
assert_equals(getComputedStyle(subject).color, removal_subject_color);
}, message_prefix + "(removal) check #" + subject_id + " color");
}
assert_equals(getComputedStyle(has_scope).color, grey);
let selector = ".red:has(#descendant:is(.a_has_scope .b))";
testClassChange(addClass, "red", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "a_has_scope", "parent", selector, true, "has_scope", red);
testClassChange(removeClass, "a_has_scope", "parent", selector, false, "has_scope", grey);
testClassChange(addClass, "a_has_scope", "has_scope", selector, true, "has_scope", red);
testClassChange(removeClass, "a_has_scope", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "a_has_scope", "child", selector, true, "has_scope", red);
testClassChange(removeClass, "a_has_scope", "child", selector, false, "has_scope", grey);
testClassChange(removeClass, "red", "has_scope", selector, false, "has_scope", grey);
selector = ".orangered:has(#descendant:is(.a_descendant .b)) #descendant";
testClassChange(addClass, "orangered", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "a_descendant", "parent", selector, true, "descendant", orangered);
testClassChange(removeClass, "a_descendant", "parent", selector, false, "descendant", grey);
testClassChange(addClass, "a_descendant", "has_scope", selector, true, "descendant", orangered);
testClassChange(removeClass, "a_descendant", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "a_descendant", "child", selector, true, "descendant", orangered);
testClassChange(removeClass, "a_descendant", "child", selector, false, "descendant", grey);
testClassChange(removeClass, "orangered", "has_scope", selector, false, "descendant", grey);
selector = ".darkred:has(#descendant:is(.a_indirect_next .b)) ~ #indirect_next";
testClassChange(addClass, "darkred", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "a_indirect_next", "parent", selector, true, "indirect_next", darkred);
testClassChange(removeClass, "a_indirect_next", "parent", selector, false, "indirect_next", grey);
testClassChange(addClass, "a_indirect_next", "has_scope", selector, true, "indirect_next", darkred);
testClassChange(removeClass, "a_indirect_next", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "a_indirect_next", "child", selector, true, "indirect_next", darkred);
testClassChange(removeClass, "a_indirect_next", "child", selector, false, "indirect_next", grey);
testClassChange(removeClass, "darkred", "has_scope", selector, false, "indirect_next", grey);
selector = ".pink:has(#descendant:is(.a_indirect_next_child .b)) ~ #indirect_next #indirect_next_child";
testClassChange(addClass, "pink", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "a_indirect_next_child", "parent", selector, true, "indirect_next_child", pink);
testClassChange(removeClass, "a_indirect_next_child", "parent", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "a_indirect_next_child", "has_scope", selector, true, "indirect_next_child", pink);
testClassChange(removeClass, "a_indirect_next_child", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "a_indirect_next_child", "child", selector, true, "indirect_next_child", pink);
testClassChange(removeClass, "a_indirect_next_child", "child", selector, false, "indirect_next_child", grey);
testClassChange(removeClass, "pink", "has_scope", selector, false, "indirect_next_child", grey);
selector = ".green:has(#descendant:is(.p + .c_has_scope ~ .d .e))";
testClassChange(addClass, "green", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "c_has_scope", "parent_previous", selector, true, "has_scope", green);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "has_scope", false, grey, true, green);
testClassChange(removeClass, "c_has_scope", "parent_previous", selector, false, "has_scope", grey);
testSiblingInsertionRemoval("c_has_scope", "parent_previous", selector, "has_scope", true, green, false, grey);
testClassChange(addClass, "c_has_scope", "previous", selector, true, "has_scope", green);
testSiblingInsertionRemoval("invalid", "previous", selector, "has_scope", false, grey, true, green);
testClassChange(removeClass, "c_has_scope", "previous", selector, false, "has_scope", grey);
testSiblingInsertionRemoval("c_has_scope", "previous", selector, "has_scope", true, green, false, grey);
testClassChange(addClass, "c_has_scope", "child_previous", selector, true, "has_scope", green);
testSiblingInsertionRemoval("invalid", "child_previous", selector, "has_scope", false, grey, true, green);
testClassChange(removeClass, "c_has_scope", "child_previous", selector, false, "has_scope", grey);
testSiblingInsertionRemoval("c_has_scope", "child_previous", selector, "has_scope", true, green, false, grey);
testClassChange(removeClass, "green", "has_scope", selector, false, "has_scope", grey);
selector = ".lightgreen:has(#descendant:is(.p + .c_descendant ~ .d .e)) #descendant";
testClassChange(addClass, "lightgreen", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "c_descendant", "parent_previous", selector, true, "descendant", lightgreen);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "descendant", false, grey, true, lightgreen);
testClassChange(removeClass, "c_descendant", "parent_previous", selector, false, "descendant", grey);
testSiblingInsertionRemoval("c_descendant", "parent_previous", selector, "descendant", true, lightgreen, false, grey);
testClassChange(addClass, "c_descendant", "previous", selector, true, "descendant", lightgreen);
testSiblingInsertionRemoval("invalid", "previous", selector, "descendant", false, grey, true, lightgreen);
testClassChange(removeClass, "c_descendant", "previous", selector, false, "descendant", grey);
testSiblingInsertionRemoval("c_descendant", "previous", selector, "descendant", true, lightgreen, false, grey);
testClassChange(addClass, "c_descendant", "child_previous", selector, true, "descendant", lightgreen);
testSiblingInsertionRemoval("invalid", "child_previous", selector, "descendant", false, grey, true, lightgreen);
testClassChange(removeClass, "c_descendant", "child_previous", selector, false, "descendant", grey);
testSiblingInsertionRemoval("c_descendant", "child_previous", selector, "descendant", true, lightgreen, false, grey);
testClassChange(removeClass, "lightgreen", "has_scope", selector, false, "descendant", grey);
selector = ".darkgreen:has(#descendant:is(.p + .c_indirect_next ~ .d .e)) ~ #indirect_next";
testClassChange(addClass, "darkgreen", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "c_indirect_next", "parent_previous", selector, true, "indirect_next", darkgreen);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "indirect_next", false, grey, true, darkgreen);
testClassChange(removeClass, "c_indirect_next", "parent_previous", selector, false, "indirect_next", grey);
testSiblingInsertionRemoval("c_indirect_next", "parent_previous", selector, "indirect_next", true, darkgreen, false, grey);
testClassChange(addClass, "c_indirect_next", "previous", selector, true, "indirect_next", darkgreen);
testSiblingInsertionRemoval("invalid", "previous", selector, "indirect_next", false, grey, true, darkgreen);
testClassChange(removeClass, "c_indirect_next", "previous", selector, false, "indirect_next", grey);
testSiblingInsertionRemoval("c_indirect_next", "previous", selector, "indirect_next", true, darkgreen, false, grey);
testClassChange(addClass, "c_indirect_next", "child_previous", selector, true, "indirect_next", darkgreen);
testSiblingInsertionRemoval("invalid", "child_previous", selector, "indirect_next", false, grey, true, darkgreen);
testClassChange(removeClass, "c_indirect_next", "child_previous", selector, false, "indirect_next", grey);
testSiblingInsertionRemoval("c_indirect_next", "child_previous", selector, "indirect_next", true, darkgreen, false, grey);
testClassChange(removeClass, "darkgreen", "has_scope", selector, false, "indirect_next", grey);
selector = ".yellowgreen:has(#descendant:is(.p + .c_indirect_next_child ~ .d .e)) ~ #indirect_next #indirect_next_child";
testClassChange(addClass, "yellowgreen", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "c_indirect_next_child", "parent_previous", selector, true, "indirect_next_child", yellowgreen);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "indirect_next_child", false, grey, true, yellowgreen);
testClassChange(removeClass, "c_indirect_next_child", "parent_previous", selector, false, "indirect_next_child", grey);
testSiblingInsertionRemoval("c_indirect_next_child", "parent_previous", selector, "indirect_next_child", true, yellowgreen, false, grey);
testClassChange(addClass, "c_indirect_next_child", "previous", selector, true, "indirect_next_child", yellowgreen);
testSiblingInsertionRemoval("invalid", "previous", selector, "indirect_next_child", false, grey, true, yellowgreen);
testClassChange(removeClass, "c_indirect_next_child", "previous", selector, false, "indirect_next_child", grey);
testSiblingInsertionRemoval("c_indirect_next_child", "previous", selector, "indirect_next_child", true, yellowgreen, false, grey);
testClassChange(addClass, "c_indirect_next_child", "child_previous", selector, true, "indirect_next_child", yellowgreen);
testSiblingInsertionRemoval("invalid", "child_previous", selector, "indirect_next_child", false, grey, true, yellowgreen);
testClassChange(removeClass, "c_indirect_next_child", "child_previous", selector, false, "indirect_next_child", grey);
testSiblingInsertionRemoval("c_indirect_next_child", "child_previous", selector, "indirect_next_child", true, yellowgreen, false, grey);
testClassChange(removeClass, "yellowgreen", "has_scope", selector, false, "indirect_next_child", grey);
selector = ".blue:has(~ #indirect_next:is(.p + .f_has_scope ~ .g))";
testClassChange(addClass, "blue", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "f_has_scope", "previous", selector, true, "has_scope", blue);
testSiblingInsertionRemoval("invalid", "previous", selector, "has_scope", false, grey, true, blue);
testClassChange(removeClass, "f_has_scope", "previous", selector, false, "has_scope", grey);
testSiblingInsertionRemoval("f_has_scope", "previous", selector, "has_scope", true, blue, false, grey);
testClassChange(addClass, "f_has_scope", "has_scope", selector, true, "has_scope", blue);
testClassChange(removeClass, "f_has_scope", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "f_has_scope", "direct_next", selector, true, "has_scope", blue);
testSiblingInsertionRemoval("invalid", "direct_next", selector, "has_scope", false, grey, true, blue);
testClassChange(removeClass, "f_has_scope", "direct_next", selector, false, "has_scope", grey);
testSiblingInsertionRemoval("f_has_scope", "direct_next", selector, "has_scope", true, blue, false, grey);
testClassChange(removeClass, "blue", "has_scope", selector, false, "has_scope", grey);
selector = ".skyblue:has(~ #indirect_next:is(.p + .f_descendant ~ .g)) #descendant";
testClassChange(addClass, "skyblue", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "f_descendant", "previous", selector, true, "descendant", skyblue);
testSiblingInsertionRemoval("invalid", "previous", selector, "descendant", false, grey, true, skyblue);
testClassChange(removeClass, "f_descendant", "previous", selector, false, "descendant", grey);
testSiblingInsertionRemoval("f_descendant", "previous", selector, "descendant", true, skyblue, false, grey);
testClassChange(addClass, "f_descendant", "has_scope", selector, true, "descendant", skyblue);
testClassChange(removeClass, "f_descendant", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "f_descendant", "direct_next", selector, true, "descendant", skyblue);
testSiblingInsertionRemoval("invalid", "direct_next", selector, "descendant", false, grey, true, skyblue);
testClassChange(removeClass, "f_descendant", "direct_next", selector, false, "descendant", grey);
testSiblingInsertionRemoval("f_descendant", "direct_next", selector, "descendant", true, skyblue, false, grey);
testClassChange(removeClass, "skyblue", "has_scope", selector, false, "descendant", grey);
selector = ".lightblue:has(~ #indirect_next:is(.p + .f_indirect_next ~ .g)) ~ #indirect_next";
testClassChange(addClass, "lightblue", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "f_indirect_next", "previous", selector, true, "indirect_next", lightblue);
testSiblingInsertionRemoval("invalid", "previous", selector, "indirect_next", false, grey, true, lightblue);
testClassChange(removeClass, "f_indirect_next", "previous", selector, false, "indirect_next", grey);
testSiblingInsertionRemoval("f_indirect_next", "previous", selector, "indirect_next", true, lightblue, false, grey);
testClassChange(addClass, "f_indirect_next", "has_scope", selector, true, "indirect_next", lightblue);
testClassChange(removeClass, "f_indirect_next", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "f_indirect_next", "direct_next", selector, true, "indirect_next", lightblue);
testSiblingInsertionRemoval("invalid", "direct_next", selector, "indirect_next", false, grey, true, lightblue);
testClassChange(removeClass, "f_indirect_next", "direct_next", selector, false, "indirect_next", grey);
testSiblingInsertionRemoval("f_indirect_next", "direct_next", selector, "indirect_next", true, lightblue, false, grey);
testClassChange(removeClass, "lightblue", "has_scope", selector, false, "indirect_next", grey);
selector = ".darkblue:has(~ #indirect_next:is(.p + .f_indirect_next_child ~ .g)) ~ #indirect_next #indirect_next_child";
testClassChange(addClass, "darkblue", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "f_indirect_next_child", "previous", selector, true, "indirect_next_child", darkblue);
testSiblingInsertionRemoval("invalid", "previous", selector, "indirect_next_child", false, grey, true, darkblue);
testClassChange(removeClass, "f_indirect_next_child", "previous", selector, false, "indirect_next_child", grey);
testSiblingInsertionRemoval("f_indirect_next_child", "previous", selector, "indirect_next_child", true, darkblue, false, grey);
testClassChange(addClass, "f_indirect_next_child", "has_scope", selector, true, "indirect_next_child", darkblue);
testClassChange(removeClass, "f_indirect_next_child", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "f_indirect_next_child", "direct_next", selector, true, "indirect_next_child", darkblue);
testSiblingInsertionRemoval("invalid", "direct_next", selector, "indirect_next_child", false, grey, true, darkblue);
testClassChange(removeClass, "f_indirect_next_child", "direct_next", selector, false, "indirect_next_child", grey);
testSiblingInsertionRemoval("f_indirect_next_child", "direct_next", selector, "indirect_next_child", true, darkblue, false, grey);
testClassChange(removeClass, "darkblue", "has_scope", selector, false, "indirect_next_child", grey);
selector = ".yellow:has(~ #indirect_next:is(.h_has_scope .i))"
testClassChange(addClass, "yellow", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "h_has_scope", "parent", selector, true, "has_scope", yellow);
testClassChange(removeClass, "h_has_scope", "parent", selector, false, "has_scope", grey);
testClassChange(removeClass, "yellow", "has_scope", selector, false, "has_scope", grey);
selector = ".ivory:has(~ #indirect_next:is(.h_descendant .i)) #descendant";
testClassChange(addClass, "ivory", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "h_descendant", "parent", selector, true, "descendant", ivory);
testClassChange(removeClass, "h_descendant", "parent", selector, false, "descendant", grey);
testClassChange(removeClass, "ivory", "has_scope", selector, false, "descendant", grey);
selector = ".greenyellow:has(~ #indirect_next:is(.h_indirect_next .i)) ~ #indirect_next";
testClassChange(addClass, "greenyellow", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "h_indirect_next", "parent", selector, true, "indirect_next", greenyellow);
testClassChange(removeClass, "h_indirect_next", "parent", selector, false, "indirect_next", grey);
testClassChange(removeClass, "greenyellow", "has_scope", selector, false, "indirect_next", grey);
selector = ".khaki:has(~ #indirect_next:is(.h_indirect_next_child .i)) ~ #indirect_next #indirect_next_child";
testClassChange(addClass, "khaki", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "h_indirect_next_child", "parent", selector, true, "indirect_next_child", khaki);
testClassChange(removeClass, "h_indirect_next_child", "parent", selector, false, "indirect_next_child", grey);
testClassChange(removeClass, "khaki", "has_scope", selector, false, "indirect_next_child", grey);
selector = ".purple:has(~ #indirect_next:is(.p + .j_has_scope ~ .k .l))"
testClassChange(addClass, "purple", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "j_has_scope", "parent_previous", selector, true, "has_scope", purple);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "has_scope", false, grey, true, purple);
testClassChange(removeClass, "j_has_scope", "parent_previous", selector, false, "has_scope", grey);
testSiblingInsertionRemoval("j_has_scope", "parent_previous", selector, "has_scope", true, purple, false, grey);
testClassChange(removeClass, "purple", "has_scope", selector, false, "has_scope", grey);
selector = ".violet:has(~ #indirect_next:is(.p + .j_descendant ~ .k .l)) #descendant";
testClassChange(addClass, "violet", "has_scope", selector, false, "descendant", grey);
testClassChange(addClass, "j_descendant", "parent_previous", selector, true, "descendant", violet);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "descendant", false, grey, true, violet);
testClassChange(removeClass, "j_descendant", "parent_previous", selector, false, "descendant", grey);
testSiblingInsertionRemoval("j_descendant", "parent_previous", selector, "descendant", true, violet, false, grey);
testClassChange(removeClass, "violet", "has_scope", selector, false, "descendant", grey);
selector = ".orchid:has(~ #indirect_next:is(.p + .j_indirect_next ~ .k .l)) ~ #indirect_next";
testClassChange(addClass, "orchid", "has_scope", selector, false, "indirect_next", grey);
testClassChange(addClass, "j_indirect_next", "parent_previous", selector, true, "indirect_next", orchid);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "indirect_next", false, grey, true, orchid);
testClassChange(removeClass, "j_indirect_next", "parent_previous", selector, false, "indirect_next", grey);
testSiblingInsertionRemoval("j_indirect_next", "parent_previous", selector, "indirect_next", true, orchid, false, grey);
testClassChange(removeClass, "orchid", "has_scope", selector, false, "indirect_next", grey);
selector = ".plum:has(~ #indirect_next:is(.p + .j_indirect_next_child ~ .k .l)) ~ #indirect_next #indirect_next_child";
testClassChange(addClass, "plum", "has_scope", selector, false, "indirect_next_child", grey);
testClassChange(addClass, "j_indirect_next_child", "parent_previous", selector, true, "indirect_next_child", plum);
testSiblingInsertionRemoval("invalid", "parent_previous", selector, "indirect_next_child", false, grey, true, plum);
testClassChange(removeClass, "j_indirect_next_child", "parent_previous", selector, false, "indirect_next_child", grey);
testSiblingInsertionRemoval("j_indirect_next_child", "parent_previous", selector, "indirect_next_child", true, plum, false, grey);
testClassChange(removeClass, "plum", "has_scope", selector, false, "indirect_next_child", grey);
selector = ".orange:has(#descendant:is(:is(.m, .n) .o))";
testClassChange(addClass, "orange", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "m", "parent", selector, true, "has_scope", orange);
testClassChange(removeClass, "m", "parent", selector, false, "has_scope", grey);
testClassChange(addClass, "n", "parent", selector, true, "has_scope", orange);
testClassChange(removeClass, "n", "parent", selector, false, "has_scope", grey);
testClassChange(addClass, "m", "has_scope", selector, true, "has_scope", orange);
testClassChange(removeClass, "m", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "n", "has_scope", selector, true, "has_scope", orange);
testClassChange(removeClass, "n", "has_scope", selector, false, "has_scope", grey);
testClassChange(addClass, "m", "child", selector, true, "has_scope", orange);
testClassChange(removeClass, "m", "child", selector, false, "has_scope", grey);
testClassChange(addClass, "n", "child", selector, true, "has_scope", orange);
testClassChange(removeClass, "n", "child", selector, false, "has_scope", grey);
testClassChange(removeClass, "orange", "has_scope", selector, false, "has_scope", grey);
</script>
|