Support reverse and alternate-reverse in CA animations
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Apr 2012 04:59:53 +0000 (04:59 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Apr 2012 04:59:53 +0000 (04:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78041

Reviewed by Beth Dakin.

Source/WebCore:

CoreAnimation does not natively support reverse and alternate-reverse
animation directions so we need to flip the animation values (keyframe
keys and timing functions) that we send to GraphicsLayerCA. Unfortunately
this code adds a lot of conditionals because it isn't as simple as
reversing the order of keys. You also now have a different alignment of
timing functions to the reversed list.

New tests to cover the two new directions, making sure the timing
functions are correctly inverted, and exercising fill modes.

Tests: animations/animation-direction-reverse-fill-mode-hardware.html
       animations/animation-direction-reverse-fill-mode.html
       animations/animation-direction-reverse-hardware-opacity.html
       animations/animation-direction-reverse-hardware.html
       animations/animation-direction-reverse-non-hardware.html
       animations/animation-direction-reverse-timing-functions-hardware.html
       animations/animation-direction-reverse-timing-functions.html

* platform/graphics/ca/GraphicsLayerCA.cpp:
  Handle the previously unsupported animation directions, reversing
  the list of values and keytimes that would be used to create
  the CA Animation.
(WebCore::GraphicsLayerCA::addAnimation):
  Do not create an animation if on Windows and using a reverse
  direction.
(WebCore::GraphicsLayerCA::createFilterAnimationsFromKeyframes):
(WebCore::GraphicsLayerCA::setupAnimation):
(WebCore::GraphicsLayerCA::setAnimationEndpoints):
(WebCore::GraphicsLayerCA::setAnimationKeyframes):
(WebCore::GraphicsLayerCA::setTransformAnimationEndpoints):
(WebCore::GraphicsLayerCA::setTransformAnimationKeyframes):
(WebCore::GraphicsLayerCA::setFilterAnimationEndpoints):
(WebCore::GraphicsLayerCA::setFilterAnimationKeyframes):
* platform/graphics/ca/PlatformCAAnimation.h:
(PlatformCAAnimation): Pass through a flag that tells the CA Animation
that it should invert the timing functions.
* platform/graphics/ca/mac/PlatformCAAnimationMac.mm:
(toCAMediaTimingFunction): Add a parameter that will invert the timing
function coefficients if necessary.
(PlatformCAAnimation::setTimingFunction):
(PlatformCAAnimation::setTimingFunctions):
* platform/graphics/ca/win/PlatformCAAnimationWin.cpp:
(toCACFTimingFunction):
  New unused parameter.

LayoutTests:

Tests support for reverse and alternate-reverse animations on
CoreAnimation objects, as well as filling out some of the software
animator tests. There are three variables to exercise: reverse vs
forward direction animations, whether reversed timing functions are
inverted correctly, and that fill mode respects the direction of
animation.

Refactored the animation test helper class so we could reuse
property parsing and evaluation.

* animations/animation-direction-reverse-fill-mode-expected.txt: Added.
* animations/animation-direction-reverse-fill-mode-hardware-expected.txt: Added.
* animations/animation-direction-reverse-fill-mode-hardware.html: Added.
* animations/animation-direction-reverse-fill-mode.html: Added.
* animations/animation-direction-reverse-hardware-expected.txt: Added.
* animations/animation-direction-reverse-hardware-opacity-expected.txt: Added.
* animations/animation-direction-reverse-hardware-opacity.html: Added.
* animations/animation-direction-reverse-hardware.html: Added.
* animations/animation-direction-reverse-non-hardware-expected.txt: Added.
* animations/animation-direction-reverse-non-hardware.html: Added.
* animations/animation-direction-reverse-timing-functions-expected.txt: Added.
* animations/animation-direction-reverse-timing-functions-hardware-expected.txt: Added.
* animations/animation-direction-reverse-timing-functions-hardware.html: Added.
* animations/animation-direction-reverse-timing-functions.html: Added.
* animations/resources/animation-test-helpers.js:
(checkExpectedValue):
(getPropertyValue):
(comparePropertyValue):

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/animations/animation-direction-reverse-fill-mode-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-fill-mode-hardware-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-fill-mode-hardware.html [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-fill-mode.html [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-hardware-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-hardware-opacity-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-hardware-opacity.html [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-hardware.html [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-non-hardware-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-non-hardware.html [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-timing-functions-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-timing-functions-hardware-expected.txt [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-timing-functions-hardware.html [new file with mode: 0644]
LayoutTests/animations/animation-direction-reverse-timing-functions.html [new file with mode: 0644]
LayoutTests/animations/resources/animation-test-helpers.js
Source/WebCore/ChangeLog
Source/WebCore/platform/animation/Animation.h
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
Source/WebCore/platform/graphics/ca/PlatformCAAnimation.h
Source/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm
Source/WebCore/platform/graphics/ca/win/PlatformCAAnimationWin.cpp

index a3c8677..127783f 100644 (file)
@@ -1,3 +1,39 @@
+2012-04-27  Dean Jackson  <dino@apple.com>
+
+        Support reverse and alternate-reverse in CA animations
+        https://bugs.webkit.org/show_bug.cgi?id=78041
+
+        Reviewed by Beth Dakin.
+
+        Tests support for reverse and alternate-reverse animations on
+        CoreAnimation objects, as well as filling out some of the software
+        animator tests. There are three variables to exercise: reverse vs
+        forward direction animations, whether reversed timing functions are
+        inverted correctly, and that fill mode respects the direction of
+        animation.
+
+        Refactored the animation test helper class so we could reuse
+        property parsing and evaluation.
+
+        * animations/animation-direction-reverse-fill-mode-expected.txt: Added.
+        * animations/animation-direction-reverse-fill-mode-hardware-expected.txt: Added.
+        * animations/animation-direction-reverse-fill-mode-hardware.html: Added.
+        * animations/animation-direction-reverse-fill-mode.html: Added.
+        * animations/animation-direction-reverse-hardware-expected.txt: Added.
+        * animations/animation-direction-reverse-hardware-opacity-expected.txt: Added.
+        * animations/animation-direction-reverse-hardware-opacity.html: Added.
+        * animations/animation-direction-reverse-hardware.html: Added.
+        * animations/animation-direction-reverse-non-hardware-expected.txt: Added.
+        * animations/animation-direction-reverse-non-hardware.html: Added.
+        * animations/animation-direction-reverse-timing-functions-expected.txt: Added.
+        * animations/animation-direction-reverse-timing-functions-hardware-expected.txt: Added.
+        * animations/animation-direction-reverse-timing-functions-hardware.html: Added.
+        * animations/animation-direction-reverse-timing-functions.html: Added.
+        * animations/resources/animation-test-helpers.js:
+        (checkExpectedValue):
+        (getPropertyValue):
+        (comparePropertyValue):
+
 2012-04-27  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r115407.
diff --git a/LayoutTests/animations/animation-direction-reverse-fill-mode-expected.txt b/LayoutTests/animations/animation-direction-reverse-fill-mode-expected.txt
new file mode 100644 (file)
index 0000000..a5c06d6
--- /dev/null
@@ -0,0 +1,17 @@
+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
+Backwards
+Forwards
+Both
+Both iterating
+PASS - start of animation - id: a expected: 100 actual: 100
+PASS - start of animation - id: b expected: 300 actual: 300
+PASS - start of animation - id: c expected: 100 actual: 100
+PASS - start of animation - id: d expected: 300 actual: 300
+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: e expected: 300 actual: 300
+
diff --git a/LayoutTests/animations/animation-direction-reverse-fill-mode-hardware-expected.txt b/LayoutTests/animations/animation-direction-reverse-fill-mode-hardware-expected.txt
new file mode 100644 (file)
index 0000000..9992582
--- /dev/null
@@ -0,0 +1,8 @@
+This test performs an animation of the transform property with 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 animations.
+Both Iterate - Reverse
+Both Iterate - Alternate Reverse
+PASS - start of animation - id: a expected: 300 actual: matrix(1, 0, 0, 1, 300, 0)
+PASS - start of animation - id: b expected: 300 actual: matrix(1, 0, 0, 1, 300, 0)
+PASS - end of animation - id: a expected: 200 actual: matrix(1, 0, 0, 1, 200, 0)
+PASS - end of animation - id: b expected: 300 actual: matrix(1, 0, 0, 1, 300, 0)
+
diff --git a/LayoutTests/animations/animation-direction-reverse-fill-mode-hardware.html b/LayoutTests/animations/animation-direction-reverse-fill-mode-hardware.html
new file mode 100644 (file)
index 0000000..dd32feb
--- /dev/null
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test simple fill mode on transform</title>
+  <style>
+    .box {
+      position: relative;
+      left: 10px;
+      top: 10px;
+      height: 100px;
+      width: 100px;
+      -webkit-transform: translate3d(100px, 0, 0);
+      -webkit-animation-delay: 0.1s;
+      -webkit-animation-duration: 0.1s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-name: anim;
+    }
+    @-webkit-keyframes anim {
+        from { -webkit-transform: translate3d(200px, 0, 0); }
+        to   { -webkit-transform: translate3d(300px, 0, 0); }
+    }
+    #a {
+      background-color: #f99;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: reverse;
+    }
+    #b {
+      background-color: #999;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: alternate-reverse;
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script>
+    const numAnims = 1;
+    var animsFinished = 0;
+    const allowance = 5;
+    const expectedValues = [
+      {id: "a", start: 300, end: 200},
+      {id: "b", start: 300, end: 300}
+    ];
+    var result = "";
+
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+
+    function animationEnded(event) {
+        if (++animsFinished == numAnims) {
+            setTimeout(endTest, 0); // This call to setTimeout 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 realValue = getPropertyValue("webkitTransform.4", expectedValues[i].id);
+            var expectedValue = expectedValues[i].end;
+            if (comparePropertyValue("webkitTransform.4", realValue, expectedValue, 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 realValue = getPropertyValue("webkitTransform.4", expectedValues[i].id);
+          var expectedValue = expectedValues[i].start;
+          if (comparePropertyValue("webkitTransform.4", realValue, expectedValue, 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 transform property with 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 animations.
+<div id="a" class="box">
+  Both Iterate - Reverse
+</div>
+<div id="b" class="box">
+  Both Iterate - Alternate Reverse
+</div>
+<div id="result">
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/animation-direction-reverse-fill-mode.html b/LayoutTests/animations/animation-direction-reverse-fill-mode.html
new file mode 100644 (file)
index 0000000..7b3c2e1
--- /dev/null
@@ -0,0 +1,125 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <title>Test animation direction reverse with fill modes</title>
+  <style>
+    .box {
+      position: relative;
+      left: 100px;
+      top: 10px;
+      height: 100px;
+      width: 100px;
+      -webkit-animation-delay: 0.1s;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-name: anim;
+      -webkit-animation-direction: reverse;
+    }
+    @-webkit-keyframes anim {
+        from { left: 200px; }
+        to   { left: 300px; }
+    }
+    #a {
+      background-color: blue;
+      -webkit-animation-fill-mode: none;
+    }
+    #b {
+      background-color: red;
+      -webkit-animation-fill-mode: backwards;
+    }
+    #c {
+      background-color: green;
+      -webkit-animation-fill-mode: forwards;
+    }
+    #d {
+      background-color: yellow;
+      -webkit-animation-fill-mode: both;
+    }
+    #e {
+      background-color: #999;
+      -webkit-animation-fill-mode: both;
+      -webkit-animation-iteration-count: 2;
+      -webkit-animation-direction: alternate-reverse;
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script type="text/javascript" charset="utf-8">
+    const numAnims = 5;
+    var animsFinished = 0;
+    const allowance = 5;
+    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: "e", start: 300, end: 300}
+    ];
+    var result = "";
+
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+
+    function animationEnded(event) {
+        if (++animsFinished == numAnims) {
+            setTimeout(endTest, 0); // This call to setTimeout 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 realValue = getPropertyValue("left", expectedValues[i].id);
+            var expectedValue = expectedValues[i].end;
+            if (comparePropertyValue("left", realValue, expectedValue, 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 realValue = getPropertyValue("left", expectedValues[i].id);
+            var expectedValue = expectedValues[i].start;
+            if (comparePropertyValue("left", realValue, expectedValue, 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
+</div>
+<div id="b" class="box">
+  Backwards
+</div>
+<div id="c" class="box">
+  Forwards
+</div>
+<div id="d" class="box">
+  Both
+</div>
+<div id="e" class="box">
+  Both iterating
+</div>
+<div id="result">
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/animation-direction-reverse-hardware-expected.txt b/LayoutTests/animations/animation-direction-reverse-hardware-expected.txt
new file mode 100644 (file)
index 0000000..29470ec
--- /dev/null
@@ -0,0 +1,25 @@
+2 keyframes: normal
+2 keyframes: alternate
+2 keyframes: reverse
+2 keyframes: alternate-reverse
+4 keyframes: normal
+4 keyframes: alternate
+4 keyframes: reverse
+4 keyframes: alternate-reverse
+PASS - "webkitTransform" property for "box1" element at 0.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box2" element at 0.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box3" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box4" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box1" element at 2.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box2" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box3" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box4" element at 2.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box5" element at 0.2s saw something close to: 1,0,0,1,40,0
+PASS - "webkitTransform" property for "box6" element at 0.2s saw something close to: 1,0,0,1,40,0
+PASS - "webkitTransform" property for "box7" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box8" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box5" element at 2.2s saw something close to: 1,0,0,1,40,0
+PASS - "webkitTransform" property for "box6" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box7" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box8" element at 2.2s saw something close to: 1,0,0,1,40,0
+
diff --git a/LayoutTests/animations/animation-direction-reverse-hardware-opacity-expected.txt b/LayoutTests/animations/animation-direction-reverse-hardware-opacity-expected.txt
new file mode 100644 (file)
index 0000000..b383ea8
--- /dev/null
@@ -0,0 +1,25 @@
+2 keyframes: normal
+2 keyframes: alternate
+2 keyframes: reverse
+2 keyframes: alternate-reverse
+4 keyframes: normal
+4 keyframes: alternate
+4 keyframes: reverse
+4 keyframes: alternate-reverse
+PASS - "opacity" property for "box1" element at 0.2s saw something close to: 0.2
+PASS - "opacity" property for "box2" element at 0.2s saw something close to: 0.2
+PASS - "opacity" property for "box3" element at 0.2s saw something close to: 0.8
+PASS - "opacity" property for "box4" element at 0.2s saw something close to: 0.8
+PASS - "opacity" property for "box1" element at 1.2s saw something close to: 0.2
+PASS - "opacity" property for "box2" element at 1.2s saw something close to: 0.8
+PASS - "opacity" property for "box3" element at 1.2s saw something close to: 0.8
+PASS - "opacity" property for "box4" element at 1.2s saw something close to: 0.2
+PASS - "opacity" property for "box5" element at 0.2s saw something close to: 0.4
+PASS - "opacity" property for "box6" element at 0.2s saw something close to: 0.4
+PASS - "opacity" property for "box7" element at 0.2s saw something close to: 0.7
+PASS - "opacity" property for "box8" element at 0.2s saw something close to: 0.7
+PASS - "opacity" property for "box5" element at 1.2s saw something close to: 0.4
+PASS - "opacity" property for "box6" element at 1.2s saw something close to: 0.7
+PASS - "opacity" property for "box7" element at 1.2s saw something close to: 0.7
+PASS - "opacity" property for "box8" element at 1.2s saw something close to: 0.4
+
diff --git a/LayoutTests/animations/animation-direction-reverse-hardware-opacity.html b/LayoutTests/animations/animation-direction-reverse-hardware-opacity.html
new file mode 100644 (file)
index 0000000..edcb440
--- /dev/null
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test of -webkit-animation-direction on composited elements (opacity)</title>
+  <style>
+    body {
+      margin: 0;
+    }
+
+    .box {
+      position: relative;
+      left: 20px;
+      top: 10px;
+      height: 50px;
+      width: 250px;
+      margin-bottom: 10px;
+      -webkit-animation-duration: 1s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-iteration-count: 8;
+    }
+    
+    .fade1 {
+      -webkit-animation-name: fade1;
+      background-color: blue;
+      color: white;
+    }
+
+    .fade2 {
+      -webkit-animation-name: fade2;
+      background-color: orange;
+    }
+
+    .normal {
+      -webkit-animation-direction: normal;
+    }
+
+    .alternate {
+      -webkit-animation-direction: alternate;
+    }
+
+    .reverse {
+      -webkit-animation-direction: reverse;
+    }
+
+    .alternate-reverse {
+      -webkit-animation-direction: alternate-reverse;
+    }
+    
+    @-webkit-keyframes fade1 {
+      from { opacity: 0; }
+      to { opacity: 1.0; }
+    }
+
+    @-webkit-keyframes fade2 {
+      0% { opacity: 0.0; }
+      40% { opacity: 0.8; }
+      60% { opacity: 0.4; }
+      100% { opacity: 1.0; }
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script>
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["fade1", 0.2, "box1", "opacity", 0.2, 0.15],
+      ["fade1", 0.2, "box2", "opacity", 0.2, 0.15],
+      ["fade1", 0.2, "box3", "opacity", 0.8, 0.15],
+      ["fade1", 0.2, "box4", "opacity", 0.8, 0.15],
+      ["fade1", 1.2, "box1", "opacity", 0.2, 0.15],
+      ["fade1", 1.2, "box2", "opacity", 0.8, 0.15],
+      ["fade1", 1.2, "box3", "opacity", 0.8, 0.15],
+      ["fade1", 1.2, "box4", "opacity", 0.2, 0.15],
+      ["fade2", 0.2, "box5", "opacity", 0.4, 0.15],
+      ["fade2", 0.2, "box6", "opacity", 0.4, 0.15],
+      ["fade2", 0.2, "box7", "opacity", 0.7, 0.15],
+      ["fade2", 0.2, "box8", "opacity", 0.7, 0.15],
+      ["fade2", 1.2, "box5", "opacity", 0.4, 0.15],
+      ["fade2", 1.2, "box6", "opacity", 0.7, 0.15],
+      ["fade2", 1.2, "box7", "opacity", 0.7, 0.15],
+      ["fade2", 1.2, "box8", "opacity", 0.4, 0.15],
+    ];
+
+    runAnimationTest(expectedValues);
+
+  </script>
+</head>
+<body>
+<div id="box1" class="box fade1 normal">2 keyframes: normal</div>
+<div id="box2" class="box fade1 alternate">2 keyframes: alternate</div>
+<div id="box3" class="box fade1 reverse">2 keyframes: reverse</div>
+<div id="box4" class="box fade1 alternate-reverse">2 keyframes: alternate-reverse</div>
+<div id="box5" class="box fade2 normal">4 keyframes: normal</div>
+<div id="box6" class="box fade2 alternate">4 keyframes: alternate</div>
+<div id="box7" class="box fade2 reverse">4 keyframes: reverse</div>
+<div id="box8" class="box fade2 alternate-reverse">4 keyframes: alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/animation-direction-reverse-hardware.html b/LayoutTests/animations/animation-direction-reverse-hardware.html
new file mode 100644 (file)
index 0000000..9a8b428
--- /dev/null
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test of -webkit-animation-direction on composited elements</title>
+  <style>
+    body {
+      margin: 0;
+    }
+
+    .box {
+      position: relative;
+      left: 20px;
+      top: 10px;
+      height: 50px;
+      width: 250px;
+      margin-bottom: 10px;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-iteration-count: 8;
+    }
+    
+    .move1 {
+      -webkit-animation-name: move1;
+      background-color: blue;
+      color: white;
+    }
+
+    .move2 {
+      -webkit-animation-name: move2;
+      background-color: orange;
+    }
+
+    .normal {
+      -webkit-animation-direction: normal;
+    }
+
+    .alternate {
+      -webkit-animation-direction: alternate;
+    }
+
+    .reverse {
+      -webkit-animation-direction: reverse;
+    }
+
+    .alternate-reverse {
+      -webkit-animation-direction: alternate-reverse;
+    }
+    
+    @-webkit-keyframes move1 {
+      from { -webkit-transform: translateX(0px); }
+      to { -webkit-transform: translateX(200px); }
+    }
+
+    @-webkit-keyframes move2 {
+      0% { -webkit-transform: translateX(0px); }
+      40% { -webkit-transform: translateX(160px); }
+      60% { -webkit-transform: translateX(120px); }
+      100% { -webkit-transform: translateX(200px); }
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script>
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move1", 0.2, "box1", "webkitTransform", [1,0,0,1, 20,0], 20],
+      ["move1", 0.2, "box2", "webkitTransform", [1,0,0,1, 20,0], 20],
+      ["move1", 0.2, "box3", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move1", 0.2, "box4", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move1", 2.2, "box1", "webkitTransform", [1,0,0,1, 20,0], 20],
+      ["move1", 2.2, "box2", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move1", 2.2, "box3", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move1", 2.2, "box4", "webkitTransform", [1,0,0,1, 20,0], 20],
+      ["move2", 0.2, "box5", "webkitTransform", [1,0,0,1, 40,0], 20],
+      ["move2", 0.2, "box6", "webkitTransform", [1,0,0,1, 40,0], 20],
+      ["move2", 0.2, "box7", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move2", 0.2, "box8", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move2", 2.2, "box5", "webkitTransform", [1,0,0,1, 40,0], 20],
+      ["move2", 2.2, "box6", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move2", 2.2, "box7", "webkitTransform", [1,0,0,1, 180,0], 20],
+      ["move2", 2.2, "box8", "webkitTransform", [1,0,0,1, 40,0], 20],
+    ];
+
+    runAnimationTest(expectedValues);
+
+  </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">2 keyframes: normal</div>
+<div id="box2" class="box move1 alternate">2 keyframes: alternate</div>
+<div id="box3" class="box move1 reverse">2 keyframes: reverse</div>
+<div id="box4" class="box move1 alternate-reverse">2 keyframes: alternate-reverse</div>
+<div id="box5" class="box move2 normal">4 keyframes: normal</div>
+<div id="box6" class="box move2 alternate">4 keyframes: alternate</div>
+<div id="box7" class="box move2 reverse">4 keyframes: reverse</div>
+<div id="box8" class="box move2 alternate-reverse">4 keyframes: alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/animation-direction-reverse-non-hardware-expected.txt b/LayoutTests/animations/animation-direction-reverse-non-hardware-expected.txt
new file mode 100644 (file)
index 0000000..8771b92
--- /dev/null
@@ -0,0 +1,25 @@
+2 keyframes: normal
+2 keyframes: alternate
+2 keyframes: reverse
+2 keyframes: alternate-reverse
+4 keyframes: normal
+4 keyframes: alternate
+4 keyframes: reverse
+4 keyframes: alternate-reverse
+PASS - "left" property for "box1" element at 0.2s saw something close to: 20
+PASS - "left" property for "box2" element at 0.2s saw something close to: 20
+PASS - "left" property for "box3" element at 0.2s saw something close to: 180
+PASS - "left" property for "box4" element at 0.2s saw something close to: 180
+PASS - "left" property for "box1" element at 2.2s saw something close to: 20
+PASS - "left" property for "box2" element at 2.2s saw something close to: 180
+PASS - "left" property for "box3" element at 2.2s saw something close to: 180
+PASS - "left" property for "box4" element at 2.2s saw something close to: 20
+PASS - "left" property for "box5" element at 0.2s saw something close to: 40
+PASS - "left" property for "box6" element at 0.2s saw something close to: 40
+PASS - "left" property for "box7" element at 0.2s saw something close to: 180
+PASS - "left" property for "box8" element at 0.2s saw something close to: 180
+PASS - "left" property for "box5" element at 2.2s saw something close to: 40
+PASS - "left" property for "box6" element at 2.2s saw something close to: 180
+PASS - "left" property for "box7" element at 2.2s saw something close to: 180
+PASS - "left" property for "box8" element at 2.2s saw something close to: 40
+
diff --git a/LayoutTests/animations/animation-direction-reverse-non-hardware.html b/LayoutTests/animations/animation-direction-reverse-non-hardware.html
new file mode 100644 (file)
index 0000000..673c2e4
--- /dev/null
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test of -webkit-animation-direction reverse on non-composited elements</title>
+  <style>
+    body {
+      margin: 0;
+    }
+
+    .box {
+      position: relative;
+      left: 20px;
+      top: 10px;
+      height: 50px;
+      width: 250px;
+      margin-bottom: 10px;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: linear;
+      -webkit-animation-iteration-count: 8;
+    }
+    
+    .move1 {
+      -webkit-animation-name: move1;
+      background-color: blue;
+      color: white;
+    }
+
+    .move2 {
+      -webkit-animation-name: move2;
+      background-color: orange;
+    }
+
+    .normal {
+      -webkit-animation-direction: normal;
+    }
+
+    .alternate {
+      -webkit-animation-direction: alternate;
+    }
+
+    .reverse {
+      -webkit-animation-direction: reverse;
+    }
+
+    .alternate-reverse {
+      -webkit-animation-direction: alternate-reverse;
+    }
+    
+    @-webkit-keyframes move1 {
+      from { left: 0px; }
+      to { left: 200px; }
+    }
+
+    @-webkit-keyframes move2 {
+      0% { left: 0px; }
+      40% { left: 160px; }
+      60% { left: 120px; }
+      100% { left: 200px; }
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script>
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move1", 0.2, "box1", "left",  20, 20],
+      ["move1", 0.2, "box2", "left",  20, 20],
+      ["move1", 0.2, "box3", "left",  180, 20],
+      ["move1", 0.2, "box4", "left",  180, 20],
+      ["move1", 2.2, "box1", "left",  20, 20],
+      ["move1", 2.2, "box2", "left",  180, 20],
+      ["move1", 2.2, "box3", "left",  180, 20],
+      ["move1", 2.2, "box4", "left",  20, 20],
+      ["move2", 0.2, "box5", "left",  40, 20],
+      ["move2", 0.2, "box6", "left",  40, 20],
+      ["move2", 0.2, "box7", "left",  180, 20],
+      ["move2", 0.2, "box8", "left",  180, 20],
+      ["move2", 2.2, "box5", "left",  40, 20],
+      ["move2", 2.2, "box6", "left",  180, 20],
+      ["move2", 2.2, "box7", "left",  180, 20],
+      ["move2", 2.2, "box8", "left",  40, 20],
+    ];
+
+    runAnimationTest(expectedValues);
+
+  </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">2 keyframes: normal</div>
+<div id="box2" class="box move1 alternate">2 keyframes: alternate</div>
+<div id="box3" class="box move1 reverse">2 keyframes: reverse</div>
+<div id="box4" class="box move1 alternate-reverse">2 keyframes: alternate-reverse</div>
+<div id="box5" class="box move2 normal">4 keyframes: normal</div>
+<div id="box6" class="box move2 alternate">4 keyframes: alternate</div>
+<div id="box7" class="box move2 reverse">4 keyframes: reverse</div>
+<div id="box8" class="box move2 alternate-reverse">4 keyframes: alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/animation-direction-reverse-timing-functions-expected.txt b/LayoutTests/animations/animation-direction-reverse-timing-functions-expected.txt
new file mode 100644 (file)
index 0000000..fe4beed
--- /dev/null
@@ -0,0 +1,13 @@
+normal
+alternate
+reverse
+alternate-reverse
+PASS - "left" property for "box1" element at 0.2s saw something close to: 18
+PASS - "left" property for "box2" element at 0.2s saw something close to: 18
+PASS - "left" property for "box3" element at 0.2s saw something close to: 198
+PASS - "left" property for "box4" element at 0.2s saw something close to: 198
+PASS - "left" property for "box1" element at 2.2s saw something close to: 18
+PASS - "left" property for "box2" element at 2.2s saw something close to: 198
+PASS - "left" property for "box3" element at 2.2s saw something close to: 198
+PASS - "left" property for "box4" element at 2.2s saw something close to: 18
+
diff --git a/LayoutTests/animations/animation-direction-reverse-timing-functions-hardware-expected.txt b/LayoutTests/animations/animation-direction-reverse-timing-functions-hardware-expected.txt
new file mode 100644 (file)
index 0000000..6d09c09
--- /dev/null
@@ -0,0 +1,13 @@
+normal
+alternate
+reverse
+alternate-reverse
+PASS - "webkitTransform" property for "box1" element at 0.2s saw something close to: 1,0,0,1,18,0
+PASS - "webkitTransform" property for "box2" element at 0.2s saw something close to: 1,0,0,1,18,0
+PASS - "webkitTransform" property for "box3" element at 0.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box4" element at 0.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box1" element at 2.2s saw something close to: 1,0,0,1,18,0
+PASS - "webkitTransform" property for "box2" element at 2.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box3" element at 2.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box4" element at 2.2s saw something close to: 1,0,0,1,18,0
+
diff --git a/LayoutTests/animations/animation-direction-reverse-timing-functions-hardware.html b/LayoutTests/animations/animation-direction-reverse-timing-functions-hardware.html
new file mode 100644 (file)
index 0000000..bab61e0
--- /dev/null
@@ -0,0 +1,75 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <title>Test of -webkit-animation-direction timing functions on composited elements</title>
+  <style>
+    body {
+      margin: 0;
+    }
+
+    .box {
+      position: relative;
+      left: 20px;
+      top: 10px;
+      height: 50px;
+      width: 250px;
+      margin-bottom: 10px;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: ease; /* ease is good for testing because it is not symmetric */
+      -webkit-animation-iteration-count: 4;
+    }
+    
+    .move1 {
+      -webkit-animation-name: move1;
+      background-color: blue;
+      color: white;
+    }
+
+    .normal {
+      -webkit-animation-direction: normal;
+    }
+
+    .alternate {
+      -webkit-animation-direction: alternate;
+    }
+
+    .reverse {
+      -webkit-animation-direction: reverse;
+    }
+
+    .alternate-reverse {
+      -webkit-animation-direction: alternate-reverse;
+    }
+    
+    @-webkit-keyframes move1 {
+      from { -webkit-transform: translateX(0px); }
+      to { -webkit-transform: translateX(200px); }
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script>
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move1", 0.2, "box1", "webkitTransform",  [1,0,0,1, 18, 0], 10],
+      ["move1", 0.2, "box2", "webkitTransform",  [1,0,0,1, 18, 0], 10],
+      ["move1", 0.2, "box3", "webkitTransform",  [1,0,0,1, 198, 0], 10],
+      ["move1", 0.2, "box4", "webkitTransform",  [1,0,0,1, 198, 0], 10],
+      ["move1", 2.2, "box1", "webkitTransform",  [1,0,0,1, 18, 0], 10],
+      ["move1", 2.2, "box2", "webkitTransform",  [1,0,0,1, 198, 0], 10],
+      ["move1", 2.2, "box3", "webkitTransform",  [1,0,0,1, 198, 0], 10],
+      ["move1", 2.2, "box4", "webkitTransform",  [1,0,0,1, 18, 0], 10],
+    ];
+
+    runAnimationTest(expectedValues);
+
+  </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">normal</div>
+<div id="box2" class="box move1 alternate">alternate</div>
+<div id="box3" class="box move1 reverse">reverse</div>
+<div id="box4" class="box move1 alternate-reverse">alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/animations/animation-direction-reverse-timing-functions.html b/LayoutTests/animations/animation-direction-reverse-timing-functions.html
new file mode 100644 (file)
index 0000000..2b4c380
--- /dev/null
@@ -0,0 +1,75 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <title>Test of -webkit-animation-direction timing functions</title>
+  <style>
+    body {
+      margin: 0;
+    }
+
+    .box {
+      position: relative;
+      left: 20px;
+      top: 10px;
+      height: 50px;
+      width: 250px;
+      margin-bottom: 10px;
+      -webkit-animation-duration: 2s;
+      -webkit-animation-timing-function: ease; /* ease is good for testing because it is not symmetric */
+      -webkit-animation-iteration-count: 4;
+    }
+    
+    .move1 {
+      -webkit-animation-name: move1;
+      background-color: blue;
+      color: white;
+    }
+
+    .normal {
+      -webkit-animation-direction: normal;
+    }
+
+    .alternate {
+      -webkit-animation-direction: alternate;
+    }
+
+    .reverse {
+      -webkit-animation-direction: reverse;
+    }
+
+    .alternate-reverse {
+      -webkit-animation-direction: alternate-reverse;
+    }
+    
+    @-webkit-keyframes move1 {
+      from { left: 0px; }
+      to { left: 200px; }
+    }
+  </style>
+  <script src="resources/animation-test-helpers.js"></script>
+  <script>
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["move1", 0.2, "box1", "left",  18, 10],
+      ["move1", 0.2, "box2", "left",  18, 10],
+      ["move1", 0.2, "box3", "left",  198, 10],
+      ["move1", 0.2, "box4", "left",  198, 10],
+      ["move1", 2.2, "box1", "left",  18, 10],
+      ["move1", 2.2, "box2", "left",  198, 10],
+      ["move1", 2.2, "box3", "left",  198, 10],
+      ["move1", 2.2, "box4", "left",  18, 10],
+    ];
+
+    runAnimationTest(expectedValues);
+
+  </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">normal</div>
+<div id="box2" class="box move1 alternate">alternate</div>
+<div id="box3" class="box move1 reverse">reverse</div>
+<div id="box4" class="box move1 alternate-reverse">alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
index e8978e1..7a40a82 100644 (file)
@@ -166,144 +166,106 @@ function checkExpectedValue(expected, index)
     }
     
     var computedValue, computedValue2;
-    var pass = true;
-    if (!property.indexOf("webkitTransform")) {
-        var element;
-        if (iframeId)
-            element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+    if (compareElements) {
+        computedValue = getPropertyValue(property, elementId, iframeId);
+        computedValue2 = getPropertyValue(property, elementId2, iframeId);
+
+        if (comparePropertyValue(property, computedValue, computedValue2, tolerance))
+            result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + 
+                            "\" elements at " + time + "s are close enough to each other" + "<br>";
         else
-            element = document.getElementById(elementId);
-            
-        computedValue = window.getComputedStyle(element).webkitTransform;
-        if (compareElements) {
-            computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitTransform;
-            var m1 = matrixStringToArray(computedValue);
-            var m2 = matrixStringToArray(computedValue2);
-            
-            // for now we assume that both matrices are either both 2D or both 3D
-            var count = (computedValue.substring(0, 7) == "matrix3d") ? 16 : 6;
-            for (var i = 0; i < count; ++i) {
-                pass = isCloseEnough(parseFloat(m1[i]), m2[i], tolerance);
-                if (!pass)
-                    break;
-            }
-        } else {
-            if (typeof expectedValue == "string")
-                pass = (computedValue == expectedValue);
-            else if (typeof expectedValue == "number") {
-                var m = matrixStringToArray(computedValue);
-                pass = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
-            } else {
-                var m = matrixStringToArray(computedValue);
-                for (i = 0; i < expectedValue.length; ++i) {
-                    pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
-                    if (!pass)
-                        break;
-                }
-            }
-        }
-    } else if (property == "webkitFilter") {
-        var element;
+            result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + 
+                            "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 + 
+                                            "\" which are not close enough to each other" + "<br>";
+    } else {
+        var elementName;
         if (iframeId)
-            element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+            elementName = iframeId + '.' + elementId;
         else
-            element = document.getElementById(elementId);
-        computedValue = window.getComputedStyle(element).webkitFilter;
-        var filterParameters = getFilterParameters(computedValue);
+            elementName = elementId;
 
-        if (compareElements) {
-            computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitFilter;
-            var filter2Parameters = getFilterParameters(computedValue2);
-            pass = filterParametersMatch(filterParameters, filter2Parameters, tolerance);
-        } else {
-            pass = filterParametersMatch(filterParameters, getFilterParameters(expectedValue), tolerance);
-        }
-    } else if (property == "lineHeight") {
-        var element;
-        if (iframeId)
-            element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+        computedValue = getPropertyValue(property, elementId, iframeId);
+
+        if (comparePropertyValue(property, computedValue, expectedValue, tolerance))
+            result += "PASS - \"" + property + "\" property for \"" + elementName + "\" element at " + time + 
+                            "s saw something close to: " + expectedValue + "<br>";
         else
-            element = document.getElementById(elementId);
-            
+            result += "FAIL - \"" + property + "\" property for \"" + elementName + "\" element at " + time + 
+                            "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
+    }
+}
+
+
+function getPropertyValue(property, elementId, iframeId)
+{
+    var computedValue;
+    var element;
+    if (iframeId)
+        element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+    else
+        element = document.getElementById(elementId);
+
+    if (property == "lineHeight")
         computedValue = parseInt(window.getComputedStyle(element).lineHeight);
-        if (compareElements) {
-            computedValue2 = parseInt(window.getComputedStyle(document.getElementById(elementId2)).lineHeight);
-            pass = isCloseEnough(computedValue, computedValue2, tolerance);
+    else if (property == "backgroundImage"
+               || property == "borderImageSource"
+               || property == "listStyleImage"
+               || property == "webkitMaskImage"
+               || property == "webkitMaskBoxImage"
+               || property == "webkitFilter"
+               || !property.indexOf("webkitTransform")) {
+        computedValue = window.getComputedStyle(element)[property.split(".")[0]];
+    } else {
+        var computedStyle = window.getComputedStyle(element).getPropertyCSSValue(property);
+        computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+    }
+
+    return computedValue;
+}
+
+function comparePropertyValue(property, computedValue, expectedValue, tolerance)
+{
+    var result = true;
+
+    if (!property.indexOf("webkitTransform")) {
+        if (typeof expectedValue == "string")
+            result = (computedValue == expectedValue);
+        else if (typeof expectedValue == "number") {
+            var m = matrixStringToArray(computedValue);
+            result = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
+        } else {
+            var m = matrixStringToArray(computedValue);
+            for (i = 0; i < expectedValue.length; ++i) {
+                result = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
+                if (!result)
+                    break;
+            }
         }
-        else
-            pass = isCloseEnough(computedValue, expectedValue, tolerance);
+    } else if (property == "webkitFilter") {
+        var filterParameters = getFilterParameters(computedValue);
+        var filter2Parameters = getFilterParameters(expectedValue);
+        result = filterParametersMatch(filterParameters, filter2Parameters, tolerance);
     } else if (property == "backgroundImage"
                || property == "borderImageSource"
                || property == "listStyleImage"
                || property == "webkitMaskImage"
                || property == "webkitMaskBoxImage") {
-        var element;
-        if (iframeId)
-            element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
-        else
-            element = document.getElementById(elementId);
-
-        computedValue = window.getComputedStyle(element)[property];
-        computedCrossFade = parseCrossFade(computedValue);
+        var computedCrossFade = parseCrossFade(computedValue);
 
         if (!computedCrossFade) {
-            pass = false;
+            result = false;
         } else {
-            if (compareElements) {
-                computedValue2 = window.getComputedStyle(document.getElementById(elementId2))[property];
-                computedCrossFade2 = parseCrossFade(computedValue2);
-                if (computedCrossFade2) {
-                    pass = (isCloseEnough(computedCrossFade.percent, computedCrossFade2.percent, tolerance) &&
-                        computedCrossFade.from == computedCrossFade2.from &&
-                        computedCrossFade.to == computedCrossFade2.to);
-                }
-                else {
-                    pass = false;
-                }
+            if (typeof expectedValue == "string") {
+                var computedCrossFade2 = parseCrossFade(expectedValue);
+                result = isCloseEnough(computedCrossFade.percent, computedCrossFade2.percent, tolerance) && computedCrossFade.from == computedCrossFade2.from && computedCrossFade.to == computedCrossFade2.to;
             } else {
-                pass = isCloseEnough(computedCrossFade.percent, expectedValue, tolerance);
+                result = isCloseEnough(computedCrossFade.percent, expectedValue, tolerance)
             }
         }
     } else {
-        var element;
-        if (iframeId)
-            element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
-        else
-            element = document.getElementById(elementId);
-
-        var computedStyle = window.getComputedStyle(element).getPropertyCSSValue(property);
-        computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
-        if (compareElements) {
-            var computedStyle2 = window.getComputedStyle(document.getElementById(elementId2)).getPropertyCSSValue(property);
-            computedValue2 = computedStyle2.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
-            pass = isCloseEnough(computedValue, computedValue2, tolerance);
-        }
-        else
-            pass = isCloseEnough(computedValue, expectedValue, tolerance);
-    }
-
-    if (compareElements) {
-        if (pass)
-            result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + 
-                            "\" elements at " + time + "s are close enough to each other" + "<br>";
-        else
-            result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + 
-                            "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 + 
-                                            "\" which are not close enough to each other" + "<br>";
-    } else {
-        var elementName;
-        if (iframeId)
-            elementName = iframeId + '.' + elementId;
-        else
-            elementName = elementId;
-        if (pass)
-            result += "PASS - \"" + property + "\" property for \"" + elementName + "\" element at " + time + 
-                            "s saw something close to: " + expectedValue + "<br>";
-        else
-            result += "FAIL - \"" + property + "\" property for \"" + elementName + "\" element at " + time + 
-                            "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
+        result = isCloseEnough(computedValue, expectedValue, tolerance);
     }
+    return result;
 }
 
 function endTest()
index 444bbca..4131e8b 100644 (file)
@@ -1,3 +1,55 @@
+2012-04-27  Dean Jackson  <dino@apple.com>
+
+        Support reverse and alternate-reverse in CA animations
+        https://bugs.webkit.org/show_bug.cgi?id=78041
+
+        Reviewed by Beth Dakin.
+
+        CoreAnimation does not natively support reverse and alternate-reverse
+        animation directions so we need to flip the animation values (keyframe
+        keys and timing functions) that we send to GraphicsLayerCA. Unfortunately
+        this code adds a lot of conditionals because it isn't as simple as
+        reversing the order of keys. You also now have a different alignment of
+        timing functions to the reversed list.
+
+        New tests to cover the two new directions, making sure the timing
+        functions are correctly inverted, and exercising fill modes.
+
+        Tests: animations/animation-direction-reverse-fill-mode-hardware.html
+               animations/animation-direction-reverse-fill-mode.html
+               animations/animation-direction-reverse-hardware-opacity.html
+               animations/animation-direction-reverse-hardware.html
+               animations/animation-direction-reverse-non-hardware.html
+               animations/animation-direction-reverse-timing-functions-hardware.html
+               animations/animation-direction-reverse-timing-functions.html
+
+        * platform/graphics/ca/GraphicsLayerCA.cpp:
+          Handle the previously unsupported animation directions, reversing
+          the list of values and keytimes that would be used to create
+          the CA Animation.
+        (WebCore::GraphicsLayerCA::addAnimation):
+          Do not create an animation if on Windows and using a reverse
+          direction.
+        (WebCore::GraphicsLayerCA::createFilterAnimationsFromKeyframes):
+        (WebCore::GraphicsLayerCA::setupAnimation):
+        (WebCore::GraphicsLayerCA::setAnimationEndpoints):
+        (WebCore::GraphicsLayerCA::setAnimationKeyframes):
+        (WebCore::GraphicsLayerCA::setTransformAnimationEndpoints):
+        (WebCore::GraphicsLayerCA::setTransformAnimationKeyframes):
+        (WebCore::GraphicsLayerCA::setFilterAnimationEndpoints):
+        (WebCore::GraphicsLayerCA::setFilterAnimationKeyframes):
+        * platform/graphics/ca/PlatformCAAnimation.h:
+        (PlatformCAAnimation): Pass through a flag that tells the CA Animation
+        that it should invert the timing functions.
+        * platform/graphics/ca/mac/PlatformCAAnimationMac.mm:
+        (toCAMediaTimingFunction): Add a parameter that will invert the timing
+        function coefficients if necessary.
+        (PlatformCAAnimation::setTimingFunction):
+        (PlatformCAAnimation::setTimingFunctions):
+        * platform/graphics/ca/win/PlatformCAAnimationWin.cpp:
+        (toCACFTimingFunction):
+          New unused parameter.
+
 2012-04-27  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r115407.
index f84330f..b5da990 100644 (file)
@@ -107,6 +107,7 @@ public:
         AnimationDirectionAlternateReverse
     };
     AnimationDirection direction() const { return static_cast<AnimationDirection>(m_direction); }
+    bool directionIsForwards() const { return m_direction == AnimationDirectionNormal || m_direction == AnimationDirectionAlternate; }
 
     unsigned fillMode() const { return m_fillMode; }
 
index 1d9b4c1..6d89f54 100644 (file)
@@ -684,6 +684,14 @@ bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const Int
     if (animationHasStepsTimingFunction(valueList, anim))
         return false;
 
+#if PLATFORM(WIN)
+    // CoreAnimation on Windows does not handle a reverse direction. Fall
+    // back to software animation in that case.
+    // https://bugs.webkit.org/show_bug.cgi?id=85121
+    if (!anim->directionIsForwards())
+        return false;
+#endif
+
     bool createdAnimations = false;
     if (valueList.property() == AnimatedPropertyWebkitTransform)
         createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, animationName, timeOffset, boxSize);
@@ -1950,8 +1958,7 @@ bool GraphicsLayerCA::createFilterAnimationsFromKeyframes(const KeyframeValueLis
     const FilterOperations* operations = static_cast<const FilterAnimationValue*>(valueList.at(listIndex))->value();
     int numAnimations = operations->size();
 
-    // FIXME: We can't currently hardware animate shadows. There is an open question about removing shadows from filters 
-    // entirely, in which case this issue is moot.
+    // FIXME: We can't currently hardware animate shadows.
     for (int i = 0; i < numAnimations; ++i) {
         if (operations->at(i)->getOperationType() == FilterOperation::DROP_SHADOW)
             return false;
@@ -1989,7 +1996,7 @@ void GraphicsLayerCA::setupAnimation(PlatformCAAnimation* propertyAnim, const An
     float repeatCount = anim->iterationCount();
     if (repeatCount == Animation::IterationCountInfinite)
         repeatCount = numeric_limits<float>::max();
-    else if (anim->direction() == Animation::AnimationDirectionAlternate)
+    else if (anim->direction() == Animation::AnimationDirectionAlternate || anim->direction() == Animation::AnimationDirectionAlternateReverse)
         repeatCount /= 2;
 
     PlatformCAAnimation::FillModeType fillMode = PlatformCAAnimation::NoFillMode;
@@ -2010,7 +2017,7 @@ void GraphicsLayerCA::setupAnimation(PlatformCAAnimation* propertyAnim, const An
 
     propertyAnim->setDuration(duration);
     propertyAnim->setRepeatCount(repeatCount);
-    propertyAnim->setAutoreverses(anim->direction());
+    propertyAnim->setAutoreverses(anim->direction() == Animation::AnimationDirectionAlternate || anim->direction() == Animation::AnimationDirectionAlternateReverse);
     propertyAnim->setRemovedOnCompletion(false);
     propertyAnim->setAdditive(additive);
     propertyAnim->setFillMode(fillMode);
@@ -2026,12 +2033,17 @@ const TimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const Ani
     return CubicBezierTimingFunction::defaultTimingFunction();
 }
 
-bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* basicAnim)
+bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* basicAnim)
 {
+    bool forwards = animation->directionIsForwards();
+
+    unsigned fromIndex = !forwards;
+    unsigned toIndex = forwards;
+    
     switch (valueList.property()) {
     case AnimatedPropertyOpacity: {
-        basicAnim->setFromValue(static_cast<const FloatAnimationValue*>(valueList.at(0))->value());
-        basicAnim->setToValue(static_cast<const FloatAnimationValue*>(valueList.at(1))->value());
+        basicAnim->setFromValue(static_cast<const FloatAnimationValue*>(valueList.at(fromIndex))->value());
+        basicAnim->setToValue(static_cast<const FloatAnimationValue*>(valueList.at(toIndex))->value());
         break;
     }
     default:
@@ -2040,23 +2052,26 @@ bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList,
     }
 
     // This codepath is used for 2-keyframe animations, so we still need to look in the start
-    // for a timing function.
-    const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim);
+    // for a timing function. Even in the reversing animation case, the first keyframe provides the timing function.
+    const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), animation);
     if (timingFunction)
-        basicAnim->setTimingFunction(timingFunction);
+        basicAnim->setTimingFunction(timingFunction, !forwards);
 
     return true;
 }
 
-bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* keyframeAnim)
+bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* keyframeAnim)
 {
     Vector<float> keyTimes;
     Vector<float> values;
     Vector<const TimingFunction*> timingFunctions;
+
+    bool forwards = animation->directionIsForwards();
     
     for (unsigned i = 0; i < valueList.size(); ++i) {
-        const AnimationValue* curValue = valueList.at(i);
-        keyTimes.append(curValue->keyTime());
+        unsigned index = forwards ? i : (valueList.size() - i - 1);
+        const AnimationValue* curValue = valueList.at(index);
+        keyTimes.append(forwards ? curValue->keyTime() : (1 - curValue->keyTime()));
 
         switch (valueList.property()) {
         case AnimatedPropertyOpacity: {
@@ -2069,24 +2084,28 @@ bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList,
             break;
         }
 
-        timingFunctions.append(timingFunctionForAnimationValue(curValue, anim));
+        if (i < (valueList.size() - 1))
+            timingFunctions.append(timingFunctionForAnimationValue(forwards ? curValue : valueList.at(index - 1), animation));
     }
     
-    // We toss the last tfArray value because it has to one shorter than the others.
-    timingFunctions.removeLast();
-
     keyframeAnim->setKeyTimes(keyTimes);
     keyframeAnim->setValues(values);
-    keyframeAnim->setTimingFunctions(timingFunctions);
+    keyframeAnim->setTimingFunctions(timingFunctions, !forwards);
     
     return true;
 }
 
-bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize)
+bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize)
 {
     ASSERT(valueList.size() == 2);
-    const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(0));
-    const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(1));
+
+    bool forwards = animation->directionIsForwards();
+    
+    unsigned fromIndex = !forwards;
+    unsigned toIndex = forwards;
+    
+    const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(fromIndex));
+    const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(toIndex));
     
     if (isMatrixAnimation) {
         TransformationMatrix fromTransform, toTransform;
@@ -2128,9 +2147,9 @@ bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& va
     }
 
     // This codepath is used for 2-keyframe animations, so we still need to look in the start
-    // for a timing function.
-    const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim);
-    basicAnim->setTimingFunction(timingFunction);
+    // for a timing function. Even in the reversing animation case, the first keyframe provides the timing function.
+    const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), animation);
+    basicAnim->setTimingFunction(timingFunction, !forwards);
 
     PlatformCAAnimation::ValueFunctionType valueFunction = getValueFunctionNameForTransformOperation(transformOpType);
     if (valueFunction != PlatformCAAnimation::NoValueFunction)
@@ -2147,9 +2166,12 @@ bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& va
     Vector<TransformationMatrix> transformationMatrixValues;
     Vector<const TimingFunction*> timingFunctions;
 
+    bool forwards = animation->directionIsForwards();
+
     for (unsigned i = 0; i < valueList.size(); ++i) {
-        const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(i));
-        keyTimes.append(curValue->keyTime());
+        unsigned index = forwards ? i : (valueList.size() - i - 1);
+        const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(index));
+        keyTimes.append(forwards ? curValue->keyTime() : (1 - curValue->keyTime()));
 
         if (isMatrixAnimation) {
             TransformationMatrix transform;
@@ -2177,13 +2199,10 @@ bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& va
             }
         }
 
