<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<style id="testsheet"></style>
<div id="t1">
<span></span>
</div>
<div id="t2">
<span></span>
</div>
<div id="t3">
<div class="second"></div>
<div></div>
<div></div>
<div></div>
</div>
<div id="t4">
<div></div>
<div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div id="t4_green1"></div>
</div>
<div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div></div>
<div></div>
<div></div>
<div id="t4_green2"></div>
</div>
</div>
<div id="t5">
<div></div>
<div>
<div>
<div></div>
<div></div>
<div class="descendant"></div>
<div></div>
</div>
<div></div>
</div>
<div></div>
<div></div>
</div>
<div id="t6">
<div></div>
<div><div class="descendant"></div></div>
<div id="nth"><div class="descendant"></div></div>
<div class="sibling"><div class="descendant"></div></div>
<div><div class="descendant"></div></div>
<div><div class="descendant"></div></div>
<div><div class="descendant"></div></div>
<div><div class="descendant"></div></div>
<div><div class="descendant"></div></div>
<div><div class="descendant"></div></div>
</div>
<script>
function setSelectorForTest(selector) {
testsheet.innerText = selector + " { background-color: green }";
}
function backgroundIsGreen(element) {
assert_equals(getComputedStyle(element).backgroundColor, "rgb(0, 128, 0)");
}
function backgroundIsTransparent(element) {
assert_equals(getComputedStyle(element).backgroundColor, "rgba(0, 0, 0, 0)");
}
setSelectorForTest("#t1 > span:nth-child(even)");
test(() => {
t1.offsetTop;
assert_equals(t1.lastChild.nodeType, Node.TEXT_NODE);
t1.insertBefore(document.createElement("span"), t1.lastChild);
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 1);
}, "Appending an element sibling should not affect :nth-child of preceding siblings.");
setSelectorForTest("#t2 > span:nth-last-child(even)");
test(() => {
t2.offsetTop;
assert_equals(t2.firstChild.nodeType, Node.TEXT_NODE);
assert_equals(t2.firstChild.nextSibling.nodeType, Node.ELEMENT_NODE);
t2.insertBefore(document.createElement("span"), t2.firstChild.nextSibling);
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 1);
}, "Prepending an element sibling should not affect :nth-last-child of succeeding siblings.");
setSelectorForTest("#t3 > .second:nth-child(2)");
test(() => {
t3.offsetTop;
let second = t3.querySelector(".second");
backgroundIsTransparent(second);
t3.insertBefore(document.createElement("div"), t3.firstChild);
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 2);
backgroundIsGreen(second);
}, "Prepending an element sibling causing :nth-child class invalidation.");
setSelectorForTest("#t4 div:nth-of-type(10)");
test(() => {
t4.offsetTop;
backgroundIsGreen(t4_green1);
backgroundIsGreen(t4_green2);
}, "div:nth-of-type(10) matching 10th div sibling.");
test(() => {
t4.firstElementChild.remove();
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 2);
}, "div:nth-of-type(10) invalidating all siblings, but no descendants, on removal.");
test(() => {
t4.insertBefore(document.createElement("div"), t4.firstChild);
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 3);
}, "div:nth-of-type(10) invalidating all siblings, but no descendants, on insertion.");
test(() => {
t4.lastElementChild.firstElementChild.remove();
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 9);
backgroundIsTransparent(t4_green2);
}, "div:nth-of-type(10) changing style on sibling removal.");
test(() => {
t4.lastElementChild.insertBefore(document.createElement("div"), t4.lastElementChild.firstChild);
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 10);
backgroundIsGreen(t4_green2);
}, "div:nth-of-type(10) changing style on sibling insertion.");
setSelectorForTest("#t5 div:nth-of-type(2) .descendant");
let descendant = t5.querySelector(".descendant");
test(() => {
t5.offsetTop;
backgroundIsGreen(descendant);
t5.firstElementChild.remove();
// The expected count should be 1, but it's actually 4 due to
// "summary:fist-of-type" in the UA stylesheet.
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 4);
backgroundIsTransparent(descendant);
}, "Removing nth-sibling affecting targeted descendant.");
test(() => {
t5.insertBefore(document.createElement("div"), t5.firstChild);
// The expected count should be 2, but it's actually 5 due to
// "summary:fist-of-type" in the UA stylesheet.
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 5);
backgroundIsGreen(descendant);
}, "Inserting nth-sibling affecting targeted descendant.");
// The #nth id is necessary to avoid UniversalSiblingInvalidationSet
// invalidating all siblings on insertion/removal, because those
// invalidation sets are still scheduled on the parent node as descedant
// invalidations.
setSelectorForTest("#t6 #nth:nth-of-type(2n) + .sibling .descendant");
descendant = t6.querySelector(".sibling .descendant");
test(() => {
t6.offsetTop;
backgroundIsTransparent(descendant);
t6.firstElementChild.remove();
// The expected count should be 1, but it's actually 2 due to
// "summary:fist-of-type" in the UA stylesheet.
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 2);
backgroundIsGreen(descendant);
}, "Removing nth-sibling affecting targeted descendant with sibling invalidation.");
test(() => {
t6.insertBefore(document.createElement("div"), t6.firstChild);
// The expected count should be 2, but it's actually 3 due to
// "summary:fist-of-type" in the UA stylesheet.
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 3);
backgroundIsTransparent(descendant);
}, "Inserting nth-sibling affecting targeted descendant with sibling invalidation.");
</script>