tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

not-pseudo-containing-complex-in-has.html (27355B)


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