2010-09-09 Dean Jackson <dino@apple.com>
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Sep 2010 18:12:38 +0000 (18:12 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Sep 2010 18:12:38 +0000 (18:12 +0000)
        Reviewed by Simon Fraser.

        Fill mode is broken with multiple keyframes
        https://bugs.webkit.org/show_bug.cgi?id=41209

        With a forward fill mode the animation would tick after
        the end of the animation, causing the fractional
        duration of the animation to wrap. This meant the last
        style update would happen using the incorrect keyframes.
        The solution was to put clamps in for the elapsed time
        and current iteration count.

        Tests: animations/fill-mode-missing-from-to-keyframes.html
               animations/fill-mode-multiple-keyframes.html

        * page/animation/KeyframeAnimation.cpp:
        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty):

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

LayoutTests/ChangeLog
LayoutTests/animations/fill-mode-missing-from-to-keyframes-expected.txt [new file with mode: 0644]
LayoutTests/animations/fill-mode-missing-from-to-keyframes.html [new file with mode: 0644]
LayoutTests/animations/fill-mode-multiple-keyframes-expected.txt [new file with mode: 0644]
LayoutTests/animations/fill-mode-multiple-keyframes.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/page/animation/KeyframeAnimation.cpp

index 5a2ed28..f58c4b8 100644 (file)
@@ -1,3 +1,22 @@
+2010-09-09  Dean Jackson  <dino@apple.com>
+
+        Reviewed by Simon Fraser.
+
+        Fill mode is broken with multiple keyframes
+        https://bugs.webkit.org/show_bug.cgi?id=41209
+
+        With a forward fill mode the animation would tick after
+        the end of the animation, causing the fractional
+        duration of the animation to wrap. This meant the last
+        style update would happen using the incorrect keyframes.
+        The solution was to put clamps in for the elapsed time
+        and current iteration count.
+
+        * animations/fill-mode-missing-from-to-keyframes-expected.txt: Added.
+        * animations/fill-mode-missing-from-to-keyframes.html: Added.
+        * animations/fill-mode-multiple-keyframes-expected.txt: Added.
+        * animations/fill-mode-multiple-keyframes.html: Added.
+
 2010-09-09  Chris Fleizach  <cfleizach@apple.com>
 
         Reviewed by David Kilzer.
