[Web Animations] Fix a host of small CSS Animations and CSS Transitions issues
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Apr 2018 21:25:56 +0000 (21:25 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Apr 2018 21:25:56 +0000 (21:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184555

Reviewed by Dean Jackson.

Source/WebCore:

A series of small and intertwined issues were preventing a number of CSS Animations and CSS Trantions
tests to fail when expressed as Web Animations.

* animation/AnimationTimeline.cpp:
(WebCore::AnimationTimeline::updateCSSAnimationsForElement): Pass the old and new RenderStyles to CSSAnimation::create()
since we're refactoring DeclarativeAnimation to create blending keyframes in initialize(), no longer requiring each subclass
to create them.
(WebCore::AnimationTimeline::cssAnimationForElementAndProperty): Return the animation, if any, for a property animated by
a CSS animation, so that we can determine if a property is already being animated when running a CSS Transition.
(WebCore::AnimationTimeline::updateCSSTransitionsForElement): Since a property can be specified twice in a "transition" property,
once via "all" and once explicitly, ensure we look at all currently running transitions for the currently-processed property to
see if we need to cancel this transition. Previously, we used to only see if it had been transitioned in the old style. We also
start transitions even if the duration is 0 provided the delay is a positive, non-zero value. Then, if there is a CSS Animation
for this property already running, use that animation's original unanimated style as the from value.
* animation/AnimationTimeline.h:
* animation/CSSAnimation.cpp:
(WebCore::CSSAnimation::create): The animation name is now set in the CSSAnimation constructor.
(WebCore::CSSAnimation::CSSAnimation): Set the animation name and keep a copy of the unanimated style such that we can
get it when a CSS Transition is created and takes precedence over this CSS Animation.
(WebCore::CSSAnimation::initialize): Deleted.
* animation/CSSAnimation.h:
* animation/CSSTransition.cpp:
(WebCore::CSSTransition::create):
(WebCore::CSSTransition::initialize):
* animation/CSSTransition.h:
* animation/DeclarativeAnimation.cpp:
(WebCore::DeclarativeAnimation::initialize): Call the new computeDeclarativeAnimationBlendingKeyframes() on the KeyframeEffect
directly in this method so that subclasses don't need to create it manually, but most important so that keyframes are created
before timing properties are set based on the backing animation.
* animation/DeclarativeAnimation.h:
* animation/KeyframeEffectReadOnly.cpp:
(WebCore::KeyframeEffectReadOnly::getKeyframes): Ensure we have a CSSValue before trying to serialize it.
(WebCore::KeyframeEffectReadOnly::computeDeclarativeAnimationBlendingKeyframes):
(WebCore::KeyframeEffectReadOnly::computeCSSAnimationBlendingKeyframes): Use the animation's unanimated style to compute keyframes,
instead of a default RenderStyle which would not use the right values for implicit keyframes.
(WebCore::KeyframeEffectReadOnly::stylesWouldYieldNewCSSTransitionsBlendingKeyframes const): Look at the property used to create
the transition rather than that specified on the backing Animation object since it can be CSSPropertyInvalid in the case of
"transition: all".
(WebCore::KeyframeEffectReadOnly::setAnimatedPropertiesInStyle): If we're dealing with a CSS animation, we consider the first and
last keyframes to always have the property listed since the underlying style was provided and should be captured.
* animation/KeyframeEffectReadOnly.h:
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::createAnimatedElementUpdate): Apply CSS Animations after CSS Trasitions since they take precedence.

LayoutTests:

Mark more tests as passing when the CSS Animations and CSS Transitions as Web Animations flag is on.

* animations/animation-border-overflow.html:
* animations/lineheight-animation.html:
* animations/missing-from-to-transforms.html:
* animations/missing-values-first-keyframe.html:
* animations/missing-values-last-keyframe.html:
* animations/transition-and-animation-1.html:
* animations/transition-and-animation-2.html:
* animations/transition-and-animation-3.html:
* animations/width-using-ems.html:
* compositing/layer-creation/mismatched-rotated-transform-animation-overlap.html:
* compositing/layer-creation/multiple-keyframes-animation-overlap.html:
* compositing/layer-creation/scale-rotation-animation-overlap.html:
* compositing/layer-creation/translate-scale-animation-overlap.html:
* css3/filters/filter-animation-from-none-hw.html:
* css3/filters/filter-animation-from-none-multi-hw.html:
* css3/filters/filter-animation-from-none-multi.html:
* css3/filters/filter-animation-from-none.html:
* imported/blink/transitions/unprefixed-transform.html:
* transitions/interrupted-all-transition.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230595 268f45cc-cd09-0410-ab3c-d52691b4dbfc

32 files changed:
LayoutTests/ChangeLog
LayoutTests/animations/animation-border-overflow.html
LayoutTests/animations/lineheight-animation.html
LayoutTests/animations/missing-from-to-transforms.html
LayoutTests/animations/missing-values-first-keyframe.html
LayoutTests/animations/missing-values-last-keyframe.html
LayoutTests/animations/transition-and-animation-1.html
LayoutTests/animations/transition-and-animation-2.html
LayoutTests/animations/transition-and-animation-3.html
LayoutTests/animations/width-using-ems.html
LayoutTests/compositing/layer-creation/mismatched-rotated-transform-animation-overlap.html
LayoutTests/compositing/layer-creation/multiple-keyframes-animation-overlap.html
LayoutTests/compositing/layer-creation/scale-rotation-animation-overlap.html
LayoutTests/compositing/layer-creation/translate-scale-animation-overlap.html
LayoutTests/css3/filters/filter-animation-from-none-hw.html
LayoutTests/css3/filters/filter-animation-from-none-multi-hw.html
LayoutTests/css3/filters/filter-animation-from-none-multi.html
LayoutTests/css3/filters/filter-animation-from-none.html
LayoutTests/imported/blink/transitions/unprefixed-transform.html
LayoutTests/transitions/interrupted-all-transition.html
Source/WebCore/ChangeLog
Source/WebCore/animation/AnimationTimeline.cpp
Source/WebCore/animation/AnimationTimeline.h
Source/WebCore/animation/CSSAnimation.cpp
Source/WebCore/animation/CSSAnimation.h
Source/WebCore/animation/CSSTransition.cpp
Source/WebCore/animation/CSSTransition.h
Source/WebCore/animation/DeclarativeAnimation.cpp
Source/WebCore/animation/DeclarativeAnimation.h
Source/WebCore/animation/KeyframeEffectReadOnly.cpp
Source/WebCore/animation/KeyframeEffectReadOnly.h
Source/WebCore/style/StyleTreeResolver.cpp

index 4eef99f..3ebe163 100644 (file)
@@ -1,3 +1,32 @@
+2018-04-12  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Fix a host of small CSS Animations and CSS Transitions issues
+        https://bugs.webkit.org/show_bug.cgi?id=184555
+
+        Reviewed by Dean Jackson.
+
+        Mark more tests as passing when the CSS Animations and CSS Transitions as Web Animations flag is on.
+
+        * animations/animation-border-overflow.html:
+        * animations/lineheight-animation.html:
+        * animations/missing-from-to-transforms.html:
+        * animations/missing-values-first-keyframe.html:
+        * animations/missing-values-last-keyframe.html:
+        * animations/transition-and-animation-1.html:
+        * animations/transition-and-animation-2.html:
+        * animations/transition-and-animation-3.html:
+        * animations/width-using-ems.html:
+        * compositing/layer-creation/mismatched-rotated-transform-animation-overlap.html:
+        * compositing/layer-creation/multiple-keyframes-animation-overlap.html:
+        * compositing/layer-creation/scale-rotation-animation-overlap.html:
+        * compositing/layer-creation/translate-scale-animation-overlap.html:
+        * css3/filters/filter-animation-from-none-hw.html:
+        * css3/filters/filter-animation-from-none-multi-hw.html:
+        * css3/filters/filter-animation-from-none-multi.html:
+        * css3/filters/filter-animation-from-none.html:
+        * imported/blink/transitions/unprefixed-transform.html:
+        * transitions/interrupted-all-transition.html:
+
 2018-04-12  Keith Rollin  <krollin@apple.com>
 
         Fix flakiness in insecure-iframe-in-main-frame.html
index c571659..fb2f5aa 100644 (file)
@@ -1,3 +1,4 @@
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 <html>
 <head>
 <title>Unfilled Animation Test</title>
index 47abb78..0c72cbc 100644 (file)
@@ -1,5 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html lang="en">
 <head>
index 2fbc1b5..debeaa3 100644 (file)
@@ -1,5 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html lang="en">
 <head>
index d8798d1..58a5f21 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 <html>
 <head>
   <style type="text/css" media="screen">
index 9c7a90d..d38d5d0 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 <html>
 <head>
   <style type="text/css" media="screen">
index 7ce8040..180c2e9 100644 (file)
@@ -1,5 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html lang="en">
 <head>
index 803ccb5..653af30 100644 (file)
@@ -1,5 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html lang="en">
 <head>
index 8e7d6d9..9c4849c 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html>
 <head>
index 5241317..2241422 100644 (file)
@@ -1,5 +1,4 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-   "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html lang="en">
 <head>
index 6d31b28..878009a 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html>
 <head>
index fccb343..5d55a30 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html>
 <head>
index 2189b30..4753056 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html>
 <head>
index e6006ca..6164ee2 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html>
 <head>
index f7eb93b..9fb8684 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 <div class="target">-webkit-transform 10ms</div><br>
 <div class="target">transform 10ms</div><br>
 <div class="target">transform 10ms, -webkit-transform 10ms</div><br>
index 9e66cd4..3b9ad2f 100644 (file)
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
 
 <html>
 <head>
index 32741be..e0e6b2f 100644 (file)
@@ -1,5 +1,56 @@
 2018-04-12  Antoine Quint  <graouts@apple.com>
 
+        [Web Animations] Fix a host of small CSS Animations and CSS Transitions issues
+        https://bugs.webkit.org/show_bug.cgi?id=184555
+
+        Reviewed by Dean Jackson.
+
+        A series of small and intertwined issues were preventing a number of CSS Animations and CSS Trantions
+        tests to fail when expressed as Web Animations.
+
+        * animation/AnimationTimeline.cpp:
+        (WebCore::AnimationTimeline::updateCSSAnimationsForElement): Pass the old and new RenderStyles to CSSAnimation::create()
+        since we're refactoring DeclarativeAnimation to create blending keyframes in initialize(), no longer requiring each subclass
+        to create them.
+        (WebCore::AnimationTimeline::cssAnimationForElementAndProperty): Return the animation, if any, for a property animated by
+        a CSS animation, so that we can determine if a property is already being animated when running a CSS Transition.
+        (WebCore::AnimationTimeline::updateCSSTransitionsForElement): Since a property can be specified twice in a "transition" property,
+        once via "all" and once explicitly, ensure we look at all currently running transitions for the currently-processed property to
+        see if we need to cancel this transition. Previously, we used to only see if it had been transitioned in the old style. We also
+        start transitions even if the duration is 0 provided the delay is a positive, non-zero value. Then, if there is a CSS Animation
+        for this property already running, use that animation's original unanimated style as the from value.
+        * animation/AnimationTimeline.h:
+        * animation/CSSAnimation.cpp:
+        (WebCore::CSSAnimation::create): The animation name is now set in the CSSAnimation constructor.
+        (WebCore::CSSAnimation::CSSAnimation): Set the animation name and keep a copy of the unanimated style such that we can
+        get it when a CSS Transition is created and takes precedence over this CSS Animation.
+        (WebCore::CSSAnimation::initialize): Deleted.
+        * animation/CSSAnimation.h:
+        * animation/CSSTransition.cpp:
+        (WebCore::CSSTransition::create):
+        (WebCore::CSSTransition::initialize):
+        * animation/CSSTransition.h:
+        * animation/DeclarativeAnimation.cpp:
+        (WebCore::DeclarativeAnimation::initialize): Call the new computeDeclarativeAnimationBlendingKeyframes() on the KeyframeEffect
+        directly in this method so that subclasses don't need to create it manually, but most important so that keyframes are created
+        before timing properties are set based on the backing animation.
+        * animation/DeclarativeAnimation.h:
+        * animation/KeyframeEffectReadOnly.cpp:
+        (WebCore::KeyframeEffectReadOnly::getKeyframes): Ensure we have a CSSValue before trying to serialize it.
+        (WebCore::KeyframeEffectReadOnly::computeDeclarativeAnimationBlendingKeyframes):
+        (WebCore::KeyframeEffectReadOnly::computeCSSAnimationBlendingKeyframes): Use the animation's unanimated style to compute keyframes,
+        instead of a default RenderStyle which would not use the right values for implicit keyframes.
+        (WebCore::KeyframeEffectReadOnly::stylesWouldYieldNewCSSTransitionsBlendingKeyframes const): Look at the property used to create
+        the transition rather than that specified on the backing Animation object since it can be CSSPropertyInvalid in the case of
+        "transition: all".
+        (WebCore::KeyframeEffectReadOnly::setAnimatedPropertiesInStyle): If we're dealing with a CSS animation, we consider the first and
+        last keyframes to always have the property listed since the underlying style was provided and should be captured.
+        * animation/KeyframeEffectReadOnly.h:
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::createAnimatedElementUpdate): Apply CSS Animations after CSS Trasitions since they take precedence.
+
+2018-04-12  Antoine Quint  <graouts@apple.com>
+
         [Web Animations] Only cancel declarative animations upon element removal
         https://bugs.webkit.org/show_bug.cgi?id=184553
 