-        const TimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation);
-        timingFunctions.append(timingFunction);
+        if (i < (valueList.size() - 1))
+            timingFunctions.append(timingFunctionForAnimationValue(forwards ? curValue : valueList.at(index - 1), animation));
     }
     
-    // We toss the last tfArray value because it has to one shorter than the others.
-    timingFunctions.removeLast();
-
     keyframeAnim->setKeyTimes(keyTimes);
     
     if (isTransformTypeNumber(transformOpType))
@@ -2193,7 +2212,7 @@ bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& va
     else
         keyframeAnim->setValues(transformationMatrixValues);
         
-    keyframeAnim->setTimingFunctions(timingFunctions);
+    keyframeAnim->setTimingFunctions(timingFunctions, !forwards);
 
     PlatformCAAnimation::ValueFunctionType valueFunction = getValueFunctionNameForTransformOperation(transformOpType);
     if (valueFunction != PlatformCAAnimation::NoValueFunction)
@@ -2206,34 +2225,39 @@ bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& va
 bool GraphicsLayerCA::setFilterAnimationEndpoints(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* basicAnim, int functionIndex, int internalFilterPropertyIndex)
 {
     ASSERT(valueList.size() == 2);
-    
-    const FilterAnimationValue* fromValue = static_cast<const FilterAnimationValue*>(valueList.at(0));
-    const FilterAnimationValue* toValue = static_cast<const FilterAnimationValue*>(valueList.at(1));
-    
+
+    bool forwards = animation->directionIsForwards();
+
+    unsigned fromIndex = !forwards;
+    unsigned toIndex = forwards;
+
+    const FilterAnimationValue* fromValue = static_cast<const FilterAnimationValue*>(valueList.at(fromIndex));
+    const FilterAnimationValue* toValue = static_cast<const FilterAnimationValue*>(valueList.at(toIndex));
+
     const FilterOperation* fromOperation = fromValue->value()->at(functionIndex);
     const FilterOperation* toOperation = toValue->value()->at(functionIndex);
-    
+
     RefPtr<DefaultFilterOperation> defaultFromOperation;
     RefPtr<DefaultFilterOperation> defaultToOperation;
-    
+
     ASSERT(fromOperation || toOperation);
-    
+
     if (!fromOperation) {
         defaultFromOperation = DefaultFilterOperation::create(toOperation->getOperationType());
         fromOperation = defaultFromOperation.get();
     }
-    
+
     if (!toOperation) {
         defaultToOperation = DefaultFilterOperation::create(fromOperation->getOperationType());
         toOperation = defaultToOperation.get();
     }
-    
+
     basicAnim->setFromValue(fromOperation, internalFilterPropertyIndex);
     basicAnim->setToValue(toOperation, internalFilterPropertyIndex);
-    
-    // This codepath is used for 2-keyframe animations, so we still need to look in the start for a timing function.
-    const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), animation);
-    basicAnim->setTimingFunction(timingFunction);
+
+    // This codepath is used for 2-keyframe animations, so we still need to look in the start
+    // for a timing function. Even in the reversing animation case, the first keyframe provides the timing function.
+    basicAnim->setTimingFunction(timingFunctionForAnimationValue(valueList.at(0), animation), !forwards);
 
     return true;
 }
