Title: [261926] trunk
Revision
261926
Author
[email protected]
Date
2020-05-20 09:26:16 -0700 (Wed, 20 May 2020)

Log Message

[Web Animations] Animation engine should not wake up every tick for steps timing functions
https://bugs.webkit.org/show_bug.cgi?id=212103
<rdar://problem/62737868>

Reviewed by Simon Fraser.

Source/WebCore:

Tests: webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html
       webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html
       webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html
       webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html

When an animation uses a steps() timing function, it will appear to animate discretely between values such
that there is only n visual changes, where n is the number of steps provided. This gives us an opportunity
to be more efficient when scheduling animations using steps() timing functions.

In WebAnimation::timeToNextTick() we now ask the associated effect for the amount of progress until the next
step. For an effect-wide steps() timing function, we can use the provided iteration progress. For animations
with a linear effect-wide timing function (the default), we have to map the provided iteration progress to
a keyframe interval, provided that interval uses a steps() timing function.

The new {Animation|Keyframe}Effect::progressUntilNextStep() method returns WTF::nullopt for any other case.

In order to test this, we add a new internals.timeToNextAnimationTick(animation) method which we use in the
two new tests.

* animation/AnimationEffect.cpp:
(WebCore::AnimationEffect::progressUntilNextStep const):
* animation/AnimationEffect.h:
* animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::setBlendingKeyframes):
(WebCore::KeyframeEffect::computeSomeKeyframesUseStepsTimingFunction):
(WebCore::KeyframeEffect::timingFunctionForKeyframeAtIndex const): Avoid any out-of-bounds use of the underlying data
structures by returning nullptr for cases where we don't have an explicit keyframe. We also make the function const
such that it may be called from progressUntilNextStep(), it always was const but wasn't marked as such.
(WebCore::KeyframeEffect::progressUntilNextStep const):
* animation/KeyframeEffect.h:
* animation/WebAnimation.cpp:
(WebCore::WebAnimation::timeToNextTick const):
* animation/WebAnimation.h:
* animation/WebAnimation.idl:
* testing/Internals.cpp:
(WebCore::Internals::timeToNextAnimationTick const):
* testing/Internals.h:
* testing/Internals.idl:

Source/WTF:

Allow Seconds to be divided or multiplied by a double with operands in any order.

* wtf/Seconds.h:
(WTF::operator*):
(WTF::operator/):

LayoutTests:

Add tests that check that an animation using a steps() timing function correctly computes the time to
the next tick accouning for the fact that it won't compute a different iteration progress until the
next step.

* webanimations/scheduling-of-animation-with-steps-timing-function-on-effect-expected.txt: Added.
* webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html: Added.
* webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe-expected.txt: Added.
* webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html: Added.
* webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes-expected.txt: Added.
* webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html: Added.
* webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function-expected.txt: Added.
* webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261925 => 261926)


--- trunk/LayoutTests/ChangeLog	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/LayoutTests/ChangeLog	2020-05-20 16:26:16 UTC (rev 261926)
@@ -1,3 +1,24 @@
+2020-05-20  Antoine Quint  <[email protected]>
+
+        [Web Animations] Animation engine should not wake up every tick for steps timing functions
+        https://bugs.webkit.org/show_bug.cgi?id=212103
+        <rdar://problem/62737868>
+
+        Reviewed by Simon Fraser.
+
+        Add tests that check that an animation using a steps() timing function correctly computes the time to
+        the next tick accouning for the fact that it won't compute a different iteration progress until the
+        next step.
+
+        * webanimations/scheduling-of-animation-with-steps-timing-function-on-effect-expected.txt: Added.
+        * webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html: Added.
+        * webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe-expected.txt: Added.
+        * webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html: Added.
+        * webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes-expected.txt: Added.
+        * webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html: Added.
+        * webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function-expected.txt: Added.
+        * webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html: Added.
+
 2020-05-20  Noam Rosenthal  <[email protected]>
 
         Fix table sizing when 'max-width' is used

