LibWeb: Invalidate pseudo-elements on hover even when not yet created

The hover invalidation code only tried matching ::before/::after
selectors when has_pseudo_element() returned true, which requires an
existing layout node. A pseudo-element that doesn't exist yet (because
its content is only set by a hover rule) has no layout node, so the
match was skipped and hovering never triggered a style recompute.
Always try ::before/::after selectors during hover invalidation.
This commit is contained in:
Jelle Raaijmakers 2026-03-24 13:35:58 +01:00 committed by Tim Flynn
parent 77b9fcf7f9
commit 16dffe39a0
5 changed files with 43 additions and 44 deletions

View file

@ -2076,14 +2076,10 @@ void Document::invalidate_style_for_elements_affected_by_pseudo_class_change(CSS
SelectorEngine::MatchContext context;
if (SelectorEngine::matches(selector, element, {}, context, {}))
return true;
if (element.has_pseudo_element(CSS::PseudoElement::Before)) {
if (SelectorEngine::matches(selector, element, {}, context, CSS::PseudoElement::Before))
return true;
}
if (element.has_pseudo_element(CSS::PseudoElement::After)) {
if (SelectorEngine::matches(selector, element, {}, context, CSS::PseudoElement::After))
return true;
}
if (SelectorEngine::matches(selector, element, {}, context, CSS::PseudoElement::Before))
return true;
if (SelectorEngine::matches(selector, element, {}, context, CSS::PseudoElement::After))
return true;
return false;
};

View file

@ -0,0 +1,3 @@
update: 16 create: 0
update: 78 create: 33
update: 16 create: 0

View file

@ -1,3 +0,0 @@
Not hovering: 16
Hovering: 78
Not hovering: 16

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<style>
.outer {
height: 100px;
}
.update {
display: inline-block;
}
.update::before {
content: "Hi";
}
.outer:hover .update::before {
content: "Long text";
}
.create {
display: inline-block;
}
.outer:hover .create::before {
content: "Flag";
}
</style>
<script src="../include.js"></script>
<div class="outer"><div class="update"></div><div class="create"></div></div>
<script>
test(() => {
const update = document.querySelector('.update');
const create = document.querySelector('.create');
println(`update: ${update.clientWidth} create: ${create.clientWidth}`);
internals.mouseMove(50, 50);
println(`update: ${update.clientWidth} create: ${create.clientWidth}`);
internals.mouseMove(200, 200);
println(`update: ${update.clientWidth} create: ${create.clientWidth}`);
});
</script>

View file

@ -1,33 +0,0 @@
<!doctype html>
<style>
.outer {
height: 100px;
}
.inner {
display: inline-block;
}
.inner::before {
content: "Hi";
background-color: red;
}
.outer:hover .inner::before {
content: "Long text";
background-color: lime;
}
</style>
<script src="../include.js"></script>
<div class="outer"><div class="inner"></div></div>
<script>
test(() => {
const inner = document.querySelector('.inner');
println('Not hovering: ' + inner.clientWidth);
// Move mouse over .outer
internals.mouseMove(80, 80);
println('Hovering: ' + inner.clientWidth);
// Move mouse away again
internals.mouseMove(200, 200);
println('Not hovering: ' + inner.clientWidth);
});
</script>