diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index a11589fc14c..cfc3816ee1d 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -2502,8 +2502,10 @@ GC::Ref StyleComputer::compute_properties(DOM::Element& elem effect->set_target(&element); element.set_cached_animation_name_animation(animation, pseudo_element); - HTML::TemporaryExecutionContext context(realm); - animation->play().release_value_but_fixme_should_propagate_errors(); + if (!element.has_display_none_ancestor()) { + HTML::TemporaryExecutionContext context(realm); + animation->play().release_value_but_fixme_should_propagate_errors(); + } } else { // The animation hasn't changed, but some properties of the animation may have if (auto animation = element.cached_animation_name_animation(pseudo_element); animation) diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 8d9850dc7e7..5f58afe20f1 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -2459,8 +2462,69 @@ void Element::set_cascaded_properties(Optionalparent_or_shadow_host()) { + if (!ancestor->is_element()) + continue; + auto const& ancestor_element = static_cast(*ancestor); + if (ancestor_element.computed_properties() && ancestor_element.computed_properties()->display().is_none()) { + return true; + } + } + return false; +} + +void Element::play_or_cancel_animations_after_display_property_change(Optional old_display, Optional new_display) +{ + // https://www.w3.org/TR/css-animations-1/#animations + // Setting the display property to none will terminate any running animation applied to the element and its descendants. + // If an element has a display of none, updating display to a value other than none will start all animations applied to + // the element by the animation-name property, as well as all animations applied to descendants with display other than none. + + if (has_display_none_ancestor()) + return; + + bool previous_display_is_none = old_display.has_value() && old_display->is_none(); + bool new_display_is_none = new_display.has_value() && new_display->is_none(); + if (previous_display_is_none == new_display_is_none) + return; + + auto play_or_cancel_depending_on_display = [&](Animations::Animation& animation) { + if (new_display_is_none) { + animation.cancel(); + } else { + HTML::TemporaryExecutionContext context(realm()); + animation.play().release_value_but_fixme_should_propagate_errors(); + } + }; + + for_each_shadow_including_inclusive_descendant([&](auto& node) { + if (!node.is_element()) + return TraversalDecision::Continue; + + auto const& element = static_cast(node); + if (auto animation = element.cached_animation_name_animation({})) + play_or_cancel_depending_on_display(*animation); + for (auto i = 0; i < to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount); i++) { + auto pseudo_element = static_cast(i); + if (auto animation = element.cached_animation_name_animation(pseudo_element)) + play_or_cancel_depending_on_display(*animation); + } + return TraversalDecision::Continue; + }); +} + void Element::set_computed_properties(GC::Ptr style) { + Optional old_display; + Optional new_display; + if (m_computed_properties) + old_display = m_computed_properties->display(); + if (style) + new_display = style->display(); + play_or_cancel_animations_after_display_property_change(old_display, new_display); + m_computed_properties = style; computed_properties_changed(); } diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index dbb5350aaee..c56fd088249 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -209,6 +209,8 @@ public: void set_pseudo_element_computed_properties(CSS::Selector::PseudoElement::Type, GC::Ptr); GC::Ptr pseudo_element_computed_properties(CSS::Selector::PseudoElement::Type); + bool has_display_none_ancestor(); + void play_or_cancel_animations_after_display_property_change(Optional old_display, Optional new_display); void reset_animated_css_properties(); GC::Ptr inline_style() { return m_inline_style; } diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-animations/display-none-prevents-starting-in-subtree.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-animations/display-none-prevents-starting-in-subtree.txt new file mode 100644 index 00000000000..9184ab91eb9 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-animations/display-none-prevents-starting-in-subtree.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass Elements in a "display: none" tree cannot start CSS Animations. \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-display/animations/display-interpolation.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-display/animations/display-interpolation.txt index 5c254a2c34e..ccbc18b1349 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-display/animations/display-interpolation.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-display/animations/display-interpolation.txt @@ -2,8 +2,8 @@ Harness status: OK Found 42 tests -28 Pass -14 Fail +31 Pass +11 Fail Pass CSS Transitions with transition-behavior:allow-discrete: property from [none] to [flex] at (-0.3) should be [flex] Pass CSS Transitions with transition-behavior:allow-discrete: property from [none] to [flex] at (0) should be [flex] Pass CSS Transitions with transition-behavior:allow-discrete: property from [none] to [flex] at (0.3) should be [flex] @@ -32,9 +32,9 @@ Pass CSS Transitions with transition: all: property from [none] to [fl Pass CSS Transitions with transition: all: property from [none] to [flex] at (0.6) should be [flex] Pass CSS Transitions with transition: all: property from [none] to [flex] at (1) should be [flex] Pass CSS Transitions with transition: all: property from [none] to [flex] at (1.5) should be [flex] -Fail CSS Animations: property from [none] to [flex] at (-0.3) should be [block] -Fail CSS Animations: property from [none] to [flex] at (0) should be [block] -Fail CSS Animations: property from [none] to [flex] at (0.3) should be [block] +Pass CSS Animations: property from [none] to [flex] at (-0.3) should be [block] +Pass CSS Animations: property from [none] to [flex] at (0) should be [block] +Pass CSS Animations: property from [none] to [flex] at (0.3) should be [block] Fail CSS Animations: property from [none] to [flex] at (0.5) should be [block] Fail CSS Animations: property from [none] to [flex] at (0.6) should be [block] Fail CSS Animations: property from [none] to [flex] at (1) should be [block] diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-animations/display-none-prevents-starting-in-subtree.html b/Tests/LibWeb/Text/input/wpt-import/css/css-animations/display-none-prevents-starting-in-subtree.html new file mode 100644 index 00000000000..c1c1d49c2a3 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-animations/display-none-prevents-starting-in-subtree.html @@ -0,0 +1,35 @@ + + + + + + + +
+
+
+
+
+