Added: trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-effect-expected.txt (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-effect-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-effect-expected.txt	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,3 @@
+
+PASS Computing the time until the next tick for an effect with a steps() easing. 
+

Added: trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,17 @@
+<script src=""
+<script src=""
+<div></div>
+<script>
+
+async_test(async t => {
+    const animation = document.querySelector("div").animate({ marginLeft: "100px" }, { duration: 1000, easing: "steps(10)" });
+
+    await animation.ready;
+    
+    animation.currentTime = 225;
+    assert_equals(internals.timeToNextAnimationTick(animation), 75, "Computing the time until the next tick when partly through a steps interval.");
+    
+    t.done();
+}, "Computing the time until the next tick for an effect with a steps() easing.");
+
+</script>

Added: trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe-expected.txt (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe-expected.txt	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,3 @@
+
+PASS Computing the time until the next tick for a keyframe with a steps() timing function. 
+

Added: trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,20 @@
+<script src=""
+<script src=""
+<div></div>
+<script>
+
+async_test(async t => {
+    const animation = document.querySelector("div").animate({
+        marginLeft: ["0", "250px", "500px", "750px", "1000px"],
+        easing: ["linear", "linear", "steps(10)", "linear"]
+    }, 1000);
+
+    await animation.ready;
+    
+    animation.currentTime = 535;
+    assert_equals(internals.timeToNextAnimationTick(animation), 15);
+
+    t.done();
+}, "Computing the time until the next tick for a keyframe with a steps() timing function.");
+
+</script>

Added: trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes-expected.txt (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes-expected.txt	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,3 @@
+
+PASS Computing the time until the next tick for a CSS Animation with implicit and explicit steps() timing functions. 
+

Added: trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,39 @@
+<script src=""
+<script src=""
+<style>
+
+    div {
+        animation: anim 1s steps(10);
+    }
+
+    @keyframes anim {
+        25% { margin-left: 250px }
+        50% { margin-left: 500px; animation-timing-function: steps(5); }
+        75% { margin-left: 750px }
+    }
+
+</style>
+<div></div>
+<script>
+
+async_test(async t => {
+    const animation = document.querySelector("div").getAnimations()[0];
+
+    await animation.ready;
+    
+    animation.currentTime = 10;
+    assert_equals(internals.timeToNextAnimationTick(animation), 15, "Progress contained in the interval for an implicit 0% keyframe.");
+
+    animation.currentTime = 265;
+    assert_equals(internals.timeToNextAnimationTick(animation), 10, "Progress contained in the interval for an explicit keyframe with an implicit steps() timing-function.");
+
+    animation.currentTime = 510;
+    assert_equals(internals.timeToNextAnimationTick(animation), 40, "Progress contained in the interval for an explicit keyframe with an explicit steps() timing-function.");
+
+    animation.currentTime = 780;
+    assert_equals(internals.timeToNextAnimationTick(animation), 20, "Progress contained in the interval for an implicit 100% keyframe.");
+
+    t.done();
+}, "Computing the time until the next tick for a CSS Animation with implicit and explicit steps() timing functions.");
+
+</script>

Added: trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function-expected.txt (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function-expected.txt	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,3 @@
+
+PASS Computing the time until the next tick for a CSS Animation with implicit steps() timing functions. 
+

Added: trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html (0 => 261926)


--- trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html	                        (rev 0)
+++ trunk/LayoutTests/webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html	2020-05-20 16:26:16 UTC (rev 261926)
@@ -0,0 +1,31 @@
+<script src=""
+<script src=""
+<style>
+
+    div {
+        animation: anim 1s steps(10);
+    }
+
+    @keyframes anim {
+        50% { margin-left: 500px; }
+    }
+
+</style>
+<div></div>
+<script>
+
+async_test(async t => {
+    const animation = document.querySelector("div").getAnimations()[0];
+
+    await animation.ready;
+    
+    animation.currentTime = 10;
+    assert_equals(internals.timeToNextAnimationTick(animation), 40, "Progress contained in the interval for an implicit 0% keyframe.");
+
+    animation.currentTime = 920;
+    assert_equals(internals.timeToNextAnimationTick(animation), 30, "Progress contained in the interval for an implicit 100% keyframe.");
+
+    t.done();
+}, "Computing the time until the next tick for a CSS Animation with implicit steps() timing functions.");
+
+</script>

Modified: trunk/Source/WTF/ChangeLog (261925 => 261926)


--- trunk/Source/WTF/ChangeLog	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WTF/ChangeLog	2020-05-20 16:26:16 UTC (rev 261926)
@@ -1,3 +1,17 @@
+2020-05-20  Antoine Quint  <[email protected]>
+
+        [Web Animations] Animation engine should not wake up every tick for steps timing functions
+        https://bugs.webkit.org/show_bug.cgi?id=212103
+        <rdar://problem/62737868>
+
+        Reviewed by Simon Fraser.
+
+        Allow Seconds to be divided or multiplied by a double with operands in any order.
+
+        * wtf/Seconds.h:
+        (WTF::operator*):
+        (WTF::operator/):
+
 2020-05-20  Youenn Fablet  <[email protected]>
 
         [Mac] Use preferedPixelBufferFormat for AVVideoCaptureSource

Modified: trunk/Source/WTF/wtf/Seconds.h (261925 => 261926)


--- trunk/Source/WTF/wtf/Seconds.h	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WTF/wtf/Seconds.h	2020-05-20 16:26:16 UTC (rev 261926)
@@ -332,6 +332,16 @@
 
 } // inline seconds_literals
 
+inline Seconds operator*(double scalar, Seconds seconds)
+{
+    return Seconds(scalar * seconds.value());
+}
+
+inline Seconds operator/(double scalar, Seconds seconds)
+{
+    return Seconds(scalar / seconds.value());
+}
+
 WTF_EXPORT_PRIVATE TextStream& operator<<(TextStream&, Seconds);
 
 } // namespace WTF

Modified: trunk/Source/WebCore/ChangeLog (261925 => 261926)


--- trunk/Source/WebCore/ChangeLog	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/ChangeLog	2020-05-20 16:26:16 UTC (rev 261926)
@@ -1,3 +1,50 @@
+2020-05-20  Antoine Quint  <[email protected]>
+
+        [Web Animations] Animation engine should not wake up every tick for steps timing functions
+        https://bugs.webkit.org/show_bug.cgi?id=212103
+        <rdar://problem/62737868>
+
+        Reviewed by Simon Fraser.
+
+        Tests: webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html
+               webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html
+               webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html
+               webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html
+
+        When an animation uses a steps() timing function, it will appear to animate discretely between values such
+        that there is only n visual changes, where n is the number of steps provided. This gives us an opportunity
+        to be more efficient when scheduling animations using steps() timing functions.
+
+        In WebAnimation::timeToNextTick() we now ask the associated effect for the amount of progress until the next
+        step. For an effect-wide steps() timing function, we can use the provided iteration progress. For animations
+        with a linear effect-wide timing function (the default), we have to map the provided iteration progress to
+        a keyframe interval, provided that interval uses a steps() timing function.
+
+        The new {Animation|Keyframe}Effect::progressUntilNextStep() method returns WTF::nullopt for any other case.
+
+        In order to test this, we add a new internals.timeToNextAnimationTick(animation) method which we use in the
+        two new tests.
+
+        * animation/AnimationEffect.cpp:
+        (WebCore::AnimationEffect::progressUntilNextStep const):
+        * animation/AnimationEffect.h:
+        * animation/KeyframeEffect.cpp:
+        (WebCore::KeyframeEffect::setBlendingKeyframes):
+        (WebCore::KeyframeEffect::computeSomeKeyframesUseStepsTimingFunction):
+        (WebCore::KeyframeEffect::timingFunctionForKeyframeAtIndex const): Avoid any out-of-bounds use of the underlying data
+        structures by returning nullptr for cases where we don't have an explicit keyframe. We also make the function const
+        such that it may be called from progressUntilNextStep(), it always was const but wasn't marked as such.
+        (WebCore::KeyframeEffect::progressUntilNextStep const):
+        * animation/KeyframeEffect.h:
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::timeToNextTick const):
+        * animation/WebAnimation.h:
+        * animation/WebAnimation.idl:
+        * testing/Internals.cpp:
+        (WebCore::Internals::timeToNextAnimationTick const):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2020-05-20  Noam Rosenthal  <[email protected]>
 
         Fix table sizing when 'max-width' is used

Modified: trunk/Source/WebCore/animation/AnimationEffect.cpp (261925 => 261926)


--- trunk/Source/WebCore/animation/AnimationEffect.cpp	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/AnimationEffect.cpp	2020-05-20 16:26:16 UTC (rev 261926)
@@ -541,4 +541,14 @@
     m_timingFunction = timingFunction;
 }
 