@@ -2245,9 +2269,12 @@ bool GraphicsLayerCA::setFilterAnimationKeyframes(const KeyframeValueList& value
     Vector<const TimingFunction*> timingFunctions;
     RefPtr<DefaultFilterOperation> defaultOperation;
 
+    bool forwards = animation->directionIsForwards();
+
     for (unsigned i = 0; i < valueList.size(); ++i) {
-        const FilterAnimationValue* curValue = static_cast<const FilterAnimationValue*>(valueList.at(i));
-        keyTimes.append(curValue->keyTime());
+        unsigned index = forwards ? i : (valueList.size() - i - 1);
+        const FilterAnimationValue* curValue = static_cast<const FilterAnimationValue*>(valueList.at(index));
+        keyTimes.append(forwards ? curValue->keyTime() : (1 - curValue->keyTime()));
 
         if (curValue->value()->operations().size() > static_cast<size_t>(functionIndex))
             values.append(curValue->value()->operations()[functionIndex]);
@@ -2257,16 +2284,13 @@ bool GraphicsLayerCA::setFilterAnimationKeyframes(const KeyframeValueList& value
             values.append(defaultOperation);
         }
 
-        const TimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation);
-        timingFunctions.append(timingFunction);
+        if (i < (valueList.size() - 1))
+            timingFunctions.append(timingFunctionForAnimationValue(forwards ? curValue : valueList.at(index - 1), animation));
     }
     
