mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 09:45:06 +02:00
LibWeb: Delay generic :has() sibling scans until sibling roots
Mark elements reached by stepping through sibling combinators inside :has() and use that breadcrumb during generic invalidation walks. Keep the existing conservative sibling scans for mutations outside those marked subtrees so nested :is(), :not(), and nesting cases continue to invalidate correctly. Also keep :has() eager within compounds that contain ::part(). Those selectors retarget the remaining simple selectors to the part host, so deferring :has() there changes which element the pseudo-class runs against and can make ::part(foo):has(.match) spuriously match. Add a counter-based sibling-scan test and a regression test covering the ::part()/ :has() selector orderings.
This commit is contained in:
committed by
Alexander Kalenik
parent
85ff13870f
commit
7a5b1d9de1
Notes:
github-actions[bot]
2026-04-20 11:21:48 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/7a5b1d9de17 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8982
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<style>
|
||||
.anchor:has(+ .wrapper .match) { color: red; }
|
||||
</style>
|
||||
<div id="outer">
|
||||
<div id="anchor" class="anchor">anchor</div>
|
||||
<div class="wrapper">
|
||||
<div>
|
||||
<div>
|
||||
<span id="target">target</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>unrelated sibling</div>
|
||||
</div>
|
||||
<script>
|
||||
function printCounters() {
|
||||
let counters = internals.getStyleInvalidationCounters();
|
||||
println(`hasAncestorWalkInvocations: ${counters.hasAncestorWalkInvocations}`);
|
||||
println(`hasAncestorWalkVisits: ${counters.hasAncestorWalkVisits}`);
|
||||
println(`hasAncestorSiblingElementChecks: ${counters.hasAncestorSiblingElementChecks}`);
|
||||
println(`hasMatchInvocations: ${counters.hasMatchInvocations}`);
|
||||
println(`hasResultCacheHits: ${counters.hasResultCacheHits}`);
|
||||
println(`hasResultCacheMisses: ${counters.hasResultCacheMisses}`);
|
||||
println(`styleInvalidations: ${counters.styleInvalidations}`);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
let anchor = document.getElementById("anchor");
|
||||
let target = document.getElementById("target");
|
||||
|
||||
getComputedStyle(anchor).color;
|
||||
internals.resetStyleInvalidationCounters();
|
||||
|
||||
target.classList.add("match");
|
||||
getComputedStyle(anchor).color;
|
||||
printCounters();
|
||||
});
|
||||
</script>
|
||||
19
Tests/LibWeb/Text/input/css/part-has-target.html
Normal file
19
Tests/LibWeb/Text/input/css/part-has-target.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
#host::part(p):has(.match) { background-color: red; }
|
||||
#host:has(.match)::part(p) { color: green; }
|
||||
</style>
|
||||
<div id="host">
|
||||
<span class="match"></span>
|
||||
</div>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
const host = document.getElementById("host");
|
||||
const shadow = host.attachShadow({ mode: "open" });
|
||||
shadow.innerHTML = `<span id="inner" part="p">text</span>`;
|
||||
const inner = shadow.getElementById("inner");
|
||||
println(getComputedStyle(inner).backgroundColor);
|
||||
println(getComputedStyle(inner).color);
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user