+Optional<double> AnimationEffect::progressUntilNextStep(double iterationProgress) const
+{
+    if (!is<StepsTimingFunction>(m_timingFunction))
+        return WTF::nullopt;
+
+    auto numberOfSteps = downcast<StepsTimingFunction>(*m_timingFunction).numberOfSteps();
+    auto nextStepProgress = ceil(iterationProgress * numberOfSteps) / numberOfSteps;
+    return nextStepProgress - iterationProgress;
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/animation/AnimationEffect.h (261925 => 261926)


--- trunk/Source/WebCore/animation/AnimationEffect.h	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/AnimationEffect.h	2020-05-20 16:26:16 UTC (rev 261926)
@@ -102,6 +102,8 @@
 
     void updateStaticTimingProperties();
 
+    virtual Optional<double> progressUntilNextStep(double) const;
+
 protected:
     explicit AnimationEffect();
 

Modified: trunk/Source/WebCore/animation/AnimationTimeline.cpp (261925 => 261926)


--- trunk/Source/WebCore/animation/AnimationTimeline.cpp	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/AnimationTimeline.cpp	2020-05-20 16:26:16 UTC (rev 261926)
@@ -399,6 +399,9 @@
             // If we already have a keyframe effect targeting this property, we should use its unanimated style to determine what the potential
             // start value of the transition shoud be to make sure that we don't account for animated values that would have been blended onto
             // the style applied during the last style resolution.
+
+            // FIXME: NO. We should be applying animations with the current time.
+
             if (auto* unanimatedStyle = keyframeEffect->unanimatedStyle())
                 return RenderStyle::clone(*unanimatedStyle);
 

Modified: trunk/Source/WebCore/animation/KeyframeEffect.cpp (261925 => 261926)


--- trunk/Source/WebCore/animation/KeyframeEffect.cpp	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/KeyframeEffect.cpp	2020-05-20 16:26:16 UTC (rev 261926)
@@ -846,6 +846,7 @@
     computedNeedsForcedLayout();
     computeStackingContextImpact();
     computeAcceleratedPropertiesState();
+    computeSomeKeyframesUseStepsTimingFunction();
 
     checkForMatchingTransformFunctionLists();
     checkForMatchingFilterFunctionLists();
@@ -1265,6 +1266,36 @@
         m_acceleratedPropertiesState = AcceleratedProperties::All;
 }
 