-    // We toss the last timing function because it has to be one shorter than the others.
-    timingFunctions.removeLast();
-
     keyframeAnim->setKeyTimes(keyTimes);
     keyframeAnim->setValues(values, internalFilterPropertyIndex);
-    keyframeAnim->setTimingFunctions(timingFunctions);
+    keyframeAnim->setTimingFunctions(timingFunctions, !forwards);
 
     return true;
 }
index 280d763..6c12631 100644 (file)
@@ -93,7 +93,7 @@ public:
     FillModeType fillMode() const;
     void setFillMode(FillModeType);
     
-    void setTimingFunction(const TimingFunction*);
+    void setTimingFunction(const TimingFunction*, bool reverse = false);
     void copyTimingFunctionFrom(const PlatformCAAnimation*);
 
     bool isRemovedOnCompletion() const;
@@ -137,7 +137,7 @@ public:
     void setKeyTimes(const Vector<float>&);
     void copyKeyTimesFrom(const PlatformCAAnimation*);
 
-    void setTimingFunctions(const Vector<const TimingFunction*>&);
+    void setTimingFunctions(const Vector<const TimingFunction*>&, bool reverse = false);
     void copyTimingFunctionsFrom(const PlatformCAAnimation*);
     
 #if ENABLE(CSS_FILTERS)
