LibWeb: Skip unstyled descendants in animation updates

Animation updates propagate inherited animated values by walking the
animated target's subtree and calling `recompute_inherited_style()` on
each element. Elements inserted during the same rendering update may
not have computed properties yet, which violates an existing assertion,
causing a crash.

Fix this by skipping unstyled descendants during this walk. The
subsequent recursive style update computes their style from scratch.
This commit is contained in:
Tim Ledbetter 2026-04-22 08:35:14 +01:00 committed by Andreas Kling
parent f426f021ef
commit 06ee7bc532
2 changed files with 27 additions and 0 deletions

View file

@ -822,6 +822,8 @@ AnimationUpdateContext::~AnimationUpdateContext()
// Traversal of the subtree is necessary to update the animated properties inherited from the target element.
target->for_each_in_subtree_of_type<DOM::Element>([&](auto& element) {
if (!element.cascaded_properties({}))
return TraversalDecision::SkipChildrenAndContinue;
auto element_invalidation = element.recompute_inherited_style();
if (element_invalidation.is_none())
return TraversalDecision::SkipChildrenAndContinue;

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html class="test-wait">
<style>
@keyframes anim { from { opacity: 1 } to { opacity: 0.5 } }
#target { animation: anim 10s infinite; }
</style>
<div id="target"></div>
<script>
const animation = target.getAnimations()[0];
function waitForAnimationTick() {
if (animation.currentTime === null || animation.currentTime === 0) {
requestAnimationFrame(waitForAnimationTick);
return;
}
target.appendChild(document.createElement("div"));
requestAnimationFrame(() => {
document.documentElement.classList.remove("test-wait");
});
}
requestAnimationFrame(waitForAnimationTick);
</script>
</html>