+void KeyframeEffect::computeSomeKeyframesUseStepsTimingFunction()
+{
+    m_someKeyframesUseStepsTimingFunction = false;
+
+    size_t numberOfKeyframes = m_blendingKeyframes.size();
+
+    // If we're dealing with a CSS Animation and it specifies a default steps() timing function,
+    // we need to check that any of the specified keyframes either does not have an explicit timing
+    // function or specifies an explicit steps() timing function.
+    if (is<CSSAnimation>(animation()) && is<StepsTimingFunction>(downcast<DeclarativeAnimation>(*animation()).backingAnimation().timingFunction())) {
+        for (size_t i = 0; i < numberOfKeyframes; i++) {
+            auto* timingFunction = m_blendingKeyframes[i].timingFunction();
+            if (!timingFunction || is<StepsTimingFunction>(timingFunction)) {
+                m_someKeyframesUseStepsTimingFunction = true;
+                return;
+            }
+        }
+        return;
+    }
+
+    // For any other type of animation, we just need to check whether any of the keyframes specify
+    // an explicit steps() timing function.
+    for (size_t i = 0; i < numberOfKeyframes; i++) {
+        if (is<StepsTimingFunction>(m_blendingKeyframes[i].timingFunction())) {
+            m_someKeyframesUseStepsTimingFunction = true;
+            return;
+        }
+    }
+}
+
 void KeyframeEffect::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle)
 {
     if (!renderer() || !animation())
@@ -1439,15 +1470,20 @@
     }
 }
 