index 5cf1006..cd85669 100644 (file)
@@ -134,13 +134,19 @@ static PlatformCAAnimation::ValueFunctionType fromCAValueFunctionType(NSString*
 }
 #endif
 
-static CAMediaTimingFunction* toCAMediaTimingFunction(const TimingFunction* timingFunction)
+static CAMediaTimingFunction* toCAMediaTimingFunction(const TimingFunction* timingFunction, bool reverse)
 {
     ASSERT(timingFunction);
     if (timingFunction->isCubicBezierTimingFunction()) {
         const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
-        return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(ctf->x1()) :static_cast<float>(ctf->y1())
-                                                               :static_cast<float>(ctf->x2()) :static_cast<float>(ctf->y2())];
+        float x1 = static_cast<float>(ctf->x1());
+        float y1 = static_cast<float>(ctf->y1());
+        float x2 = static_cast<float>(ctf->x2());
+        float y2 = static_cast<float>(ctf->y2());
+        return [CAMediaTimingFunction functionWithControlPoints:(reverse ? 1 - x2 : x1)
+                                                               :(reverse ? 1 - y2 : y1)
+                                                               :(reverse ? 1 - x1 : x2)
+                                                               :(reverse ? 1 - y1 : y2)];
     }
     
     return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
@@ -306,9 +312,9 @@ void PlatformCAAnimation::setFillMode(FillModeType value)
     [m_animation.get() setFillMode:toCAFillModeType(value)];
 }
 
