Title: [187121] trunk
Revision
187121
Author
[email protected]
Date
2015-07-21 14:19:20 -0700 (Tue, 21 Jul 2015)

Log Message

Safari mis-applies "animation-fill-mode: forwards" when using fractional iteration count
https://bugs.webkit.org/show_bug.cgi?id=146996

Reviewed by Dean Jackson.
Source/WebCore:

animation-fill-mode: forwards with fractional iteration counts always snapped to
1 or 0, depending on direction. Fix to compute the fill-forward state from the
correct keyframes.

If filling forwards, AnimationBase::progress() sets the elapsed time to the duration,
then uses fractionalTime() to handle animation direction mapping. If the fractionalTime
is integral, we can return early, avoiding the cost of mapping through timing functions.

Tested by existing tests.

* page/animation/AnimationBase.cpp:
(WebCore::AnimationBase::progress):
(WebCore::AnimationBase::getElapsedTime):
* page/animation/KeyframeAnimation.cpp:
(WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty): It was possible
to end up with prevIndex == nextIndex with reverse animations, which resulted
in divide-by-zero when computing scale. Fix by picking a nextIndex that is different
from prevIndex.

LayoutTests:

Progressions, improved tests.

* animations/animation-direction-reverse-fill-mode-expected.txt: New results; this is a progression.
* animations/animation-direction-reverse-fill-mode.html: Use a shorter animation. Fixed results.
* animations/fill-mode-iteration-count-non-integer-expected.txt:
* animations/fill-mode-iteration-count-non-integer.html: Use iteration counts that are not multiplies
of 0.5, so the test can differentiation between forward and backwards states. Add a non-linear timing
function to check that fill-forwards consults the timing functions. Don't print exact succeeding
results because they may have floating point values.
* animations/fill-mode-reverse-expected.txt: New results; this is a progression.
* animations/fill-mode-reverse.html: Fixed results, use gray.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (187120 => 187121)


--- trunk/LayoutTests/ChangeLog	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/ChangeLog	2015-07-21 21:19:20 UTC (rev 187121)
@@ -1,3 +1,22 @@
+2015-07-21  Simon Fraser  <[email protected]>
+
+        Safari mis-applies "animation-fill-mode: forwards" when using fractional iteration count
+        https://bugs.webkit.org/show_bug.cgi?id=146996
+
+        Reviewed by Dean Jackson.
+        
+        Progressions, improved tests.
+
+        * animations/animation-direction-reverse-fill-mode-expected.txt: New results; this is a progression.
+        * animations/animation-direction-reverse-fill-mode.html: Use a shorter animation. Fixed results.
+        * animations/fill-mode-iteration-count-non-integer-expected.txt:
+        * animations/fill-mode-iteration-count-non-integer.html: Use iteration counts that are not multiplies
+        of 0.5, so the test can differentiation between forward and backwards states. Add a non-linear timing
+        function to check that fill-forwards consults the timing functions. Don't print exact succeeding
+        results because they may have floating point values.
+        * animations/fill-mode-reverse-expected.txt: New results; this is a progression.
+        * animations/fill-mode-reverse.html: Fixed results, use gray.
+
 2015-07-21  Said Abou-Hallawa  <[email protected]>
 
         REGRESSION (r172417, r184065): Multiple rendering issues with fixed attached background-image

Modified: trunk/LayoutTests/animations/animation-direction-reverse-fill-mode-expected.txt (187120 => 187121)


--- trunk/LayoutTests/animations/animation-direction-reverse-fill-mode-expected.txt	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/animations/animation-direction-reverse-fill-mode-expected.txt	2015-07-21 21:19:20 UTC (rev 187121)
@@ -11,7 +11,7 @@
 PASS - start of animation - id: e expected: 300 actual: 300
 PASS - end of animation - id: a expected: 100 actual: 100
 PASS - end of animation - id: b expected: 100 actual: 100
-PASS - end of animation - id: c expected: 300 actual: 300
-PASS - end of animation - id: d expected: 300 actual: 300
+PASS - end of animation - id: c expected: 200 actual: 200
+PASS - end of animation - id: d expected: 200 actual: 200
 PASS - end of animation - id: e expected: 300 actual: 300
 

