Files
ladybird/Tests/LibWeb/Text/input/css-has-invalidation/has-feature-metadata-buckets.html
Andreas Kling e1d62eaf85 LibWeb: Bucket :has() invalidation metadata by feature
Record per-feature :has() invalidation metadata instead of only tracking
whether some selector somewhere mentions a class, id, attribute, tag,
or pseudo-class. The new buckets preserve the relative selector and a
coarse scope classification for each :has() argument, which gives the
next invalidation step enough information to route mutations more
precisely.

Keep this commit behavior-preserving for mutation handling by only
switching the lookup path over to the new metadata buckets. Expose a
test-only counter for the number of candidate :has() metadata entries a
mutation matched, and add coverage showing that one feature can map to
one or multiple :has() buckets without forcing a document-wide yes/no
answer.
2026-04-20 13:20:41 +02:00

54 lines
2.0 KiB
HTML

<!DOCTYPE html>
<script src="../include.js"></script>
<script src="_helpers.js"></script>
<style>
.anchor-a:has(.match-a) { color: red; }
.anchor-b:has(.match-b) { color: red; }
.anchor-c:has(.shared) { color: red; }
.anchor-d:has(.shared) { color: red; }
</style>
<div class="anchor-a"><span class="match-a">a</span></div>
<div class="anchor-b"><span class="match-b">b</span></div>
<div class="anchor-c"><span class="shared">c</span></div>
<div class="anchor-d"><span class="shared">d</span></div>
<div id="outside">
<span id="unrelated-target">u</span>
<span id="single-target">s</span>
<span id="shared-target">m</span>
</div>
<script>
function printMetadataCounters(label) {
const counters = internals.getStyleInvalidationCounters();
println(`[${label}]`);
println(` hasInvalidationMetadataCandidates: ${counters.hasInvalidationMetadataCandidates}`);
println(` hasAncestorWalkInvocations: ${counters.hasAncestorWalkInvocations}`);
println(` hasAncestorWalkVisits: ${counters.hasAncestorWalkVisits}`);
println(` styleInvalidations: ${counters.styleInvalidations}`);
}
test(() => {
let unrelatedTarget = document.getElementById("unrelated-target");
let singleTarget = document.getElementById("single-target");
let sharedTarget = document.getElementById("shared-target");
settleAndReset();
unrelatedTarget.classList.add("unrelated");
getComputedStyle(unrelatedTarget).color;
printMetadataCounters("add unrelated class");
internals.resetStyleInvalidationCounters();
singleTarget.classList.add("match-a");
getComputedStyle(singleTarget).color;
printMetadataCounters("add class referenced by one :has()");
internals.resetStyleInvalidationCounters();
sharedTarget.classList.add("shared");
getComputedStyle(sharedTarget).color;
printMetadataCounters("add class referenced by two :has() rules");
});
</script>