-TimingFunction* KeyframeEffect::timingFunctionForKeyframeAtIndex(size_t index)
+TimingFunction* KeyframeEffect::timingFunctionForKeyframeAtIndex(size_t index) const
 {
-    if (!m_parsedKeyframes.isEmpty())
+    if (!m_parsedKeyframes.isEmpty()) {
+        if (index >= m_parsedKeyframes.size())
+            return nullptr;
         return m_parsedKeyframes[index].timingFunction.get();
+    }
 
     auto effectAnimation = animation();
     if (is<DeclarativeAnimation>(effectAnimation)) {
         // If we're dealing with a CSS Animation, the timing function is specified either on the keyframe itself.
         if (is<CSSAnimation>(effectAnimation)) {
+            if (index >= m_blendingKeyframes.size())
+                return nullptr;
             if (auto* timingFunction = m_blendingKeyframes[index].timingFunction())
                 return timingFunction;
         }
@@ -1795,4 +1831,57 @@
     return m_blendingKeyframesSource == BlendingKeyframesSource::WebAnimation && targetsPseudoElement();
 }
 
+Optional<double> KeyframeEffect::progressUntilNextStep(double iterationProgress) const
+{
+    ASSERT(iterationProgress >= 0 && iterationProgress <= 1);
+
+    if (auto progress = AnimationEffect::progressUntilNextStep(iterationProgress))
+        return progress;
+
+    if (!is<LinearTimingFunction>(timingFunction()) || !m_someKeyframesUseStepsTimingFunction)
+        return WTF::nullopt;
+
+    if (m_blendingKeyframes.isEmpty())
+        return WTF::nullopt;
+
+    auto progressUntilNextStepInInterval = [iterationProgress](double intervalStartProgress, double intervalEndProgress, TimingFunction* timingFunction) -> Optional<double> {
+        if (!is<StepsTimingFunction>(timingFunction))
+            return WTF::nullopt;
+
+        auto numberOfSteps = downcast<StepsTimingFunction>(*timingFunction).numberOfSteps();
+        auto intervalProgress = intervalEndProgress - intervalStartProgress;
+        auto iterationProgressMappedToCurrentInterval = (iterationProgress - intervalStartProgress) / intervalProgress;
+        auto nextStepProgress = ceil(iterationProgressMappedToCurrentInterval * numberOfSteps) / numberOfSteps;
+        return (nextStepProgress - iterationProgressMappedToCurrentInterval) * intervalProgress;
+    };
+
+    for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) {
+        auto intervalEndProgress = m_blendingKeyframes[i].key();
+        // We can stop once we find a keyframe for which the progress is more than the provided iteration progress.
+        if (intervalEndProgress <= iterationProgress)
+            continue;
+
+        // In case we're on the first keyframe, then this means we are dealing with an implicit 0% keyframe.
+        // This will be a linear timing function unless we're dealing with a CSS Animation which might have
+        // the default timing function for its keyframes defined on its backing Animation object.
+        if (!i) {
+            if (is<CSSAnimation>(animation()))
+                return progressUntilNextStepInInterval(0, intervalEndProgress, downcast<DeclarativeAnimation>(*animation()).backingAnimation().timingFunction());
+            return WTF::nullopt;
+        }
+
+        return progressUntilNextStepInInterval(m_blendingKeyframes[i - 1].key(), intervalEndProgress, timingFunctionForKeyframeAtIndex(i - 1));
+    }
+
+    // If we end up here, then this means we are dealing with an implicit 100% keyframe.
+    // This will be a linear timing function unless we're dealing with a CSS Animation which might have
+    // the default timing function for its keyframes defined on its backing Animation object.
+    auto& lastExplicitKeyframe = m_blendingKeyframes[m_blendingKeyframes.size() - 1];
+    if (is<CSSAnimation>(animation()))
+        return progressUntilNextStepInInterval(lastExplicitKeyframe.key(), 1, downcast<DeclarativeAnimation>(*animation()).backingAnimation().timingFunction());
+
+    // In any other case, we are not dealing with an interval with a steps() timing function.
+    return WTF::nullopt;
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/animation/KeyframeEffect.h (261925 => 261926)


--- trunk/Source/WebCore/animation/KeyframeEffect.h	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/KeyframeEffect.h	2020-05-20 16:26:16 UTC (rev 261926)
@@ -181,10 +181,11 @@
     void addPendingAcceleratedAction(AcceleratedAction);
     void updateAcceleratedActions();
     void setAnimatedPropertiesInStyle(RenderStyle&, double);
-    TimingFunction* timingFunctionForKeyframeAtIndex(size_t);
+    TimingFunction* timingFunctionForKeyframeAtIndex(size_t) const;
     Ref<const Animation> backingAnimationForCompositedRenderer() const;
     void computedNeedsForcedLayout();
     void computeStackingContextImpact();
+    void computeSomeKeyframesUseStepsTimingFunction();
     void clearBlendingKeyframes();
     void updateBlendingKeyframes(RenderStyle&);
     void computeCSSAnimationBlendingKeyframes(const RenderStyle&);
@@ -191,6 +192,7 @@
     void computeCSSTransitionBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
     void computeAcceleratedPropertiesState();
     void setBlendingKeyframes(KeyframeList&);
+    Optional<double> progressUntilNextStep(double) const final;
     void checkForMatchingTransformFunctionLists();
     void checkForMatchingFilterFunctionLists();
     void checkForMatchingColorFilterFunctionLists();
@@ -222,6 +224,7 @@
 #endif
     bool m_colorFilterFunctionListsMatch { false };
     bool m_inTargetEffectStack { false };
+    bool m_someKeyframesUseStepsTimingFunction { false };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/animation/WebAnimation.cpp (261925 => 261926)


--- trunk/Source/WebCore/animation/WebAnimation.cpp	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/WebAnimation.cpp	2020-05-20 16:26:16 UTC (rev 261926)
@@ -1482,6 +1482,11 @@
             // Fully-accelerated running animations in the "active" phase can wait until they ended.
             return (effect.endTime() - timing.localTime.value()) / playbackRate;
         }