Modified: trunk/LayoutTests/animations/animation-direction-reverse-fill-mode.html (187120 => 187121)


--- trunk/LayoutTests/animations/animation-direction-reverse-fill-mode.html	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/animations/animation-direction-reverse-fill-mode.html	2015-07-21 21:19:20 UTC (rev 187121)
@@ -10,7 +10,7 @@
       height: 100px;
       width: 100px;
       -webkit-animation-delay: 0.1s;
-      -webkit-animation-duration: 2s;
+      -webkit-animation-duration: 0.5s;
       -webkit-animation-timing-function: linear;
       -webkit-animation-name: anim;
       -webkit-animation-direction: reverse;
@@ -36,7 +36,7 @@
       -webkit-animation-fill-mode: both;
     }
     #e {
-      background-color: #999;
+      background-color: gray;
       -webkit-animation-fill-mode: both;
       -webkit-animation-iteration-count: 2;
       -webkit-animation-direction: alternate-reverse;
@@ -50,8 +50,8 @@
     const expectedValues = [
       {id: "a", start: 100, end: 100},
       {id: "b", start: 300, end: 100},
-      {id: "c", start: 100, end: 300},
-      {id: "d", start: 300, end: 300},
+      {id: "c", start: 100, end: 200},
+      {id: "d", start: 300, end: 200},
       {id: "e", start: 300, end: 300}
     ];
     var result = "";

Modified: trunk/LayoutTests/animations/fill-mode-iteration-count-non-integer-expected.txt (187120 => 187121)


--- trunk/LayoutTests/animations/fill-mode-iteration-count-non-integer-expected.txt	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/animations/fill-mode-iteration-count-non-integer-expected.txt	2015-07-21 21:19:20 UTC (rev 187121)
@@ -4,14 +4,20 @@
 Forwards
 Both
 Both iterating
+Both reverse alternate
+Ease function
 PASS - start of animation - id: a expected: 100 actual: 100
 PASS - start of animation - id: b expected: 200 actual: 200
 PASS - start of animation - id: c expected: 100 actual: 100
 PASS - start of animation - id: d expected: 200 actual: 200
 PASS - start of animation - id: e expected: 200 actual: 200
-PASS - end of animation - id: a expected: 100 actual: 100
-PASS - end of animation - id: b expected: 100 actual: 100
-PASS - end of animation - id: c expected: 300 actual: 300
-PASS - end of animation - id: d expected: 300 actual: 300
-PASS - end of animation - id: e expected: 300 actual: 300
+PASS - start of animation - id: f expected: 300 actual: 300
+PASS - start of animation - id: g expected: 300 actual: 300
+PASS - end of animation - id: a close to expected: 100
+PASS - end of animation - id: b close to expected: 100
+PASS - end of animation - id: c close to expected: 225
+PASS - end of animation - id: d close to expected: 225
+PASS - end of animation - id: e close to expected: 225
+PASS - end of animation - id: f close to expected: 275
+PASS - end of animation - id: g close to expected: 291
 

Modified: trunk/LayoutTests/animations/fill-mode-iteration-count-non-integer.html (187120 => 187121)


--- trunk/LayoutTests/animations/fill-mode-iteration-count-non-integer.html	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/animations/fill-mode-iteration-count-non-integer.html	2015-07-21 21:19:20 UTC (rev 187121)
@@ -10,10 +10,10 @@
       position: relative;
       left: 100px;
       top: 10px;
-      height: 100px;
+      height: 25px;
       width: 100px;
       -webkit-animation-delay: 0.1s;
-      -webkit-animation-duration: 0.2s;
+      -webkit-animation-duration: 2s;
       -webkit-animation-timing-function: linear;
       -webkit-animation-name: anim;
     }
@@ -34,19 +34,32 @@
     #c {
       background-color: green;
       -webkit-animation-fill-mode: forwards;
-      -webkit-animation-iteration-count: 1.5;
+      -webkit-animation-iteration-count: 1.25;
     }
     #d {
       background-color: yellow;
       -webkit-animation-fill-mode: both;