diff --git a/LayoutTests/animations/fill-mode-missing-from-to-keyframes-expected.txt b/LayoutTests/animations/fill-mode-missing-from-to-keyframes-expected.txt
new file mode 100644 (file)
index 0000000..69b63a7
--- /dev/null
@@ -0,0 +1,22 @@
+This test performs an animation of the left property with four different fill modes. It animates over 0.1 second with a 0.1 second delay. It takes snapshots at document load and the end of the animation.
+None - from/to present
+Backwards - from/to present
+Forwards - from/to present
+Both - from/to present
+Both iterating - from/to present
+None - from missing
+Backwards - from missing
+Forwards - from missing
+Both - from missing
+Both iterating - from missing
+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: 200 actual: 200
+
diff --git a/LayoutTests/animations/fill-mode-missing-from-to-keyframes.html b/LayoutTests/animations/fill-mode-missing-from-to-keyframes.html
new file mode 100644 (file)
index 0000000..34807f5
--- /dev/null
@@ -0,0 +1,194 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <title>Test missing keyframes with fill modes</title>
+  <style type="text/css" media="screen">
+    .box {
+      position: relative;
+      left: 100px;
+      top: 10px;
+      height: 30px;
+      width: 200px;
+      -webkit-animation-delay: 0.1s;
+      -webkit-animation-duration: 0.1s;
+      -webkit-animation-timing-function: linear;
+    }
+    @-webkit-keyframes anim1 {
+        from { left: 200px; }
+        50% { left: 250px; }
+        to   { left: 300px; }
+    }
+    @-webkit-keyframes anim2 {
+        50% { left: 250px; }
+        to   { left: 300px; }
+    }
+    @-webkit-keyframes anim3 {
+        from { left: 200px; }
+        50% { left: 250px; }
+    }
+    @-webkit-keyframes anim4 {
+        50% { left: 250px; }
+    }
+
+    #a {
+      background-color: blue;
+      -webkit-animation-fill-mode: none;
+      -webkit-animation-name: anim1;
+    }
+    #b {
+      background-color: red;
+      -webkit-animation-fill-mode: backwards;
+      -webkit-animation-name: anim1;
+    }
+    #c {
+      background-color: green;
+      -webkit-animation-fill-mode: forwards;
+      -webkit-animation-name: anim1;
+    }
+    #d {
+      background-color: yellow;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-name: anim1;
+    }
+    #e {
+      background-color: #999;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: alternate;
+      -webkit-animation-name: anim1;
+    }
+
+    #f {
+      background-color: blue;
+      -webkit-animation-fill-mode: none;
+      -webkit-animation-name: anim2;
+    }
+    #g {
+      background-color: red;
+      -webkit-animation-fill-mode: backwards;
+      -webkit-animation-name: anim2;
+    }
+    #h {
+      background-color: green;
+      -webkit-animation-fill-mode: forwards;
+      -webkit-animation-name: anim2;
+    }
+    #i {
+      background-color: yellow;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-name: anim2;
+    }
+    #j {
+      background-color: #999;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: alternate;
+      -webkit-animation-name: anim2;
+    }
+
+  </style>
+  <script type="text/javascript" charset="utf-8">
+    const numAnims = 10;
+    var animsFinished = 0;
+    const allowance = 5;
+    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: 200}
+    ];
+    var result = "";
+
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+
+    function animationEnded(event) {
+        if (++animsFinished == numAnims) {
+            setTimeout(endTest, 0); // this set timeout should be ok in the test environment
+                                    // since we're just giving style a chance to resolve
+        }
+    };
+
+    function endTest() {
+
+        for (var i=0; i < expectedValues.length; i++) {
+            var el = document.getElementById(expectedValues[i].id);
+            var expectedValue = expectedValues[i].end;
+            var realValue = window.getComputedStyle(el).getPropertyCSSValue("left").getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+            if (Math.abs(expectedValue - realValue) < allowance) {
+              result += "PASS";
+            } else {
+              result += "FAIL";
+            }
+            result += " - end of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+        }
+        document.getElementById('result').innerHTML = result;
+
+        if (window.layoutTestController)
+            layoutTestController.notifyDone();
+    }
+
+    window.onload = function () {
+        for (var i=0; i < expectedValues.length; i++) {
+            var el = document.getElementById(expectedValues[i].id);
+            var expectedValue = expectedValues[i].start;
+            var realValue = window.getComputedStyle(el).getPropertyCSSValue("left").getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+            if (Math.abs(expectedValue - realValue) < allowance) {
+              result += "PASS";
+            } else {
+              result += "FAIL";
+            }
+            result += " - start of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+        }
+        document.addEventListener("webkitAnimationEnd", animationEnded, false);
+    };
+
+  </script>
+</head>
+<body>
+This test performs an animation of the left property with four different
+fill modes. It animates over 0.1 second with a 0.1 second delay.
+It takes snapshots at document load and the end of the animation.
+
+<div id="a" class="box">
+  None - from/to present
+</div>
+<div id="b" class="box">
+  Backwards - from/to present
+</div>
+<div id="c" class="box">
+  Forwards - from/to present
+</div>
+<div id="d" class="box">
+  Both - from/to present
+</div>
+<div id="e" class="box">
+  Both iterating - from/to present
+</div>
+
+<div id="f" class="box">
+  None - from missing
+</div>
+<div id="g" class="box">
+  Backwards - from missing
+</div>
+<div id="h" class="box">
+  Forwards - from missing
+</div>
+<div id="i" class="box">
+  Both - from missing
+</div>
+<div id="j" class="box">
+  Both iterating - from missing
+</div>
+
+<div id="result">
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/fill-mode-multiple-keyframes-expected.txt b/LayoutTests/animations/fill-mode-multiple-keyframes-expected.txt
new file mode 100644 (file)
index 0000000..f9e9d88
--- /dev/null
@@ -0,0 +1,32 @@
+This test performs an animation of the left property with four different fill modes on two sets of objects. The first set has animations with only from and to keyframes. The second set has animations with from, to and a third keyframe at 50%. It animates over 0.1 second with a 0.1 second delay. It takes snapshots at document load and the end of the animation.
+None - 2 keyframes
+Backwards - 2 keyframes
+Forwards - 2 keyframes
+Both - 2 keyframes
+Both iterating - 2 keyframes
+None - 3 keyframes
+Backwards - 3 keyframes
+Forwards - 3 keyframes
+Both - 3 keyframes
+Both iterating - 3 keyframes
+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 - start of animation - id: f expected: 100 actual: 100
+PASS - start of animation - id: g expected: 200 actual: 200
+PASS - start of animation - id: h expected: 100 actual: 100
+PASS - start of animation - id: i expected: 200 actual: 200
+PASS - start of animation - id: j 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: 200 actual: 200
+PASS - end of animation - id: f expected: 100 actual: 100
+PASS - end of animation - id: g expected: 100 actual: 100
+PASS - end of animation - id: h expected: 300 actual: 300
+PASS - end of animation - id: i expected: 300 actual: 300
+PASS - end of animation - id: j expected: 200 actual: 200
+
diff --git a/LayoutTests/animations/fill-mode-multiple-keyframes.html b/LayoutTests/animations/fill-mode-multiple-keyframes.html
new file mode 100644 (file)
index 0000000..b698ac3
--- /dev/null
@@ -0,0 +1,169 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <title>Test simple animation with fill modes</title>
+  <style type="text/css" media="screen">
+    .box {
+      position: relative;
+      left: 100px;
+      top: 10px;
+      height: 30px;
+      width: 200px;
+      -webkit-animation-delay: 0.1s;
+      -webkit-animation-duration: 0.1s;
+      -webkit-animation-timing-function: linear;
+    }
+
+    .two-keyframes {
+      -webkit-animation-name: anim1;
+    }
+
+    .three-keyframes {
+      -webkit-animation-name: anim2;
+    }
+
+    @-webkit-keyframes anim1 {
+        from { left: 200px; }
+        to   { left: 300px; }
+    }
+    @-webkit-keyframes anim2 {
+        from { left: 200px; }
+        50% { left: 250px; }
+        to   { left: 300px; }
+    }
+
+    #a, #f {
+      background-color: blue;
+      -webkit-animation-fill-mode: none;
+    }
+    #b, #g {
+      background-color: red;
+      -webkit-animation-fill-mode: backwards;
+    }
+    #c, #h {
+      background-color: green;
+      -webkit-animation-fill-mode: forwards;
+    }
+    #d, #i {
+      background-color: yellow;
+      -webkit-animation-fill-mode: both;
+    }
+    #e, #j {
+      background-color: #999;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: alternate;
+    }
+  </style>
+  <script type="text/javascript" charset="utf-8">
+    const numAnims = 10;
+    var animsFinished = 0;
+    const allowance = 5;
+    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: 200},
+      {id: "f", start: 100, end: 100},
+      {id: "g", start: 200, end: 100},
+      {id: "h", start: 100, end: 300},
+      {id: "i", start: 200, end: 300},
+      {id: "j", start: 200, end: 200}
+    ];
+    var result = "";
+
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+
+    function animationEnded(event) {
+        if (++animsFinished == numAnims) {
+            setTimeout(endTest, 0); // this set timeout should be ok in the test environment
+                                    // since we're just giving style a chance to resolve
+        }
+    };
+
+    function endTest() {
+
+        for (var i=0; i < expectedValues.length; i++) {
+            var el = document.getElementById(expectedValues[i].id);
+            var expectedValue = expectedValues[i].end;
+            var realValue = window.getComputedStyle(el).getPropertyCSSValue("left").getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+            if (Math.abs(expectedValue - realValue) < allowance) {
+              result += "PASS";
+            } else {
+              result += "FAIL";
+            }
+            result += " - end of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+        }
+        document.getElementById('result').innerHTML = result;
+
+        if (window.layoutTestController)
+            layoutTestController.notifyDone();
+    }
+
+    window.onload = function () {
+        for (var i=0; i < expectedValues.length; i++) {
+            var el = document.getElementById(expectedValues[i].id);
+            var expectedValue = expectedValues[i].start;
+            var realValue = window.getComputedStyle(el).getPropertyCSSValue("left").getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+            if (Math.abs(expectedValue - realValue) < allowance) {
+              result += "PASS";
+            } else {
+              result += "FAIL";
+            }
+            result += " - start of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+        }
+        document.addEventListener("webkitAnimationEnd", animationEnded, false);
+    };
+
+  </script>
+</head>
+<body>
+This test performs an animation of the left property with four different
+fill modes on two sets of objects. The first set has animations with
+only from and to keyframes. The second set has animations with from, to and
+a third keyframe at 50%. It animates over 0.1 second with a 0.1 second delay.
+It takes snapshots at document load and the end of the animation.
+
+<div id="a" class="box two-keyframes">
+  None - 2 keyframes
+</div>
+<div id="b" class="box two-keyframes">
+  Backwards - 2 keyframes
+</div>
+<div id="c" class="box two-keyframes">
+  Forwards - 2 keyframes
+</div>
+<div id="d" class="box two-keyframes">
+  Both - 2 keyframes
+</div>
+<div id="e" class="box two-keyframes">
+  Both iterating - 2 keyframes
+</div>
+
+<div id="f" class="box three-keyframes">
+  None - 3 keyframes
+</div>
+<div id="g" class="box three-keyframes">
+  Backwards - 3 keyframes
+</div>
+<div id="h" class="box three-keyframes">
+  Forwards - 3 keyframes
+</div>
+<div id="i" class="box three-keyframes">
+  Both - 3 keyframes
+</div>
+<div id="j" class="box three-keyframes">
+  Both iterating - 3 keyframes
+</div>
+
+<div id="result">
+</div>
+</body>
+</html>
index 1ed45db..fd4846a 100644 (file)
@@ -1,3 +1,23 @@
+2010-09-09  Dean Jackson  <dino@apple.com>
+
+        Reviewed by Simon Fraser.
+
+        Fill mode is broken with multiple keyframes
+        https://bugs.webkit.org/show_bug.cgi?id=41209
+
+        With a forward fill mode the animation would tick after
+        the end of the animation, causing the fractional
+        duration of the animation to wrap. This meant the last
+        style update would happen using the incorrect keyframes.
+        The solution was to put clamps in for the elapsed time
+        and current iteration count.
+
+        Tests: animations/fill-mode-missing-from-to-keyframes.html
+               animations/fill-mode-multiple-keyframes.html
+
+        * page/animation/KeyframeAnimation.cpp:
+        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty):
+
 2010-09-09  Chris Fleizach  <cfleizach@apple.com>
 
         Reviewed by David Kilzer.
index eeddb94..df963fb 100644 (file)
@@ -39,6 +39,8 @@
 #include "RenderStyle.h"
 #include <wtf/UnusedParam.h>
 
+using namespace std;
+
 namespace WebCore {
 
 KeyframeAnimation::KeyframeAnimation(const Animation* animation, RenderObject* renderer, int index, CompositeAnimation* compAnim, RenderStyle* unanimatedStyle)
@@ -66,6 +68,8 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(int property, const Re
 {
     // Find the first key
     double elapsedTime = getElapsedTime();
+    if (m_animation->duration() && m_animation->iterationCount() != Animation::IterationCountInfinite)
+        elapsedTime = min(elapsedTime, m_animation->duration() * m_animation->iterationCount());
 
     double fractionalTime = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
 
@@ -75,6 +79,8 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(int property, const Re
 
     // FIXME: share this code with AnimationBase::progress().
     int iteration = static_cast<int>(fractionalTime);
+    if (m_animation->iterationCount() != Animation::IterationCountInfinite)
+        iteration = min(iteration, m_animation->iterationCount() - 1);
     fractionalTime -= iteration;
     
     bool reversing = (m_animation->direction() == Animation::AnimationDirectionAlternate) && (iteration & 1);