+        if (auto iterationProgress = effect.getComputedTiming().simpleIterationProgress) {
+            // In case we're in a range that uses a steps() timing function, we can compute the time until the next step starts.
+            if (auto progressUntilNextStep = effect.progressUntilNextStep(*iterationProgress))
+                return effect.iterationDuration() * *progressUntilNextStep / playbackRate;
+        }
         // Other animations in the "active" phase will need to update their animated value at the immediate next opportunity.
         return 0_s;
     case AnimationEffectPhase::After:

Modified: trunk/Source/WebCore/animation/WebAnimation.h (261925 => 261926)


--- trunk/Source/WebCore/animation/WebAnimation.h	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/WebAnimation.h	2020-05-20 16:26:16 UTC (rev 261926)
@@ -120,7 +120,7 @@
 
     bool needsTick() const;
     virtual void tick();
-    Seconds timeToNextTick() const;
+    WEBCORE_EXPORT Seconds timeToNextTick() const;
     virtual void resolve(RenderStyle&);
     void effectTargetDidChange(Element* previousTarget, Element* newTarget);
     void acceleratedStateDidChange();

Modified: trunk/Source/WebCore/animation/WebAnimation.idl (261925 => 261926)


--- trunk/Source/WebCore/animation/WebAnimation.idl	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/animation/WebAnimation.idl	2020-05-20 16:26:16 UTC (rev 261926)
@@ -41,7 +41,8 @@
     EnabledAtRuntime=WebAnimations,
     InterfaceName=Animation,
     CustomConstructor(),