-      -webkit-animation-iteration-count: 1.5;
+      -webkit-animation-iteration-count: 1.25;
     }
     #e {
-      background-color: #999;
+      background-color: gray;
       -webkit-animation-fill-mode: both;
-      -webkit-animation-iteration-count: 2.5;
+      -webkit-animation-iteration-count: 2.25;
       -webkit-animation-direction: alternate;
     }
+    #f {
+      background-color: silver;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2.25;
+      -webkit-animation-direction: alternate-reverse;
+    }
+    #g {
+      background-color: orange;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2.25;
+      -webkit-animation-timing-function: ease-out;
+      -webkit-animation-direction: alternate-reverse;
+    }
   </style>
   <script type="text/_javascript_" charset="utf-8">
     const numAnims = 5;
@@ -55,9 +68,11 @@
     const expectedValues = [
       {id: "a", start: 100, end: 100},
       {id: "b", start: 200, end: 100},
-      {id: "c", start: 100, end: 300},
-      {id: "d", start: 200, end: 300},
-      {id: "e", start: 200, end: 300}
+      {id: "c", start: 100, end: 225},
+      {id: "d", start: 200, end: 225},
+      {id: "e", start: 200, end: 225},
+      {id: "f", start: 300, end: 275},
+      {id: "g", start: 300, end: 291}
     ];
     var result = "";
 
@@ -80,11 +95,10 @@
             var expectedValue = expectedValues[i].end;
             var realValue = window.getComputedStyle(el).getPropertyCSSValue("left").getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
             if (Math.abs(expectedValue - realValue) < allowance) {
-              result += "PASS";
+              result += "PASS - end of animation - id: " + expectedValues[i].id + " close to expected: " + expectedValue + "<br>";
             } else {
-              result += "FAIL";
+              result += "FAIL - end of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
             }
-            result += " - end of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
         }
         document.getElementById('result').innerHTML = result;
 
@@ -128,6 +142,12 @@
 <div id="e" class="box">
   Both iterating
 </div>
+<div id="f" class="box">
+  Both reverse alternate
+</div>
+<div id="g" class="box">
+  Ease function
+</div>
 <div id="result">
 </div>
 </body>

Modified: trunk/LayoutTests/animations/fill-mode-reverse-expected.txt (187120 => 187121)


--- trunk/LayoutTests/animations/fill-mode-reverse-expected.txt	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/animations/fill-mode-reverse-expected.txt	2015-07-21 21:19:20 UTC (rev 187121)
@@ -11,7 +11,7 @@
 PASS - start of animation - id: e expected: 200 actual: 200
 PASS - end of animation - id: a expected: 100 actual: 100
 PASS - end of animation - id: b expected: 100 actual: 100
-PASS - end of animation - id: c expected: 300 actual: 300
-PASS - end of animation - id: d expected: 300 actual: 300
+PASS - end of animation - id: c expected: 200 actual: 200
+PASS - end of animation - id: d expected: 200 actual: 200
 PASS - end of animation - id: e expected: 200 actual: 200
 

Modified: trunk/LayoutTests/animations/fill-mode-reverse.html (187120 => 187121)


--- trunk/LayoutTests/animations/fill-mode-reverse.html	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/LayoutTests/animations/fill-mode-reverse.html	2015-07-21 21:19:20 UTC (rev 187121)
@@ -39,7 +39,7 @@
       -webkit-animation-fill-mode: both;
     }
     #e {
-      background-color: #999;
+      background-color: gray;
       -webkit-animation-fill-mode: both;
       -webkit-animation-iteration-count: 2;
       -webkit-animation-direction: alternate;
@@ -52,8 +52,8 @@
     const expectedValues = [
       {id: "a", start: 100, end: 100},
       {id: "b", start: 300, end: 100},
-      {id: "c", start: 100, end: 300},
-      {id: "d", start: 300, end: 300},
+      {id: "c", start: 100, end: 200},
+      {id: "d", start: 300, end: 200},
       {id: "e", start: 200, end: 200}
     ];
     var result = "";