index 2b26ebc..1aa8dbc 100644 (file)
@@ -182,7 +182,7 @@ void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const Re
                 cssAnimationsByName.get(name)->setBackingAnimation(currentAnimation);
             } else if (currentAnimation.isValidAnimation()) {
                 // Otherwise we are dealing with a new animation name and must create a CSSAnimation for it.
-                cssAnimationsByName.set(name, CSSAnimation::create(element, currentAnimation));
+                cssAnimationsByName.set(name, CSSAnimation::create(element, currentAnimation, oldStyle, newStyle));
             }
             // Remove the name of this animation from our list since it's now known to be current.
             namesOfPreviousAnimations.remove(name);
@@ -199,6 +199,17 @@ void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const Re
         m_elementToCSSAnimationByName.remove(&element);
 }
 
+RefPtr<WebAnimation> AnimationTimeline::cssAnimationForElementAndProperty(Element& element, CSSPropertyID property)
+{
+    RefPtr<WebAnimation> matchingAnimation;
+    for (const auto& animation : m_elementToCSSAnimationsMap.get(&element)) {
+        auto* effect = animation->effect();
+        if (is<KeyframeEffectReadOnly>(effect) && downcast<KeyframeEffectReadOnly>(effect)->animatedProperties().contains(property))
+            matchingAnimation = animation;
+    }
+    return matchingAnimation;
+}
+
 static bool shouldBackingAnimationBeConsideredForCSSTransition(const Animation& backingAnimation)
 {
     auto mode = backingAnimation.animationMode();
@@ -269,21 +280,25 @@ void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const R
                     break;
                 }
 