-    CustomToJSObject
+    CustomToJSObject,
+    ExportMacro=WEBCORE_EXPORT
 ] interface WebAnimation : EventTarget {
     attribute DOMString id;
     [ImplementedAs=bindingsEffect] attribute AnimationEffect? effect;

Modified: trunk/Source/WebCore/style/StyleTreeResolver.cpp (261925 => 261926)


--- trunk/Source/WebCore/style/StyleTreeResolver.cpp	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/style/StyleTreeResolver.cpp	2020-05-20 16:26:16 UTC (rev 261926)
@@ -317,6 +317,7 @@
             if (oldStyle && (oldStyle->hasTransitions() || newStyle->hasTransitions()))
                 m_document.timeline().updateCSSTransitionsForElement(element, *oldStyle, *newStyle);
 
+            // FIXME: Maybe need to do this first such that CSS Transitions are aware of newly created or canceled animations
             if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
                 m_document.timeline().updateCSSAnimationsForElement(element, oldStyle, *newStyle);
         }

Modified: trunk/Source/WebCore/testing/Internals.cpp (261925 => 261926)


--- trunk/Source/WebCore/testing/Internals.cpp	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/testing/Internals.cpp	2020-05-20 16:26:16 UTC (rev 261926)
@@ -196,6 +196,7 @@
 #include "ViewportArguments.h"
 #include "VoidCallback.h"
 #include "WebAnimation.h"
+#include "WebAnimationUtilities.h"
 #include "WebCoreJSClientData.h"
 #include "WindowProxy.h"
 #include "WorkerThread.h"
@@ -1200,6 +1201,11 @@
     return 0;
 }
 
+double Internals::timeToNextAnimationTick(WebAnimation& animation) const
+{
+    return secondsToWebAnimationsAPITime(animation.timeToNextTick());
+}
+
 ExceptionOr<RefPtr<Element>> Internals::pseudoElement(Element& element, const String& pseudoId)
 {
     if (pseudoId != "before" && pseudoId != "after")

Modified: trunk/Source/WebCore/testing/Internals.h (261925 => 261926)


--- trunk/Source/WebCore/testing/Internals.h	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/testing/Internals.h	2020-05-20 16:26:16 UTC (rev 261926)
@@ -106,6 +106,7 @@
 class TypeConversions;
 class UnsuspendableActiveDOMObject;
 class VoidCallback;
+class WebAnimation;
 class WebGLRenderingContext;
 class WindowProxy;
 class XMLHttpRequest;
@@ -239,6 +240,7 @@
     };
     Vector<AcceleratedAnimation> acceleratedAnimationsForElement(Element&);
     unsigned numberOfAnimationTimelineInvalidations() const;
+    double timeToNextAnimationTick(WebAnimation&) const;
 
     // For animations testing, we need a way to get at pseudo elements.
     ExceptionOr<RefPtr<Element>> pseudoElement(Element&, const String&);

Modified: trunk/Source/WebCore/testing/Internals.idl (261925 => 261926)


--- trunk/Source/WebCore/testing/Internals.idl	2020-05-20 16:10:10 UTC (rev 261925)
+++ trunk/Source/WebCore/testing/Internals.idl	2020-05-20 16:26:16 UTC (rev 261926)
@@ -297,6 +297,7 @@
     // Web Animations testing.
     sequence<AcceleratedAnimation> acceleratedAnimationsForElement(Element element);
     unsigned long numberOfAnimationTimelineInvalidations();
+    double timeToNextAnimationTick(WebAnimation animation);
 
     // For animations testing, we need a way to get at pseudo elements.
     [MayThrowException] Element? pseudoElement(Element element, DOMString pseudoId);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to