Modified: trunk/Source/WebCore/ChangeLog (187120 => 187121)


--- trunk/Source/WebCore/ChangeLog	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/Source/WebCore/ChangeLog	2015-07-21 21:19:20 UTC (rev 187121)
@@ -1,3 +1,29 @@
+2015-07-21  Simon Fraser  <[email protected]>
+
+        Safari mis-applies "animation-fill-mode: forwards" when using fractional iteration count
+        https://bugs.webkit.org/show_bug.cgi?id=146996
+
+        Reviewed by Dean Jackson.
+
+        animation-fill-mode: forwards with fractional iteration counts always snapped to
+        1 or 0, depending on direction. Fix to compute the fill-forward state from the
+        correct keyframes.
+        
+        If filling forwards, AnimationBase::progress() sets the elapsed time to the duration,
+        then uses fractionalTime() to handle animation direction mapping. If the fractionalTime
+        is integral, we can return early, avoiding the cost of mapping through timing functions.
+
+        Tested by existing tests.
+
+        * page/animation/AnimationBase.cpp:
+        (WebCore::AnimationBase::progress):
+        (WebCore::AnimationBase::getElapsedTime):
+        * page/animation/KeyframeAnimation.cpp:
+        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty): It was possible
+        to end up with prevIndex == nextIndex with reverse animations, which resulted
+        in divide-by-zero when computing scale. Fix by picking a nextIndex that is different
+        from prevIndex.
+
 2015-07-21  David Hyatt  <[email protected]>
 
         Fix failing WK1 layout tests. Make sure to reset the text zoom scale

Modified: trunk/Source/WebCore/page/animation/AnimationBase.cpp (187120 => 187121)


--- trunk/Source/WebCore/page/animation/AnimationBase.cpp	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/Source/WebCore/page/animation/AnimationBase.cpp	2015-07-21 21:19:20 UTC (rev 187121)
@@ -618,23 +618,25 @@
     if (preActive())
         return 0;
 
+    if (postActive() || !m_animation->duration())
+        return 1.0;
+
     double elapsedTime = getElapsedTime();
 
     double duration = m_animation->duration();
     if (m_animation->iterationCount() > 0)
         duration *= m_animation->iterationCount();
 
-    if (postActive() || !m_animation->duration())
-        return 1.0;
+    if (fillingForwards())
+        elapsedTime = duration;
 
+    double fractionalTime = this->fractionalTime(scale, elapsedTime, offset);
+
     if (m_animation->iterationCount() > 0 && elapsedTime >= duration) {
-        const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
-        const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
-        return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0;
+        if (WTF::isIntegral(fractionalTime))
+            return fractionalTime;
     }
 
-    const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset);
-
     if (!timingFunction)
         timingFunction = m_animation->timingFunction().get();
 
@@ -740,7 +742,7 @@
         return m_pauseTime - m_startTime;
     if (m_startTime <= 0)
         return 0;
-    if (postActive())
+    if (postActive() || fillingForwards())
         return 1;
 
     return beginAnimationUpdateTime() - m_startTime;

Modified: trunk/Source/WebCore/page/animation/KeyframeAnimation.cpp (187120 => 187121)


--- trunk/Source/WebCore/page/animation/KeyframeAnimation.cpp	2015-07-21 20:57:06 UTC (rev 187120)
+++ trunk/Source/WebCore/page/animation/KeyframeAnimation.cpp	2015-07-21 21:19:20 UTC (rev 187121)
@@ -106,9 +106,16 @@
     if (prevIndex == -1)
         prevIndex = 0;
 
-    if (nextIndex == -1)
-        nextIndex = m_keyframes.size() - 1;
+    if (nextIndex == -1) {
+        int lastIndex = m_keyframes.size() - 1;
+        if (prevIndex == lastIndex)
+            nextIndex = 0;
+        else
+            nextIndex = lastIndex;
+    }
 
+    ASSERT(prevIndex != nextIndex);
+
     const KeyframeValue& prevKeyframe = m_keyframes[prevIndex];
     const KeyframeValue& nextKeyframe = m_keyframes[nextIndex];
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to