-                bool hadProperty = previousProperties.removeFirst(property);
+                previousProperties.removeFirst(property);
                 // We've found a backing animation that we didn't know about for a valid property.
                 if (!previousBackingAnimations.contains(&backingAnimation)) {
                     // If we already had a CSSTransition for this property, check whether its timing properties match the current backing
                     // animation's properties and whether its blending keyframes match the old and new styles. If they do, move on to the
                     // next transition, otherwise delete the previous CSSTransition object, and create a new one.
-                    if (hadProperty) {
+                    if (cssTransitionsByProperty.contains(property)) {
                         if (cssTransitionsByProperty.get(property)->matchesBackingAnimationAndStyles(backingAnimation, oldStyle, newStyle))
                             continue;
                         removeDeclarativeAnimation(cssTransitionsByProperty.take(property));
                     }
                     // Now we can create a new CSSTransition with the new backing animation provided it has a valid
                     // duration and the from and to values are distinct.
-                    if (backingAnimation.duration() > 0 && oldStyle && !CSSPropertyAnimation::propertiesEqual(property, oldStyle, &newStyle))
-                        cssTransitionsByProperty.set(property, CSSTransition::create(element, property, backingAnimation, oldStyle, newStyle));
+                    if ((backingAnimation.duration() || backingAnimation.delay() > 0) && oldStyle) {
+                        auto existingAnimation = cssAnimationForElementAndProperty(element, property);
+                        const auto* fromStyle = existingAnimation ? &downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : oldStyle;
+                        if (!CSSPropertyAnimation::propertiesEqual(property, fromStyle, &newStyle))
+                            cssTransitionsByProperty.set(property, CSSTransition::create(element, property, backingAnimation, fromStyle, newStyle));
+                    }
                 }
             }
         }