-void PlatformCAAnimation::setTimingFunction(const TimingFunction* value)
+void PlatformCAAnimation::setTimingFunction(const TimingFunction* value, bool reverse)
 {
-    [m_animation.get() setTimingFunction:toCAMediaTimingFunction(value)];
+    [m_animation.get() setTimingFunction:toCAMediaTimingFunction(value, reverse)];
 }
 
 void PlatformCAAnimation::copyTimingFunctionFrom(const PlatformCAAnimation* value)
@@ -694,12 +700,12 @@ void PlatformCAAnimation::copyKeyTimesFrom(const PlatformCAAnimation* value)
     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:[other keyTimes]];
 }
 
-void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value)
+void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
 {
     NSMutableArray* array = [NSMutableArray array];
 
     for (size_t i = 0; i < value.size(); ++i)
-        [array addObject:toCAMediaTimingFunction(value[i])];
+        [array addObject:toCAMediaTimingFunction(value[i], reverse)];
     
     [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:array];
 }
index 944288c..4a71fe5 100644 (file)
@@ -288,8 +288,9 @@ void PlatformCAAnimation::setFillMode(FillModeType value)
     CACFAnimationSetFillMode(m_animation.get(), toCACFFillModeType(value));
 }
 
-void PlatformCAAnimation::setTimingFunction(const TimingFunction* value)
+void PlatformCAAnimation::setTimingFunction(const TimingFunction* value, bool reverse)
 {
+    UNUSED_PARAM(reverse);
     CACFAnimationSetTimingFunction(m_animation.get(), toCACFTimingFunction(value).get());
 }
 
@@ -533,8 +534,9 @@ void PlatformCAAnimation::copyKeyTimesFrom(const PlatformCAAnimation* value)
     CACFAnimationSetKeyTimes(m_animation.get(), CACFAnimationGetKeyTimes(value->platformAnimation()));
 }
 
-void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value)
+void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
 {
+    UNUSED_PARAM(reverse);
     if (animationType() != Keyframe)
         return;