index 30cd238..f3968fe 100644 (file)
@@ -87,6 +87,7 @@ protected:
 private:
     HashMap<Element*, Vector<RefPtr<WebAnimation>>>& relevantMapForAnimation(WebAnimation&);
     void cancelOrRemoveDeclarativeAnimation(RefPtr<DeclarativeAnimation>);
+    RefPtr<WebAnimation> cssAnimationForElementAndProperty(Element&, CSSPropertyID);
 
     ClassType m_classType;
     std::optional<Seconds> m_currentTime;
index bd8c5fe..9d17205 100644 (file)
 
 namespace WebCore {
 
-Ref<CSSAnimation> CSSAnimation::create(Element& target, const Animation& backingAnimation)
+Ref<CSSAnimation> CSSAnimation::create(Element& target, const Animation& backingAnimation, const RenderStyle* oldStyle, const RenderStyle& newStyle)
 {
-    auto result = adoptRef(*new CSSAnimation(target, backingAnimation));
-    result->m_animationName = backingAnimation.name();
-    result->initialize(target);
+    auto result = adoptRef(*new CSSAnimation(target, backingAnimation, newStyle));
+    result->initialize(target, oldStyle, newStyle);
     return result;
 }
 
-CSSAnimation::CSSAnimation(Element& element, const Animation& backingAnimation)
+CSSAnimation::CSSAnimation(Element& element, const Animation& backingAnimation, const RenderStyle& unanimatedStyle)
     : DeclarativeAnimation(element, backingAnimation)
+    , m_animationName(backingAnimation.name())
+    , m_unanimatedStyle(RenderStyle::clonePtr(unanimatedStyle))
 {
 }
 
-void CSSAnimation::initialize(const Element& target)
-{
-    DeclarativeAnimation::initialize(target);
-
-    downcast<KeyframeEffectReadOnly>(effect())->computeCSSAnimationBlendingKeyframes();
-}
-
 void CSSAnimation::syncPropertiesWithBackingAnimation()
 {
     DeclarativeAnimation::syncPropertiesWithBackingAnimation();
index 51264db..12bf20b 100644 (file)
@@ -32,26 +32,27 @@ namespace WebCore {
 
 class Animation;
 class Element;
+class RenderStyle;
 
 class CSSAnimation final : public DeclarativeAnimation {
 public:
-    static Ref<CSSAnimation> create(Element&, const Animation&);
+    static Ref<CSSAnimation> create(Element&, const Animation&, const RenderStyle* oldStyle, const RenderStyle& newStyle);
     ~CSSAnimation() = default;
 
     bool isCSSAnimation() const override { return true; }
     const String& animationName() const { return m_animationName; }
+    const RenderStyle& unanimatedStyle() const { return *m_unanimatedStyle; }
 
     std::optional<double> bindingsCurrentTime() const final;
 
 protected:
-    void initialize(const Element&) final;
     void syncPropertiesWithBackingAnimation() final;
 
 private:
-    CSSAnimation(Element&, const Animation&);
+    CSSAnimation(Element&, const Animation&, const RenderStyle&);
 
     String m_animationName;
-
+    std::unique_ptr<RenderStyle> m_unanimatedStyle;
 };
 
 } // namespace WebCore
index e95479b..d93e6ff 100644 (file)
@@ -35,8 +35,7 @@ namespace WebCore {
 Ref<CSSTransition> CSSTransition::create(Element& target, CSSPropertyID property, const Animation& backingAnimation, const RenderStyle* oldStyle, const RenderStyle& newStyle)
 {
     auto result = adoptRef(*new CSSTransition(target, property, backingAnimation));
-    result->initialize(target);
-    downcast<KeyframeEffectReadOnly>(result->effect())->computeCSSTransitionBlendingKeyframes(oldStyle, newStyle);
+    result->initialize(target, oldStyle, newStyle);
     return result;
 }
 
@@ -46,9 +45,9 @@ CSSTransition::CSSTransition(Element& element, CSSPropertyID property, const Ani
 {
 }
 
-void CSSTransition::initialize(const Element& target)
+void CSSTransition::initialize(const Element& target, const RenderStyle* oldStyle, const RenderStyle& newStyle)
 {
-    DeclarativeAnimation::initialize(target);
+    DeclarativeAnimation::initialize(target, oldStyle, newStyle);
 
     suspendEffectInvalidation();
 
index fae1bec..50dda8b 100644 (file)
@@ -48,7 +48,7 @@ public:
     bool canBeListed() const final;
 
 protected:
-    void initialize(const Element&) final;
+    void initialize(const Element&, const RenderStyle* oldStyle, const RenderStyle& newStyle) final;
 
 private:
     CSSTransition(Element&, CSSPropertyID, const Animation&);
index 3b93378..b6137c1 100644 (file)
@@ -56,7 +56,7 @@ void DeclarativeAnimation::setBackingAnimation(const Animation& backingAnimation
     syncPropertiesWithBackingAnimation();
 }
 
-void DeclarativeAnimation::initialize(const Element& target)
+void DeclarativeAnimation::initialize(const Element& target, const RenderStyle* oldStyle, const RenderStyle& newStyle)
 {
     // We need to suspend invalidation of the animation's keyframe effect during its creation
     // as it would otherwise trigger invalidation of the document's style and this would be
@@ -65,6 +65,7 @@ void DeclarativeAnimation::initialize(const Element& target)
 
     setEffect(KeyframeEffectReadOnly::create(target));
     setTimeline(&target.document().timeline());
+    downcast<KeyframeEffectReadOnly>(effect())->computeDeclarativeAnimationBlendingKeyframes(oldStyle, newStyle);
     syncPropertiesWithBackingAnimation();
     if (backingAnimation().playState() == AnimPlayStatePlaying)
         play();
index 3235834..80d280a 100644 (file)
@@ -34,6 +34,7 @@ namespace WebCore {
 
 class Animation;
 class Element;
+class RenderStyle;
 
 class DeclarativeAnimation : public WebAnimation {
 public:
@@ -51,7 +52,7 @@ public:
 protected:
     DeclarativeAnimation(Element&, const Animation&);
 
-    virtual void initialize(const Element&);
+    virtual void initialize(const Element&, const RenderStyle* oldStyle, const RenderStyle& newStyle);
     virtual void syncPropertiesWithBackingAnimation();
 
 private:
index 7e5c629..f700df8 100644 (file)
@@ -537,7 +537,9 @@ Vector<Strong<JSObject>> KeyframeEffectReadOnly::getKeyframes(ExecState& state)
                 // 1. Let property name be the result of applying the animation property name to IDL attribute name algorithm to the property name of declaration.
                 auto propertyName = CSSPropertyIDToIDLAttributeName(cssPropertyId);
                 // 2. Let IDL value be the result of serializing the property value of declaration by passing declaration to the algorithm to serialize a CSS value.
-                auto idlValue = computedStyleExtractor.valueForPropertyinStyle(style, cssPropertyId)->cssText();
+                String idlValue = "";
+                if (auto cssValue = computedStyleExtractor.valueForPropertyinStyle(style, cssPropertyId))
+                    idlValue = cssValue->cssText();
                 // 3. Let value be the result of converting IDL value to an ECMAScript String value.
                 auto value = toJS<IDLDOMString>(state, idlValue);
                 // 4. Call the [[DefineOwnProperty]] internal method on output keyframe with property name property name,
@@ -817,21 +819,27 @@ void KeyframeEffectReadOnly::checkForMatchingBackdropFilterFunctionLists()
 }
 #endif
 
+void KeyframeEffectReadOnly::computeDeclarativeAnimationBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle)
+{
+    ASSERT(is<DeclarativeAnimation>(animation()));
+    if (is<CSSAnimation>(animation()))
+        computeCSSAnimationBlendingKeyframes();
+    else if (is<CSSTransition>(animation()))
+        computeCSSTransitionBlendingKeyframes(oldStyle, newStyle);
+}
+
 void KeyframeEffectReadOnly::computeCSSAnimationBlendingKeyframes()
 {
     ASSERT(is<CSSAnimation>(animation()));
 
-    auto& backingAnimation = downcast<CSSAnimation>(animation())->backingAnimation();
+    auto cssAnimation = downcast<CSSAnimation>(animation());
+    auto& backingAnimation = cssAnimation->backingAnimation();
     if (backingAnimation.name().isEmpty())
         return;
 
-    auto renderStyle = RenderStyle::createPtr();
-    // We need to call update() on the FontCascade or we'll hit an ASSERT when parsing font-related properties.
-    renderStyle->fontCascade().update(nullptr);
-
     KeyframeList keyframeList(backingAnimation.name());
     if (auto* styleScope = Style::Scope::forOrdinal(*m_target, backingAnimation.nameStyleScopeOrdinal()))
-        styleScope->resolver().keyframeStylesForAnimation(*m_target, renderStyle.get(), keyframeList);
+        styleScope->resolver().keyframeStylesForAnimation(*m_target, &cssAnimation->unanimatedStyle(), keyframeList);
 
     // Ensure resource loads for all the frames.
     for (auto& keyframe : keyframeList.keyframes()) {
@@ -877,7 +885,7 @@ bool KeyframeEffectReadOnly::stylesWouldYieldNewCSSTransitionsBlendingKeyframes(
     if (!hasBlendingKeyframes())
         return false;
 
-    auto property = downcast<CSSTransition>(animation())->backingAnimation().property();
+    auto property = downcast<CSSTransition>(animation())->property();
 
     // There cannot be new keyframes if the start and to values are the same.
     if (CSSPropertyAnimation::propertiesEqual(property, &oldStyle, &newStyle))
@@ -991,6 +999,8 @@ void KeyframeEffectReadOnly::setAnimatedPropertiesInStyle(RenderStyle& targetSty
     // The effect value of a single property referenced by a keyframe effect as one of its target properties,
     // for a given iteration progress, current iteration and underlying value is calculated as follows.
 
+    bool isCSSAnimation = is<CSSAnimation>(animation());
+
     for (auto cssPropertyId : m_blendingKeyframes.properties()) {
         // 1. If iteration progress is unresolved abort this procedure.
         // 2. Let target property be the longhand property for which the effect value is to be calculated.
@@ -1005,9 +1015,13 @@ void KeyframeEffectReadOnly::setAnimatedPropertiesInStyle(RenderStyle& targetSty
         Vector<std::optional<size_t>> propertySpecificKeyframes;
         for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) {
             auto& keyframe = m_blendingKeyframes[i];
-            if (!keyframe.containsProperty(cssPropertyId))
-                continue;
             auto offset = keyframe.key();
+            if (!keyframe.containsProperty(cssPropertyId)) {
+                // If we're dealing with a CSS animation, we consider the first and last keyframes to always have the property listed
+                // since the underlying style was provided and should be captured.
+                if (!isCSSAnimation || (offset && offset < 1))
+                    continue;
+            }
             if (!offset)
                 numberOfKeyframesWithZeroOffset++;
             if (offset == 1)
index e24865b..5fc6f66 100644 (file)
@@ -112,8 +112,7 @@ public:
     bool backdropFilterFunctionListsMatch() const override { return m_backdropFilterFunctionListsMatch; }
 #endif
 
-    void computeCSSAnimationBlendingKeyframes();
-    void computeCSSTransitionBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
+    void computeDeclarativeAnimationBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
     bool stylesWouldYieldNewCSSTransitionsBlendingKeyframes(const RenderStyle& oldStyle, const RenderStyle& newStyle) const;
     bool hasBlendingKeyframes() const { return m_blendingKeyframes.size(); }
     const HashSet<CSSPropertyID>& animatedProperties() const { return m_blendingKeyframes.properties(); }
@@ -139,6 +138,8 @@ private:
     Ref<const Animation> backingAnimationForCompositedRenderer() const;
     void computeStackingContextImpact();
     void updateBlendingKeyframes();
+    void computeCSSAnimationBlendingKeyframes();
+    void computeCSSTransitionBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
     void computeShouldRunAccelerated();
     void setBlendingKeyframes(KeyframeList&);
     void checkForMatchingTransformFunctionLists();
index b8e4cf0..040031c 100644 (file)
@@ -292,11 +292,11 @@ ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderSt
         // First, we need to make sure that any new CSS animation occuring on this element has a matching WebAnimation
         // on the document timeline. Note that we get timeline() on the Document here because we need a timeline created
         // in case no Web Animations have been created through the JS API.
-        if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
-            m_document.timeline().updateCSSAnimationsForElement(element, *newStyle, oldStyle);
-
         if ((oldStyle && oldStyle->hasTransitions()) || newStyle->hasTransitions())
             m_document.timeline().updateCSSTransitionsForElement(element, *newStyle, oldStyle);
+
+        if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
+            m_document.timeline().updateCSSAnimationsForElement(element, *newStyle, oldStyle);
     }
 
     if (auto timeline = m_document.existingTimeline()) {