2010-09-08 Dean Jackson <dino@apple.com>
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Sep 2010 23:06:19 +0000 (23:06 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Sep 2010 23:06:19 +0000 (23:06 +0000)
        Reviewed by Simon Fraser.

        Implement steps() timing function for animations
        https://bugs.webkit.org/show_bug.cgi?id=44541

        Tests: animations/timing-functions.html
               transitions/steps-timing-function.html

        * css/CSSComputedStyleDeclaration.cpp:
        (WebCore::getTimingFunctionValue):
            - when creating computed style we now test what
              type of timing function it being used
        * css/CSSParser.cpp:
        (WebCore::CSSParser::parseCubicBezierTimingFunctionValue):
            - rename this method from parseTimingFunctionValue
        (WebCore::CSSParser::parseAnimationTimingFunction):
            - support parsing the steps() function
        * css/CSSParser.h:
            - method rename
        * css/CSSStyleSelector.cpp:
        (WebCore::CSSStyleSelector::mapAnimationTimingFunction):
            - handle 'step-start' and 'step-end' identifiers. Also
              now use ::create when constructing objects
        * css/CSSTimingFunctionValue.cpp:
        (WebCore::CSSLinearTimingFunctionValue::cssText):
        (WebCore::CSSCubicBezierTimingFunctionValue::cssText):
        (WebCore::CSSStepsTimingFunctionValue::cssText):
            - new text output for computed style. We now produce
              the text 'linear' when appropriate.
        * css/CSSTimingFunctionValue.h:
        (WebCore::CSSTimingFunctionValue::isLinearTimingFunctionValue):
        (WebCore::CSSTimingFunctionValue::isCubicBezierTimingFunctionValue):
        (WebCore::CSSTimingFunctionValue::isStepsTimingFunctionValue):
        (WebCore::CSSTimingFunctionValue::CSSTimingFunctionValue):
        (WebCore::CSSTimingFunctionValue::isTimingFunctionValue):
        (WebCore::CSSLinearTimingFunctionValue::create):
        (WebCore::CSSLinearTimingFunctionValue::isLinearTimingFunctionValue):
        (WebCore::CSSLinearTimingFunctionValue::CSSLinearTimingFunctionValue):
        (WebCore::CSSCubicBezierTimingFunctionValue::create):
        (WebCore::CSSCubicBezierTimingFunctionValue::isCubicBezierTimingFunctionValue):
        (WebCore::CSSCubicBezierTimingFunctionValue::CSSCubicBezierTimingFunctionValue):
        (WebCore::CSSStepsTimingFunctionValue::create):
        (WebCore::CSSStepsTimingFunctionValue::numberOfSteps):
        (WebCore::CSSStepsTimingFunctionValue::stepAtStart):
        (WebCore::CSSStepsTimingFunctionValue::isStepsTimingFunctionValue):
        (WebCore::CSSStepsTimingFunctionValue::CSSStepsTimingFunctionValue):
            - CSSTimingFunction is now a pure virtual ref-counted base class, with
              subclasses for each of the three supported timing functions.
        * css/CSSValueKeywords.in:
            - new keywords step-start and step-end
        * page/animation/AnimationBase.cpp:
        (WebCore::solveStepsFunction):
            - produces the output value from a stepping function
        (WebCore::AnimationBase::progress):
            - now has to switch based on timing function type
        * page/animation/KeyframeAnimation.cpp:
        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty):
            - use ref-counted access
        * platform/animation/Animation.cpp:
        (WebCore::Animation::animationsMatch):
            - change timing function comparison for operator==
        * platform/animation/Animation.h:
        (WebCore::Animation::timingFunction):
        (WebCore::Animation::setTimingFunction):
        (WebCore::Animation::initialAnimationTimingFunction):
            - move to ref-counted timing function class
        * platform/animation/TimingFunction.h:
        (WebCore::TimingFunction::~TimingFunction):
        (WebCore::TimingFunction::isLinearTimingFunction):
        (WebCore::TimingFunction::isCubicBezierTimingFunction):
        (WebCore::TimingFunction::isStepsTimingFunction):
        (WebCore::TimingFunction::TimingFunction):
        (WebCore::LinearTimingFunction::create):
        (WebCore::LinearTimingFunction::~LinearTimingFunction):
        (WebCore::LinearTimingFunction::operator==):
        (WebCore::LinearTimingFunction::LinearTimingFunction):
        (WebCore::CubicBezierTimingFunction::create):
        (WebCore::CubicBezierTimingFunction::~CubicBezierTimingFunction):
        (WebCore::CubicBezierTimingFunction::operator==):
        (WebCore::CubicBezierTimingFunction::CubicBezierTimingFunction):
        (WebCore::StepsTimingFunction::create):
        (WebCore::StepsTimingFunction::~StepsTimingFunction):
        (WebCore::StepsTimingFunction::operator==):
        (WebCore::StepsTimingFunction::numberOfSteps):
        (WebCore::StepsTimingFunction::stepAtStart):
        (WebCore::StepsTimingFunction::StepsTimingFunction):
            - TimingFunction is now a ref-counted pure virtual base class,
              with three subclasses representing the types of timing functions
              that are supported.
        * platform/graphics/GraphicsLayer.h:
        (WebCore::AnimationValue::AnimationValue):
        (WebCore::FloatAnimationValue::FloatAnimationValue):
        (WebCore::TransformAnimationValue::TransformAnimationValue):
            - use PassRefPtr in function parameters
        * platform/graphics/qt/GraphicsLayerQt.cpp:
        (WebCore::solveStepsFunction):
        (WebCore::applyTimingFunction):
        (WebCore::AnimationQt::AnimationQt):
        (WebCore::AnimationQt::updateCurrentTime):
            - implement the timing function switch for QT
        * platform/graphics/mac/GraphicsLayerCA.mm:
        (WebCore::getCAMediaTimingFunction):
            - update for new timing function interface
        (WebCore::animationHasStepsTimingFunction):
            - new method to make sure animations with steps() functions
              never try to execute in Core Animation
        (WebCore::GraphicsLayerCA::addAnimation):
            - test for steps() timing function
        (WebCore::GraphicsLayerCA::timingFunctionForAnimationValue):
        * rendering/style/RenderStyleConstants.h:
            - remove old RenderStyle enum for timing function types

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

29 files changed:
LayoutTests/ChangeLog
LayoutTests/animations/animation-shorthand.html
LayoutTests/animations/computed-style-expected.txt
LayoutTests/animations/computed-style.html
LayoutTests/animations/fill-unset-properties.html
LayoutTests/animations/timing-functions-expected.txt [new file with mode: 0644]
LayoutTests/animations/timing-functions.html [new file with mode: 0644]
LayoutTests/transitions/inherit-other-props-expected.txt
LayoutTests/transitions/inherit-other-props.html
LayoutTests/transitions/steps-timing-function-expected.txt [new file with mode: 0644]
LayoutTests/transitions/steps-timing-function.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/css/CSSComputedStyleDeclaration.cpp
WebCore/css/CSSParser.cpp
WebCore/css/CSSParser.h
WebCore/css/CSSStyleSelector.cpp
WebCore/css/CSSTimingFunctionValue.cpp
WebCore/css/CSSTimingFunctionValue.h
WebCore/css/CSSValueKeywords.in
WebCore/page/animation/AnimationBase.cpp
WebCore/page/animation/KeyframeAnimation.cpp
WebCore/platform/animation/Animation.cpp
WebCore/platform/animation/Animation.h
WebCore/platform/animation/TimingFunction.h
WebCore/platform/graphics/GraphicsLayer.h
WebCore/platform/graphics/mac/GraphicsLayerCA.mm
WebCore/platform/graphics/qt/GraphicsLayerQt.cpp
WebCore/rendering/RenderLayerBacking.cpp
WebCore/rendering/style/RenderStyleConstants.h

index 274173a..03558cd 100644 (file)
@@ -1,3 +1,26 @@
+2010-09-08  Dean Jackson  <dino@apple.com>
+
+        Reviewed by Simon Fraser.
+
+        Implement steps() timing function for animations
+        https://bugs.webkit.org/show_bug.cgi?id=44541
+
+        * transitions/steps-timing-function-expected.txt: Added.
+        * transitions/steps-timing-function.html: Added.
+        * animations/timing-functions-expected.txt: Added.
+        * animations/timing-functions.html: Added.
+            - New tests that exercise steps in animations
+              and transitions, in software and hardware
+
+        * animations/computed-style-expected.txt:
+        * animations/computed-style.html:
+        * animations/animation-shorthand.html:
+        * animations/fill-unset-properties.html:
+        * transitions/inherit-other-props-expected.txt:
+        * transitions/inherit-other-props.html:
+            - Change existing tests to use 'linear' computed
+              style where necessary
+
 2010-09-08  Adam Barth  <abarth@webkit.org>
 
         Reviewed by Eric Seidel.
index adf3719..e8fbdf8 100644 (file)
       { id: 'a',  values: [ "none", "0s", "cubic-bezier(0.25, 0.1, 0.25, 1)", "0s", "1", "normal", "none" ] },
       { id: 'b',  values: [ "none", "0s", "cubic-bezier(0.25, 0.1, 0.25, 1)", "0s", "1", "normal", "none" ] },
       { id: 'c',  values: [ "anim1", "10s", "cubic-bezier(0.25, 0.1, 0.25, 1)", "0s", "1", "normal", "none" ] },
-      { id: 'd',  values: [ "anim1", "10s", "cubic-bezier(0, 0, 1, 1)", "0s", "1", "normal", "none" ] },
-      { id: 'e',  values: [ "anim1", "10s", "cubic-bezier(0, 0, 1, 1)", "5s", "1", "normal", "none" ] },
-      { id: 'f',  values: [ "anim1", "10s", "cubic-bezier(0, 0, 1, 1)", "5s", "3", "normal", "none" ] },
-      { id: 'g',  values: [ "anim1", "10s", "cubic-bezier(0, 0, 1, 1)", "5s", "infinite", "alternate", "none" ] },
-      { id: 'h',  values: [ "anim1", "10s", "cubic-bezier(0, 0, 1, 1)", "5s", "infinite", "alternate", "forwards" ] },
-      { id: 'i',  values: [ "anim1", "10s", "cubic-bezier(0, 0, 1, 1)", "0s", "1", "normal", "none" ] },
-      { id: 'j',  values: [ "anim1, anim2, anim3", "10s, 3s, 5s", "cubic-bezier(0, 0, 1, 1), cubic-bezier(0.25, 0.1, 0.25, 1), cubic-bezier(0.25, 0.1, 0.25, 1)", "0s, 0s, 0s", "infinite, 1, 1", "normal, normal, normal", "backwards, none, both" ] }
+      { id: 'd',  values: [ "anim1", "10s", "linear", "0s", "1", "normal", "none" ] },
+      { id: 'e',  values: [ "anim1", "10s", "linear", "5s", "1", "normal", "none" ] },
+      { id: 'f',  values: [ "anim1", "10s", "linear", "5s", "3", "normal", "none" ] },
+      { id: 'g',  values: [ "anim1", "10s", "linear", "5s", "infinite", "alternate", "none" ] },
+      { id: 'h',  values: [ "anim1", "10s", "linear", "5s", "infinite", "alternate", "forwards" ] },
+      { id: 'i',  values: [ "anim1", "10s", "linear", "0s", "1", "normal", "none" ] },
+      { id: 'j',  values: [ "anim1, anim2, anim3", "10s, 3s, 5s", "linear, cubic-bezier(0.25, 0.1, 0.25, 1), cubic-bezier(0.25, 0.1, 0.25, 1)", "0s, 0s, 0s", "infinite, 1, 1", "normal, normal, normal", "backwards, none, both" ] }
     ];
     
     function start()
index 68b0921..8acc520 100644 (file)
@@ -5,14 +5,16 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS test1Style.webkitAnimationName is 'anim1'
 PASS test2Style.webkitAnimationName is 'anim2, anim3'
+PASS test3Style.webkitAnimationName is 'anim1, anim2, anim3'
 PASS test1Style.webkitAnimationDuration is '10s'
 PASS test2Style.webkitAnimationDuration is '5s, 2.5s'
 PASS test1Style.webkitAnimationFillMode is 'backwards'
 PASS test2Style.webkitAnimationFillMode is 'forwards, both'
 PASS test1Style.webkitAnimationIterationCount is '10'
 PASS test2Style.webkitAnimationIterationCount is '10, infinite'
-PASS test1Style.webkitAnimationTimingFunction is 'cubic-bezier(0, 0, 1, 1)'
-PASS test2Style.webkitAnimationTimingFunction is 'cubic-bezier(0, 0, 1, 1), cubic-bezier(0.42, 0, 0.58, 1)'
+PASS test1Style.webkitAnimationTimingFunction is 'linear'
+PASS test2Style.webkitAnimationTimingFunction is 'linear, cubic-bezier(0.42, 0, 0.58, 1)'
+PASS test3Style.webkitAnimationTimingFunction is 'steps(1, start), steps(1, end), steps(5, end)'
 PASS test1Style.webkitAnimationDirection is 'normal'
 PASS test2Style.webkitAnimationDirection is 'normal, alternate'
 
index 89c8eed..d3d8678 100644 (file)
         -webkit-animation-timing-function: linear, ease-in-out;
         -webkit-animation-direction: normal, alternate;
     }
+    #test3 {
+        width: 20px;
+        height: 20px;
+        background-color: blue;
+        position: relative;
+        -webkit-animation-name: anim1, anim2, anim3;
+        -webkit-animation-duration: 5s;
+        -webkit-animation-timing-function: step-start, step-end, steps(5, end);
+    }
     @-webkit-keyframes anim1 {
         from { left: 10px; }
         to { left: 20px; }
@@ -44,6 +53,7 @@
 <body>
 <p id="test1"></p>
 <p id="test2"></p>
+<p id="test3"></p>
 <p id="description"></p>
 <div id="console"></div>
 <script>
@@ -55,9 +65,11 @@ var test2 = document.getElementById("test2");
 
 var test1Style = window.getComputedStyle(test1);
 var test2Style = window.getComputedStyle(test2);
+var test3Style = window.getComputedStyle(test3);
 
 shouldBe("test1Style.webkitAnimationName", "'anim1'");
 shouldBe("test2Style.webkitAnimationName", "'anim2, anim3'");
+shouldBe("test3Style.webkitAnimationName", "'anim1, anim2, anim3'");
 
 shouldBe("test1Style.webkitAnimationDuration", "'10s'");
 shouldBe("test2Style.webkitAnimationDuration", "'5s, 2.5s'");
@@ -68,8 +80,9 @@ shouldBe("test2Style.webkitAnimationFillMode", "'forwards, both'");
 shouldBe("test1Style.webkitAnimationIterationCount", "'10'");
 shouldBe("test2Style.webkitAnimationIterationCount", "'10, infinite'");
 
-shouldBe("test1Style.webkitAnimationTimingFunction", "'cubic-bezier(0, 0, 1, 1)'");
-shouldBe("test2Style.webkitAnimationTimingFunction", "'cubic-bezier(0, 0, 1, 1), cubic-bezier(0.42, 0, 0.58, 1)'");
+shouldBe("test1Style.webkitAnimationTimingFunction", "'linear'");
+shouldBe("test2Style.webkitAnimationTimingFunction", "'linear, cubic-bezier(0.42, 0, 0.58, 1)'");
+shouldBe("test3Style.webkitAnimationTimingFunction", "'steps(1, start), steps(1, end), steps(5, end)'");
 
 shouldBe("test1Style.webkitAnimationDirection", "'normal'");
 shouldBe("test2Style.webkitAnimationDirection", "'normal, alternate'");
index a49deca..85036c4 100644 (file)
@@ -29,7 +29,7 @@
       { 'property': 'webkitTransitionDuration', 'value': '2s, 2s, 1s, 1s, 2s' },
       { 'property': 'webkitTransitionProperty', 'value': 'left, top, width, height, opacity' },
       { 'property': 'webkitTransitionDelay',    'value': '4s, 3s, 4s, 3s, 4s' },
-      { 'property': 'webkitTransitionTimingFunction', 'value': 'cubic-bezier(0, 0, 1, 1), cubic-bezier(0, 0, 1, 1), cubic-bezier(0, 0, 1, 1), cubic-bezier(0, 0, 1, 1), cubic-bezier(0, 0, 1, 1)' },
+      { 'property': 'webkitTransitionTimingFunction', 'value': 'linear, linear, linear, linear, linear' },
       { 'property': 'webkitAnimationName',      'value': 'a, b, c, d, e' },
       { 'property': 'webkitAnimationDuration',  'value': '10s, 20s, 10s, 20s, 10s' },
       { 'property': 'webkitAnimationDelay',     'value': '1s, 1s, 1s, 1s, 1s' },
diff --git a/LayoutTests/animations/timing-functions-expected.txt b/LayoutTests/animations/timing-functions-expected.txt
new file mode 100644 (file)
index 0000000..6ebd347
--- /dev/null
@@ -0,0 +1,26 @@
+This test performs an animation of the left property. It animates over 1 second. It takes 3 snapshots and expects each result to be within a specified range.
+PASS - "left" property for "box1" element at 0.25s saw something close to: 141
+PASS - "left" property for "box1" element at 0.5s saw something close to: 180
+PASS - "left" property for "box1" element at 0.75s saw something close to: 196
+PASS - "left" property for "box2" element at 0.25s saw something close to: 141
+PASS - "left" property for "box2" element at 0.5s saw something close to: 180
+PASS - "left" property for "box2" element at 0.75s saw something close to: 196
+PASS - "left" property for "box3" element at 0.25s saw something close to: 125
+PASS - "left" property for "box3" element at 0.5s saw something close to: 150
+PASS - "left" property for "box3" element at 0.75s saw something close to: 175
+PASS - "left" property for "box4" element at 0.25s saw something close to: 200
+PASS - "left" property for "box4" element at 0.5s saw something close to: 200
+PASS - "left" property for "box4" element at 0.75s saw something close to: 200
+PASS - "left" property for "box5" element at 0.25s saw something close to: 100
+PASS - "left" property for "box5" element at 0.5s saw something close to: 100
+PASS - "left" property for "box5" element at 0.75s saw something close to: 100
+PASS - "left" property for "box6" element at 0.25s saw something close to: 100
+PASS - "left" property for "box6" element at 0.5s saw something close to: 133
+PASS - "left" property for "box6" element at 0.75s saw something close to: 166
+PASS - "left" property for "box7" element at 0.25s saw something close to: 133
+PASS - "left" property for "box7" element at 0.5s saw something close to: 166
+PASS - "left" property for "box7" element at 0.75s saw something close to: 200
+PASS - "left" property for "box8" element at 0.25s saw something close to: 100
+PASS - "left" property for "box8" element at 0.5s saw something close to: 133
+PASS - "left" property for "box8" element at 0.75s saw something close to: 166
+
diff --git a/LayoutTests/animations/timing-functions.html b/LayoutTests/animations/timing-functions.html
new file mode 100644 (file)
index 0000000..fea3e0c
--- /dev/null
@@ -0,0 +1,105 @@
+<!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 timing functions</title>
+  <style type="text/css" media="screen">
+    .box {
+      position: relative;
+      left: 100px;
+      top: 0px;
+      height: 5px;
+      width: 5px;
+      background-color: blue;
+      -webkit-animation-duration: 1s;
+      -webkit-animation-name: anim;
+    }
+    @-webkit-keyframes anim {
+        from { left: 100px; }
+        to   { left: 200px; }
+    }
+    #box1 {
+    }
+    #box2 {
+      -webkit-animation-timing-function: ease;
+    }
+    #box3 {
+      -webkit-animation-timing-function: linear;
+    }
+    #box4 {
+      -webkit-animation-timing-function: step-start;
+    }
+    #box5 {
+      -webkit-animation-timing-function: step-end;
+    }
+    #box6 {
+      -webkit-animation-timing-function: steps(3);
+    }
+    #box7 {
+      -webkit-animation-timing-function: steps(3, start);
+    }
+    #box8 {
+      -webkit-animation-timing-function: steps(3, end);
+    }
+    
+  </style>
+  <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+  <script type="text/javascript" charset="utf-8">
+
+    const expectedValues = [
+      // [animation-name, time, element-id, property, expected-value, tolerance]
+      ["anim", 0.25, "box1", "left", 141, 5],
+      ["anim", 0.50, "box1", "left", 180, 5],
+      ["anim", 0.75, "box1", "left", 196, 5],
+      ["anim", 0.25, "box2", "left", 141, 5],
+      ["anim", 0.50, "box2", "left", 180, 5],
+      ["anim", 0.75, "box2", "left", 196, 5],
+      ["anim", 0.25, "box3", "left", 125, 5],
+      ["anim", 0.50, "box3", "left", 150, 5],
+      ["anim", 0.75, "box3", "left", 175, 5],
+      ["anim", 0.25, "box4", "left", 200, 5],
+      ["anim", 0.50, "box4", "left", 200, 5],
+      ["anim", 0.75, "box4", "left", 200, 5],
+      ["anim", 0.25, "box5", "left", 100, 5],
+      ["anim", 0.50, "box5", "left", 100, 5],
+      ["anim", 0.75, "box5", "left", 100, 5],
+      ["anim", 0.25, "box6", "left", 100, 5],
+      ["anim", 0.50, "box6", "left", 133, 5],
+      ["anim", 0.75, "box6", "left", 166, 5],
+      ["anim", 0.25, "box7", "left", 133, 5],
+      ["anim", 0.50, "box7", "left", 166, 5],
+      ["anim", 0.75, "box7", "left", 200, 5],
+      ["anim", 0.25, "box8", "left", 100, 5],
+      ["anim", 0.50, "box8", "left", 133, 5],
+      ["anim", 0.75, "box8", "left", 166, 5],
+    ];
+    
+    runAnimationTest(expectedValues);
+    
+  </script>
+</head>
+<body>
+This test performs an animation of the left property. It animates over 1 second.
+It takes 3 snapshots and expects each result to be within a specified range.
+<div class="box" id="box1">
+</div>
+<div class="box" id="box2">
+</div>
+<div class="box" id="box3">
+</div>
+<div class="box" id="box4">
+</div>
+<div class="box" id="box5">
+</div>
+<div class="box" id="box6">
+</div>
+<div class="box" id="box7">
+</div>
+<div class="box" id="box8">
+</div>
+<div id="result">
+</div>
+</body>
+</html>
index 29d1a5c..2ddadbd 100644 (file)
@@ -3,16 +3,16 @@ PASS -- Box 1 computed transition duration: 0s expected: 0s
 PASS -- Box 1 computed transition timing function: cubic-bezier(0.25, 0.1, 0.25, 1) expected: cubic-bezier(0.25, 0.1, 0.25, 1)
 PASS -- Box 2 computed transition property: left expected: left
 PASS -- Box 2 computed transition duration: 2s expected: 2s
-PASS -- Box 2 computed transition timing function: cubic-bezier(0, 0, 1, 1) expected: cubic-bezier(0, 0, 1, 1)
+PASS -- Box 2 computed transition timing function: linear expected: linear
 PASS -- Box 3 computed transition property: left expected: left
 PASS -- Box 3 computed transition duration: 2s expected: 2s
-PASS -- Box 3 computed transition timing function: cubic-bezier(0, 0, 1, 1) expected: cubic-bezier(0, 0, 1, 1)
+PASS -- Box 3 computed transition timing function: linear expected: linear
 PASS -- Box 4 computed transition property: left expected: left
 PASS -- Box 4 computed transition duration: 2s expected: 2s
-PASS -- Box 4 computed transition timing function: cubic-bezier(0, 0, 1, 1) expected: cubic-bezier(0, 0, 1, 1)
+PASS -- Box 4 computed transition timing function: linear expected: linear
 PASS -- Box 5 computed transition property: left expected: left
 PASS -- Box 5 computed transition duration: 2s expected: 2s
-PASS -- Box 5 computed transition timing function: cubic-bezier(0, 0, 1, 1) expected: cubic-bezier(0, 0, 1, 1)
+PASS -- Box 5 computed transition timing function: linear expected: linear
 PASS -- Box 6 computed transition property: all expected: all
 PASS -- Box 6 computed transition duration: 0s expected: 0s
 PASS -- Box 6 computed transition timing function: cubic-bezier(0.25, 0.1, 0.25, 1) expected: cubic-bezier(0.25, 0.1, 0.25, 1)
index a75a5b8..1a921c2 100644 (file)
 
     var kExpectedTimingFunction = [
       'cubic-bezier(0.25, 0.1, 0.25, 1)', /* box1 */
-      'cubic-bezier(0, 0, 1, 1)', /* box2 */
-      'cubic-bezier(0, 0, 1, 1)', /* box3 */
-      'cubic-bezier(0, 0, 1, 1)', /* box4 */ /* inherits from box3 */
-      'cubic-bezier(0, 0, 1, 1)', /* box5 */
+      'linear', /* box2 */
+      'linear', /* box3 */
+      'linear', /* box4 */ /* inherits from box3 */
+      'linear', /* box5 */
       'cubic-bezier(0.25, 0.1, 0.25, 1)', /* box6 */ /* does NOT inherit */
     ];
 
diff --git a/LayoutTests/transitions/steps-timing-function-expected.txt b/LayoutTests/transitions/steps-timing-function-expected.txt
new file mode 100644 (file)
index 0000000..4cf1170
--- /dev/null
@@ -0,0 +1,6 @@
+The box should move horizontally 200px over 1s, in 3 equal increments.
+
+PASS - "-webkit-transform.4" property for "box" element at 0.25s saw something close to: 0
+PASS - "-webkit-transform.4" property for "box" element at 0.5s saw something close to: 66
+PASS - "-webkit-transform.4" property for "box" element at 0.75s saw something close to: 133
+
diff --git a/LayoutTests/transitions/steps-timing-function.html b/LayoutTests/transitions/steps-timing-function.html
new file mode 100644 (file)
index 0000000..66fae31
--- /dev/null
@@ -0,0 +1,49 @@
+<!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>Transition with steps timing function</title>
+  <style type="text/css" media="screen">
+    #box {
+      height: 100px;
+      width: 100px;
+      background-color: blue;
+      -webkit-transform: translateX(0px);
+      -webkit-transition-duration: 1s;
+      -webkit-transition-timing-function: steps(3, end);
+      -webkit-transition-property: -webkit-transform;
+    }
+  </style>
+  <script src="transition-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+  <script type="text/javascript" charset="utf-8">
+    
+    const expectedValues = [
+      // [time, element-id, property, expected-value, tolerance]
+      [0.25, "box", "-webkit-transform.4", 0, 5],
+      [0.5, "box", "-webkit-transform.4", 66, 5],
+      [0.75, "box", "-webkit-transform.4", 133, 5],
+    ];
+    
+    function setupTest()
+    {
+      var box = document.getElementById('box');
+      box.style.webkitTransform = 'translateX(200px)';
+    }
+    
+    runTransitionTest(expectedValues, setupTest, true);
+    
+  </script>
+</head>
+<body>
+
+<p>
+The box should move horizontally 200px over 1s, in 3 equal increments.
+</p>
+<div id="box">
+</div>
+<div id="result">
+</div>
+</body>
+</html>
index 93cf35b..c2b6ef3 100644 (file)
@@ -1,3 +1,117 @@
+2010-09-08  Dean Jackson  <dino@apple.com>
+
+        Reviewed by Simon Fraser.
+
+        Implement steps() timing function for animations
+        https://bugs.webkit.org/show_bug.cgi?id=44541
+
+        Tests: animations/timing-functions.html
+               transitions/steps-timing-function.html
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::getTimingFunctionValue):
+            - when creating computed style we now test what
+              type of timing function it being used
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::parseCubicBezierTimingFunctionValue):
+            - rename this method from parseTimingFunctionValue
+        (WebCore::CSSParser::parseAnimationTimingFunction):
+            - support parsing the steps() function
+        * css/CSSParser.h:
+            - method rename
+        * css/CSSStyleSelector.cpp:
+        (WebCore::CSSStyleSelector::mapAnimationTimingFunction):
+            - handle 'step-start' and 'step-end' identifiers. Also
+              now use ::create when constructing objects
+        * css/CSSTimingFunctionValue.cpp:
+        (WebCore::CSSLinearTimingFunctionValue::cssText):
+        (WebCore::CSSCubicBezierTimingFunctionValue::cssText):
+        (WebCore::CSSStepsTimingFunctionValue::cssText):
+            - new text output for computed style. We now produce
+              the text 'linear' when appropriate.
+        * css/CSSTimingFunctionValue.h:
+        (WebCore::CSSTimingFunctionValue::isLinearTimingFunctionValue):
+        (WebCore::CSSTimingFunctionValue::isCubicBezierTimingFunctionValue):
+        (WebCore::CSSTimingFunctionValue::isStepsTimingFunctionValue):
+        (WebCore::CSSTimingFunctionValue::CSSTimingFunctionValue):
+        (WebCore::CSSTimingFunctionValue::isTimingFunctionValue):
+        (WebCore::CSSLinearTimingFunctionValue::create):
+        (WebCore::CSSLinearTimingFunctionValue::isLinearTimingFunctionValue):
+        (WebCore::CSSLinearTimingFunctionValue::CSSLinearTimingFunctionValue):
+        (WebCore::CSSCubicBezierTimingFunctionValue::create):
+        (WebCore::CSSCubicBezierTimingFunctionValue::isCubicBezierTimingFunctionValue):
+        (WebCore::CSSCubicBezierTimingFunctionValue::CSSCubicBezierTimingFunctionValue):
+        (WebCore::CSSStepsTimingFunctionValue::create):
+        (WebCore::CSSStepsTimingFunctionValue::numberOfSteps):
+        (WebCore::CSSStepsTimingFunctionValue::stepAtStart):
+        (WebCore::CSSStepsTimingFunctionValue::isStepsTimingFunctionValue):
+        (WebCore::CSSStepsTimingFunctionValue::CSSStepsTimingFunctionValue):
+            - CSSTimingFunction is now a pure virtual ref-counted base class, with
+              subclasses for each of the three supported timing functions.
+        * css/CSSValueKeywords.in:
+            - new keywords step-start and step-end
+        * page/animation/AnimationBase.cpp:
+        (WebCore::solveStepsFunction):
+            - produces the output value from a stepping function
+        (WebCore::AnimationBase::progress):
+            - now has to switch based on timing function type
+        * page/animation/KeyframeAnimation.cpp:
+        (WebCore::KeyframeAnimation::fetchIntervalEndpointsForProperty):
+            - use ref-counted access
+        * platform/animation/Animation.cpp:
+        (WebCore::Animation::animationsMatch):
+            - change timing function comparison for operator==
+        * platform/animation/Animation.h:
+        (WebCore::Animation::timingFunction):
+        (WebCore::Animation::setTimingFunction):
+        (WebCore::Animation::initialAnimationTimingFunction):
+            - move to ref-counted timing function class
+        * platform/animation/TimingFunction.h:
+        (WebCore::TimingFunction::~TimingFunction):
+        (WebCore::TimingFunction::isLinearTimingFunction):
+        (WebCore::TimingFunction::isCubicBezierTimingFunction):
+        (WebCore::TimingFunction::isStepsTimingFunction):
+        (WebCore::TimingFunction::TimingFunction):
+        (WebCore::LinearTimingFunction::create):
+        (WebCore::LinearTimingFunction::~LinearTimingFunction):
+        (WebCore::LinearTimingFunction::operator==):
+        (WebCore::LinearTimingFunction::LinearTimingFunction):
+        (WebCore::CubicBezierTimingFunction::create):
+        (WebCore::CubicBezierTimingFunction::~CubicBezierTimingFunction):
+        (WebCore::CubicBezierTimingFunction::operator==):
+        (WebCore::CubicBezierTimingFunction::CubicBezierTimingFunction):
+        (WebCore::StepsTimingFunction::create):
+        (WebCore::StepsTimingFunction::~StepsTimingFunction):
+        (WebCore::StepsTimingFunction::operator==):
+        (WebCore::StepsTimingFunction::numberOfSteps):
+        (WebCore::StepsTimingFunction::stepAtStart):
+        (WebCore::StepsTimingFunction::StepsTimingFunction):
+            - TimingFunction is now a ref-counted pure virtual base class,
+              with three subclasses representing the types of timing functions
+              that are supported.
+        * platform/graphics/GraphicsLayer.h:
+        (WebCore::AnimationValue::AnimationValue):
+        (WebCore::FloatAnimationValue::FloatAnimationValue):
+        (WebCore::TransformAnimationValue::TransformAnimationValue):
+            - use PassRefPtr in function parameters
+        * platform/graphics/qt/GraphicsLayerQt.cpp:
+        (WebCore::solveStepsFunction):
+        (WebCore::applyTimingFunction):
+        (WebCore::AnimationQt::AnimationQt):
+        (WebCore::AnimationQt::updateCurrentTime):
+            - implement the timing function switch for QT
+        * platform/graphics/mac/GraphicsLayerCA.mm:
+        (WebCore::getCAMediaTimingFunction):
+            - update for new timing function interface
+        (WebCore::animationHasStepsTimingFunction):
+            - new method to make sure animations with steps() functions
+              never try to execute in Core Animation
+        (WebCore::GraphicsLayerCA::addAnimation):
+            - test for steps() timing function
+        (WebCore::GraphicsLayerCA::timingFunctionForAnimationValue):
+        * rendering/style/RenderStyleConstants.h:
+            - remove old RenderStyle enum for timing function types
+
 2010-09-08  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         Unreviewed trivial fix after r66960.
index b1e8ac4..7d3cdc9 100644 (file)
@@ -501,13 +501,29 @@ static PassRefPtr<CSSValue> getTimingFunctionValue(const AnimationList* animList
     RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
     if (animList) {
         for (size_t i = 0; i < animList->size(); ++i) {
-            const TimingFunction& tf = animList->animation(i)->timingFunction();
-            list->append(CSSTimingFunctionValue::create(tf.x1(), tf.y1(), tf.x2(), tf.y2()));
+            const TimingFunction* tf = animList->animation(i)->timingFunction().get();
+            if (tf->isCubicBezierTimingFunction()) {
+                const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
+                list->append(CSSCubicBezierTimingFunctionValue::create(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2()));
+            } else if (tf->isStepsTimingFunction()) {
+                const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
+                list->append(CSSStepsTimingFunctionValue::create(stf->numberOfSteps(), stf->stepAtStart()));
+            } else {
+                list->append(CSSLinearTimingFunctionValue::create());
+            }
         }
     } else {
         // Note that initialAnimationTimingFunction() is used for both transitions and animations
-        const TimingFunction& tf = Animation::initialAnimationTimingFunction();
-        list->append(CSSTimingFunctionValue::create(tf.x1(), tf.y1(), tf.x2(), tf.y2()));
+        const TimingFunction* tf = Animation::initialAnimationTimingFunction().get();
+        if (tf->isCubicBezierTimingFunction()) {
+            const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
+            list->append(CSSCubicBezierTimingFunctionValue::create(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2()));
+        } else if (tf->isStepsTimingFunction()) {
+            const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
+            list->append(CSSStepsTimingFunctionValue::create(stf->numberOfSteps(), stf->stepAtStart()));
+        } else {
+            list->append(CSSLinearTimingFunctionValue::create());
+        }
     }
     return list.release();
 }
index 2ad3294..95a2784 100644 (file)
@@ -2784,7 +2784,7 @@ void CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<C
         m_valueList->next();
 }
 
-bool CSSParser::parseTimingFunctionValue(CSSParserValueList*& args, double& result)
+bool CSSParser::parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result)
 {
     CSSParserValue* v = args->current();
     if (!validUnit(v, FNumber, m_strict))
@@ -2805,31 +2805,67 @@ bool CSSParser::parseTimingFunctionValue(CSSParserValueList*& args, double& resu
 PassRefPtr<CSSValue> CSSParser::parseAnimationTimingFunction()
 {
     CSSParserValue* value = m_valueList->current();
-    if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut || value->id == CSSValueEaseInOut)
+    if (value->id == CSSValueEase || value->id == CSSValueLinear || value->id == CSSValueEaseIn || value->id == CSSValueEaseOut
+        || value->id == CSSValueEaseInOut || value->id == CSSValueStepStart || value->id == CSSValueStepEnd)
         return CSSPrimitiveValue::createIdentifier(value->id);
 
     // We must be a function.
     if (value->unit != CSSParserValue::Function)
         return 0;
 
-    // The only timing function we accept for now is a cubic bezier function.  4 points must be specified.
     CSSParserValueList* args = value->function->args.get();
-    if (!equalIgnoringCase(value->function->name, "cubic-bezier(") || !args || args->size() != 7)
-        return 0;
 
-    // There are two points specified.  The values must be between 0 and 1.
-    double x1, y1, x2, y2;
+    if (equalIgnoringCase(value->function->name, "steps(")) {
+        // For steps, 1 or 2 params must be specified (comma-separated)
+        if (!args || (args->size() != 1 && args->size() != 3))
+            return 0;
 
-    if (!parseTimingFunctionValue(args, x1))
-        return 0;
-    if (!parseTimingFunctionValue(args, y1))
-        return 0;
-    if (!parseTimingFunctionValue(args, x2))
-        return 0;
-    if (!parseTimingFunctionValue(args, y2))
-        return 0;
+        // There are two values.
+        int numSteps;
+        bool stepAtStart = false;
 
-    return CSSTimingFunctionValue::create(x1, y1, x2, y2);
+        CSSParserValue* v = args->current();
+        if (!validUnit(v, FInteger, m_strict))
+            return 0;
+        numSteps = min(v->fValue, (double)INT_MAX);
+        if (numSteps < 1)
+            return 0;
+        v = args->next();
+
+        if (v) {
+            // There is a comma so we need to parse the second value
+            if (v->unit != CSSParserValue::Operator && v->iValue != ',')
+                return 0;
+            v = args->next();
+            if (v->id != CSSValueStart && v->id != CSSValueEnd)
+                return 0;
+            stepAtStart = v->id == CSSValueStart;
+        }
+
+        return CSSStepsTimingFunctionValue::create(numSteps, stepAtStart);
+    }
+    
+    if (equalIgnoringCase(value->function->name, "cubic-bezier(")) {
+        // For cubic bezier, 4 values must be specified.
+        if (!args || args->size() != 7)
+            return 0;
+
+        // There are two points specified.  The values must be between 0 and 1.
+        double x1, y1, x2, y2;
+
+        if (!parseCubicBezierTimingFunctionValue(args, x1))
+            return 0;
+        if (!parseCubicBezierTimingFunctionValue(args, y1))
+            return 0;
+        if (!parseCubicBezierTimingFunctionValue(args, x2))
+            return 0;
+        if (!parseCubicBezierTimingFunctionValue(args, y2))
+            return 0;
+
+        return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
+    }
+    
+    return 0;
 }
 
 bool CSSParser::parseAnimationProperty(int propId, RefPtr<CSSValue>& result)
index e840a70..47f0bed 100644 (file)
@@ -108,7 +108,7 @@ namespace WebCore {
         PassRefPtr<CSSValue> parseAnimationTimingFunction();
 
         void parseTransformOriginShorthand(RefPtr<CSSValue>&, RefPtr<CSSValue>&, RefPtr<CSSValue>&);
-        bool parseTimingFunctionValue(CSSParserValueList*& args, double& result);
+        bool parseCubicBezierTimingFunctionValue(CSSParserValueList*& args, double& result);
         bool parseAnimationProperty(int propId, RefPtr<CSSValue>&);
         bool parseTransitionShorthand(bool important);
         bool parseAnimationShorthand(bool important);
index 2a7b08e..47d01e8 100644 (file)
@@ -6059,19 +6059,25 @@ void CSSStyleSelector::mapAnimationTimingFunction(Animation* animation, CSSValue
         CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value);
         switch (primitiveValue->getIdent()) {
             case CSSValueLinear:
-                animation->setTimingFunction(TimingFunction(LinearTimingFunction, 0.0, 0.0, 1.0, 1.0));
+                animation->setTimingFunction(LinearTimingFunction::create());
                 break;
             case CSSValueEase:
-                animation->setTimingFunction(TimingFunction());
+                animation->setTimingFunction(CubicBezierTimingFunction::create());
                 break;
             case CSSValueEaseIn:
-                animation->setTimingFunction(TimingFunction(CubicBezierTimingFunction, 0.42, 0.0, 1.0, 1.0));
+                animation->setTimingFunction(CubicBezierTimingFunction::create(0.42, 0.0, 1.0, 1.0));
                 break;
             case CSSValueEaseOut:
-                animation->setTimingFunction(TimingFunction(CubicBezierTimingFunction, 0.0, 0.0, 0.58, 1.0));
+                animation->setTimingFunction(CubicBezierTimingFunction::create(0.0, 0.0, 0.58, 1.0));
                 break;
             case CSSValueEaseInOut:
-                animation->setTimingFunction(TimingFunction(CubicBezierTimingFunction, 0.42, 0.0, 0.58, 1.0));
+                animation->setTimingFunction(CubicBezierTimingFunction::create(0.42, 0.0, 0.58, 1.0));
+                break;
+            case CSSValueStepStart:
+                animation->setTimingFunction(StepsTimingFunction::create(1, true));
+                break;
+            case CSSValueStepEnd:
+                animation->setTimingFunction(StepsTimingFunction::create(1, false));
                 break;
         }
         return;
@@ -6079,7 +6085,14 @@ void CSSStyleSelector::mapAnimationTimingFunction(Animation* animation, CSSValue
     
     if (value->isTimingFunctionValue()) {
         CSSTimingFunctionValue* timingFunction = static_cast<CSSTimingFunctionValue*>(value);
-        animation->setTimingFunction(TimingFunction(CubicBezierTimingFunction, timingFunction->x1(), timingFunction->y1(), timingFunction->x2(), timingFunction->y2()));
+        if (timingFunction->isCubicBezierTimingFunctionValue()) {
+            CSSCubicBezierTimingFunctionValue* cubicTimingFunction = static_cast<CSSCubicBezierTimingFunctionValue*>(value);
+            animation->setTimingFunction(CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2()));
+        } else if (timingFunction->isStepsTimingFunctionValue()) {
+            CSSStepsTimingFunctionValue* stepsTimingFunction = static_cast<CSSStepsTimingFunctionValue*>(value);
+            animation->setTimingFunction(StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart()));
+        } else
+            animation->setTimingFunction(LinearTimingFunction::create());
     }
 }
 
index e576d36..9eecb2c 100644 (file)
 
 namespace WebCore {
 
-String CSSTimingFunctionValue::cssText() const
+String CSSLinearTimingFunctionValue::cssText() const
+{
+    return "linear";
+}
+
+String CSSCubicBezierTimingFunctionValue::cssText() const
 {
     String text("cubic-bezier(");
     text += String::number(m_x1);
@@ -44,4 +49,14 @@ String CSSTimingFunctionValue::cssText() const
     return text;
 }
 
+String CSSStepsTimingFunctionValue::cssText() const
+{
+    String text("steps(");
+    text += String::number(m_steps);
+    text += ", ";
+    text += m_stepAtStart ? "start" : "end";
+    text += ")";
+    return text;
+}
+
 } // namespace WebCore
index e465993..27e899b 100644 (file)
@@ -33,20 +33,51 @@ namespace WebCore {
 
 class CSSTimingFunctionValue : public CSSValue {
 public:
-    static PassRefPtr<CSSTimingFunctionValue> create(double x1, double y1, double x2, double y2)
+    virtual String cssText() const = 0;
+
+    virtual bool isLinearTimingFunctionValue() const { return false; }
+    virtual bool isCubicBezierTimingFunctionValue() const { return false; }
+    virtual bool isStepsTimingFunctionValue() const { return false; }
+
+protected:
+    CSSTimingFunctionValue()
+    {
+    }
+
+    virtual bool isTimingFunctionValue() const { return true; }
+};
+
+class CSSLinearTimingFunctionValue : public CSSTimingFunctionValue {
+public:
+    static PassRefPtr<CSSLinearTimingFunctionValue> create()
+    {
+        return adoptRef(new CSSLinearTimingFunctionValue);
+    }
+    
+private:
+    CSSLinearTimingFunctionValue()
     {
-        return adoptRef(new CSSTimingFunctionValue(x1, y1, x2, y2));
     }
 
     virtual String cssText() const;
 
+    virtual bool isLinearTimingFunctionValue() const { return true; }
+};
+    
+class CSSCubicBezierTimingFunctionValue : public CSSTimingFunctionValue {
+public:
+    static PassRefPtr<CSSCubicBezierTimingFunctionValue> create(double x1, double y1, double x2, double y2)
+    {
+        return adoptRef(new CSSCubicBezierTimingFunctionValue(x1, y1, x2, y2));
+    }
+
     double x1() const { return m_x1; }
     double y1() const { return m_y1; }
     double x2() const { return m_x2; }
     double y2() const { return m_y2; }
 
 private:
-    CSSTimingFunctionValue(double x1, double y1, double x2, double y2)
+    CSSCubicBezierTimingFunctionValue(double x1, double y1, double x2, double y2)
         : m_x1(x1)
         , m_y1(y1)
         , m_x2(x2)
@@ -54,14 +85,41 @@ private:
     {
     }
 
-    virtual bool isTimingFunctionValue() const { return true; }
-    
+    virtual String cssText() const;
+
+    virtual bool isCubicBezierTimingFunctionValue() const { return true; }
+
     double m_x1;
     double m_y1;
     double m_x2;
     double m_y2;
 };
 
+class CSSStepsTimingFunctionValue : public CSSTimingFunctionValue {
+public:
+    static PassRefPtr<CSSStepsTimingFunctionValue> create(int steps, bool stepAtStart)
+    {
+        return adoptRef(new CSSStepsTimingFunctionValue(steps, stepAtStart));
+    }
+
+    int numberOfSteps() const { return m_steps; }
+    bool stepAtStart() const { return m_stepAtStart; }
+    
+private:
+    CSSStepsTimingFunctionValue(int steps, bool stepAtStart)
+        : m_steps(steps)
+        , m_stepAtStart(stepAtStart)
+    {
+    }
+
+    virtual String cssText() const;
+
+    virtual bool isStepsTimingFunctionValue() const { return true; }
+
+    int m_steps;
+    bool m_stepAtStart;
+};
+    
 } // namespace
 
 #endif
index 7a9b9c7..1e7c2b5 100644 (file)
@@ -691,6 +691,8 @@ linear
 ease-in
 ease-out
 ease-in-out
+step-start
+step-end
 
 #
 # CSS_PROP_ZOOM
index d4926ea..6efed8e 100644 (file)
@@ -70,6 +70,13 @@ static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x
     return bezier.solve(t, solveEpsilon(duration));
 }
 
+static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
+{
+    if (stepAtStart)
+        return min(1.0, (floor(numSteps * t) + 1) / numSteps);
+    return floor(numSteps * t) / numSteps;
+}
+
 static inline int blendFunc(const AnimationBase*, int from, int to, double progress)
 {  
     return int(from + (to - from) * progress);
@@ -1249,18 +1256,20 @@ double AnimationBase::progress(double scale, double offset, const TimingFunction
         fractionalTime = (fractionalTime - offset) * scale;
         
     if (!tf)
-        tf = &m_animation->timingFunction();
-
-    if (tf->type() == LinearTimingFunction)
+        tf = m_animation->timingFunction().get();
+
+    if (tf->isCubicBezierTimingFunction()) {
+        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
+        return solveCubicBezierFunction(ctf->x1(),
+                                        ctf->y1(),
+                                        ctf->x2(),
+                                        ctf->y2(),
+                                        fractionalTime, m_animation->duration());
+    } else if (tf->isStepsTimingFunction()) {
+        const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
+        return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime);
+    } else
         return fractionalTime;
-
-    // Cubic bezier.
-    double result = solveCubicBezierFunction(tf->x1(),
-                                            tf->y1(),
-                                            tf->x2(),
-                                            tf->y2(),
-                                            fractionalTime, m_animation->duration());
-    return result;
 }
 
 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
index 01ec2f1..eeddb94 100644 (file)
@@ -127,7 +127,7 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(int property, const Re
     const TimingFunction* timingFunction = 0;
     if (fromStyle->animations() && fromStyle->animations()->size() > 0) {
         // We get the timing function from the first animation, because we've synthesized a RenderStyle for each keyframe.
-        timingFunction = &(fromStyle->animations()->animation(0)->timingFunction());
+        timingFunction = fromStyle->animations()->animation(0)->timingFunction().get();
     }
 
     prog = progress(scale, offset, timingFunction);
index bc33a9e..112ee36 100644 (file)
@@ -106,23 +106,23 @@ bool Animation::animationsMatch(const Animation* o, bool matchPlayStates) const
     if (!o)
         return false;
     
-    bool result = m_name == o->m_name &&
-                  m_property == o->m_property && 
-                  m_iterationCount == o->m_iterationCount &&
-                  m_delay == o->m_delay &&
-                  m_duration == o->m_duration &&
-                  m_timingFunction == o->m_timingFunction &&
-                  m_direction == o->m_direction &&
-                  m_fillMode == o->m_fillMode &&
-                  m_delaySet == o->m_delaySet &&
-                  m_directionSet == o->m_directionSet &&
-                  m_durationSet == o->m_durationSet &&
-                  m_fillModeSet == o->m_fillModeSet &&
-                  m_iterationCountSet == o->m_iterationCountSet &&
-                  m_nameSet == o->m_nameSet &&
-                  m_propertySet == o->m_propertySet &&
-                  m_timingFunctionSet == o->m_timingFunctionSet &&
-                  m_isNone == o->m_isNone;
+    bool result = m_name == o->m_name
+                  && m_property == o->m_property 
+                  && m_iterationCount == o->m_iterationCount
+                  && m_delay == o->m_delay
+                  && m_duration == o->m_duration
+                  && *(m_timingFunction.get()) == *(o->m_timingFunction.get())
+                  && m_direction == o->m_direction
+                  && m_fillMode == o->m_fillMode
+                  && m_delaySet == o->m_delaySet
+                  && m_directionSet == o->m_directionSet
+                  && m_durationSet == o->m_durationSet
+                  && m_fillModeSet == o->m_fillModeSet
+                  && m_iterationCountSet == o->m_iterationCountSet
+                  && m_nameSet == o->m_nameSet
+                  && m_propertySet == o->m_propertySet
+                  && m_timingFunctionSet == o->m_timingFunctionSet
+                  && m_isNone == o->m_isNone;
 
     if (!result)
         return false;
index cabb0eb..9130415 100644 (file)
@@ -26,6 +26,7 @@
 #define Animation_h
 
 #include "PlatformString.h"
+#include "RenderStyleConstants.h"
 #include "TimingFunction.h"
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefCounted.h>
@@ -94,7 +95,7 @@ public:
     const String& name() const { return m_name; }
     unsigned playState() const { return m_playState; }
     int property() const { return m_property; }
-    const TimingFunction& timingFunction() const { return m_timingFunction; }
+    const PassRefPtr<TimingFunction> timingFunction() const { return m_timingFunction; }
 
     void setDelay(double c) { m_delay = c; m_delaySet = true; }
     void setDirection(AnimationDirection d) { m_direction = d; m_directionSet = true; }
@@ -104,7 +105,7 @@ public:
     void setName(const String& n) { m_name = n; m_nameSet = true; }
     void setPlayState(unsigned d) { m_playState = d; m_playStateSet = true; }
     void setProperty(int t) { m_property = t; m_propertySet = true; }
-    void setTimingFunction(const TimingFunction& f) { m_timingFunction = f; m_timingFunctionSet = true; }
+    void setTimingFunction(PassRefPtr<TimingFunction> f) { m_timingFunction = f; m_timingFunctionSet = true; }
 
     void setIsNoneAnimation(bool n) { m_isNone = n; }
 
@@ -129,7 +130,7 @@ private:
     int m_iterationCount;
     double m_delay;
     double m_duration;
-    TimingFunction m_timingFunction;
+    RefPtr<TimingFunction> m_timingFunction;
     AnimationDirection m_direction : 1;
     unsigned m_fillMode : 2;
 
@@ -156,7 +157,7 @@ public:
     static String initialAnimationName() { return String("none"); }
     static unsigned initialAnimationPlayState() { return AnimPlayStatePlaying; }
     static int initialAnimationProperty() { return cAnimateAll; }
-    static TimingFunction initialAnimationTimingFunction() { return TimingFunction(); }
+    static PassRefPtr<TimingFunction> initialAnimationTimingFunction() { return CubicBezierTimingFunction::create(); }
 };
 
 } // namespace WebCore
index d3f71ff..8ef2d8f 100644 (file)
 #ifndef TimingFunction_h
 #define TimingFunction_h
 
-#include "RenderStyleConstants.h"
+#include <wtf/RefCounted.h>
 
 namespace WebCore {
 
-struct TimingFunction : FastAllocBase {
-    TimingFunction()
-        : m_type(CubicBezierTimingFunction)
-        , m_x1(0.25)
-        , m_y1(0.1)
-        , m_x2(0.25)
-        , m_y2(1.0)
+class TimingFunction : public RefCounted<TimingFunction> {
+public:
+
+    enum TimingFunctionType {
+        LinearFunction, CubicBezierFunction, StepsFunction
+    };
+    
+    virtual ~TimingFunction() { }
+    
+    bool isLinearTimingFunction() const { return m_type == LinearFunction; }
+    bool isCubicBezierTimingFunction() const { return m_type == CubicBezierFunction; }
+    bool isStepsTimingFunction() const { return m_type == StepsFunction; }
+    
+    virtual bool operator==(const TimingFunction& other) = 0;
+
+protected:
+    TimingFunction(TimingFunctionType type)
+        : m_type(type)
     {
     }
+    
+    TimingFunctionType m_type;
+};
 
-    // This explicit copy constructor works around an inlining bug in GCC 4.2 (only reproed on mac, but may exist on other platforms).
-    TimingFunction(const TimingFunction& that)
-        : m_type(that.m_type)
-        , m_x1(that.m_x1)
-        , m_y1(that.m_y1)
-        , m_x2(that.m_x2)
-        , m_y2(that.m_y2)
+class LinearTimingFunction : public TimingFunction {
+public:
+    static PassRefPtr<LinearTimingFunction> create()
     {
+        return adoptRef(new LinearTimingFunction);
     }
-
-    TimingFunction(ETimingFunctionType timingFunction, double x1 = 0.0, double y1 = 0.0, double x2 = 1.0, double y2 = 1.0)
-        : m_type(timingFunction)
-        , m_x1(x1)
-        , m_y1(y1)
-        , m_x2(x2)
-        , m_y2(y2)
+    
+    ~LinearTimingFunction() { }
+    
+    virtual bool operator==(const TimingFunction& other)
     {
+        return other.isLinearTimingFunction();
+    }
+    
+private:
+    LinearTimingFunction()
+        : TimingFunction(LinearFunction)
+    {
+    }
+};
+    
+class CubicBezierTimingFunction : public TimingFunction {
+public:
+    static PassRefPtr<CubicBezierTimingFunction> create(double x1 = 0.25, double y1 = 0.1, double x2 = 0.25, double y2 = 1.0)
+    {
+        return adoptRef(new CubicBezierTimingFunction(x1, y1, x2, y2));
     }
 
-    bool operator==(const TimingFunction& o) const
+    ~CubicBezierTimingFunction() { }
+    
+    virtual bool operator==(const TimingFunction& other)
     {
-        return m_type == o.m_type && m_x1 == o.m_x1 && m_y1 == o.m_y1 && m_x2 == o.m_x2 && m_y2 == o.m_y2;
+        if (other.isCubicBezierTimingFunction()) {
+            const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(&other);
+            return m_x1 == ctf->m_x1 && m_y1 == ctf->m_y1 && m_x2 == ctf->m_x2 && m_y2 == ctf->m_y2;
+        }
+        return false;
     }
 
     double x1() const { return m_x1; }
     double y1() const { return m_y1; }
     double x2() const { return m_x2; }
     double y2() const { return m_y2; }
-
-    ETimingFunctionType type() const { return m_type; }
-
+    
 private:
-    ETimingFunctionType m_type;
+    CubicBezierTimingFunction(double x1, double y1, double x2, double y2)
+        : TimingFunction(CubicBezierFunction)
+        , m_x1(x1)
+        , m_y1(y1)
+        , m_x2(x2)
+        , m_y2(y2)
+    {
+    }
 
     double m_x1;
     double m_y1;
@@ -79,6 +113,39 @@ private:
     double m_y2;
 };
 
+class StepsTimingFunction : public TimingFunction {
+public:
+    static PassRefPtr<StepsTimingFunction> create(int steps, bool stepAtStart)
+    {
+        return adoptRef(new StepsTimingFunction(steps, stepAtStart));
+    }
+    
+    ~StepsTimingFunction() { }
+    
+    virtual bool operator==(const TimingFunction& other)
+    {
+        if (other.isStepsTimingFunction()) {
+            const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(&other);
+            return m_steps == stf->m_steps && m_stepAtStart == stf->m_stepAtStart;
+        }
+        return false;
+    }
+    
+    int numberOfSteps() const { return m_steps; }
+    bool stepAtStart() const { return m_stepAtStart; }
+    
+private:
+    StepsTimingFunction(int steps, bool stepAtStart)
+        : TimingFunction(StepsFunction)
+        , m_steps(steps)
+        , m_stepAtStart(stepAtStart)
+    {
+    }
+    
+    int m_steps;
+    bool m_stepAtStart;
+};
+    
 } // namespace WebCore
 
 #endif // TimingFunction_h
index 0f74cd5..68a580e 100644 (file)
@@ -85,18 +85,18 @@ class FloatPoint3D;
 class GraphicsContext;
 class Image;
 class TextStream;
-struct TimingFunction;
+class TimingFunction;
 
 // Base class for animation values (also used for transitions). Here to
 // represent values for properties being animated via the GraphicsLayer,
 // without pulling in style-related data from outside of the platform directory.
 class AnimationValue : public Noncopyable {
 public:
-    AnimationValue(float keyTime, const TimingFunction* timingFunction = 0)
+    AnimationValue(float keyTime, PassRefPtr<TimingFunction> timingFunction = 0)
         : m_keyTime(keyTime)
     {
         if (timingFunction)
-            m_timingFunction = adoptPtr(new TimingFunction(*timingFunction));
+            m_timingFunction = timingFunction;
     }
     
     virtual ~AnimationValue() { }
@@ -106,13 +106,13 @@ public:
 
 private:
     float m_keyTime;
-    OwnPtr<TimingFunction> m_timingFunction;
+    RefPtr<TimingFunction> m_timingFunction;
 };
 
 // Used to store one float value of an animation.
 class FloatAnimationValue : public AnimationValue {
 public:
-    FloatAnimationValue(float keyTime, float value, const TimingFunction* timingFunction = 0)
+    FloatAnimationValue(float keyTime, float value, PassRefPtr<TimingFunction> timingFunction = 0)
         : AnimationValue(keyTime, timingFunction)
         , m_value(value)
     {
@@ -127,7 +127,7 @@ private:
 // Used to store one transform value in a keyframe list.
 class TransformAnimationValue : public AnimationValue {
 public:
-    TransformAnimationValue(float keyTime, const TransformOperations* value = 0, const TimingFunction* timingFunction = 0)
+    TransformAnimationValue(float keyTime, const TransformOperations* value = 0, PassRefPtr<TimingFunction> timingFunction = 0)
         : AnimationValue(keyTime, timingFunction)
     {
         if (value)
index 315cc00..5fedaff 100644 (file)
@@ -272,16 +272,16 @@ static TransformationMatrix flipTransform()
 }
 #endif
 
-static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction)
-{
-    switch (timingFunction.type()) {
-        case LinearTimingFunction:
-            return [CAMediaTimingFunction functionWithName:@"linear"];
-        case CubicBezierTimingFunction:
-            return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(timingFunction.x1()) :static_cast<float>(timingFunction.y1())
-                        :static_cast<float>(timingFunction.x2()) :static_cast<float>(timingFunction.y2())];
-    }
-    return 0;
+static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction* timingFunction)
+{
+    // By this point, timing functions can only be linear or cubic, not steps.
+    ASSERT(!timingFunction->isStepsTimingFunction());
+    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())];
+    } else
+        return [CAMediaTimingFunction functionWithName:@"linear"];
 }
 
 static void setLayerBorderColor(PlatformLayer* layer, const Color& color)
@@ -356,6 +356,20 @@ static NSDictionary* nullActionsDictionary()
     return actions;
 }
 
+static bool animationHasStepsTimingFunction(const KeyframeValueList& valueList, const Animation* anim)
+{
+    if (anim->timingFunction()->isStepsTimingFunction())
+        return true;
+    
+    for (unsigned i = 0; i < valueList.size(); ++i) {
+        const TimingFunction* timingFunction = valueList.at(i)->timingFunction();
+        if (timingFunction && timingFunction->isStepsTimingFunction())
+            return true;
+    }
+
+    return false;
+}
+
 PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client)
 {
     return new GraphicsLayerCA(client);
@@ -716,6 +730,11 @@ bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const Int
         return false;
 #endif
 
+    // CoreAnimation does not handle the steps() timing function. Fall back
+    // to software animation in that case.
+    if (animationHasStepsTimingFunction(valueList, anim))
+        return false;
+
     bool createdAnimations = false;
     if (valueList.property() == AnimatedPropertyWebkitTransform)
         createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, keyframesName, timeOffset, boxSize);
@@ -1913,9 +1932,9 @@ CAMediaTimingFunction* GraphicsLayerCA::timingFunctionForAnimationValue(const An
     if (animValue->timingFunction())
         tf = animValue->timingFunction();
     else if (anim->isTimingFunctionSet())
-        tf = &anim->timingFunction();
+        tf = anim->timingFunction().get();
 
-    return getCAMediaTimingFunction(tf ? *tf : TimingFunction());
+    return getCAMediaTimingFunction(tf ? tf : CubicBezierTimingFunction::create().get());
 }
 
 bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, CABasicAnimation* basicAnim)
index 89badcc..151b1df 100644 (file)
@@ -1213,7 +1213,7 @@ PlatformLayer* GraphicsLayerQt::platformLayer() const
 
 template <typename T>
 struct KeyframeValueQt {
-    TimingFunction timingFunction;
+    const TimingFunction* timingFunction;
     T value;
 };
 
@@ -1230,23 +1230,32 @@ static inline double solveCubicBezierFunction(qreal p1x, qreal p1y, qreal p2x, q
     return bezier.solve(t, solveEpsilon(duration));
 }
 
-static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, double duration)
+static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
+{
+    if (stepAtStart)
+        return min(1.0, (floor(numSteps * t) + 1) / numSteps);
+    return floor(numSteps * t) / numSteps;
+}
+
+static inline qreal applyTimingFunction(const TimingFunction* timingFunction, qreal progress, double duration)
 {
     // We want the timing function to be as close as possible to what the web-developer intended, so
     // we're using the same function used by WebCore when compositing is disabled. Using easing-curves
     // would probably work for some of the cases, but wouldn't really buy us anything as we'd have to
     // convert the bezier function back to an easing curve.
 
-    if (timingFunction.type() == LinearTimingFunction)
-        return progress;
-    if (timingFunction.type() == CubicBezierTimingFunction) {
-        return solveCubicBezierFunction(timingFunction.x1(),
-                                        timingFunction.y1(),
-                                        timingFunction.x2(),
-                                        timingFunction.y2(),
+    if (timingFunction->isCubicBezierTimingFunction()) {
+        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
+        return solveCubicBezierFunction(ctf->x1(),
+                                        ctf->y1(),
+                                        ctf->x2(),
+                                        ctf->y2(),
                                         double(progress), double(duration) / 1000);
-    }
-    return progress;
+    } else if (tf->isStepsTimingFunction()) {
+        const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(timingFunction);
+        return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), double(progress));
+    } else
+        return progress;
 }
 
 // Helper functions to safely get a value out of WebCore's AnimationValue*.
@@ -1322,9 +1331,9 @@ public:
             const AnimationValue* animationValue = values.at(i);
             KeyframeValueQt<T> keyframeValue;
             if (animationValue->timingFunction())
-                keyframeValue.timingFunction = *animationValue->timingFunction();
+                keyframeValue.timingFunction = animationValue->timingFunction();
             else
-                keyframeValue.timingFunction = anim->timingFunction();
+                keyframeValue.timingFunction = anim->timingFunction().get();
             webkitAnimationToQtAnimationValue(animationValue, keyframeValue.value);
             m_keyframeValues[animationValue->keyTime()] = keyframeValue;
         }
@@ -1381,7 +1390,7 @@ protected:
         const KeyframeValueQt<T>& fromKeyframe = it.value();
         const KeyframeValueQt<T>& toKeyframe = it2.value();
 
-        const TimingFunction& timingFunc = fromKeyframe.timingFunction;
+        const TimingFunction* timingFunc = fromKeyframe.timingFunction;
         const T& fromValue = fromKeyframe.value;
         const T& toValue = toKeyframe.value;
 
index 981fa16..6c58e0d 100644 (file)
@@ -1125,7 +1125,7 @@ bool RenderLayerBacking::startAnimation(double timeOffset, const Animation* anim
             continue;
             
         // get timing function
-        const TimingFunction* tf = keyframeStyle->hasAnimations() ? &((*keyframeStyle->animations()).animation(0)->timingFunction()) : 0;
+        RefPtr<TimingFunction> tf = keyframeStyle->hasAnimations() ? (*keyframeStyle->animations()).animation(0)->timingFunction() : 0;
         
         if (currentKeyframe.containsProperty(CSSPropertyWebkitTransform))
             transformVector.insert(new TransformAnimationValue(key, &(keyframeStyle->transform()), tf));
index 68c8113..a78321f 100644 (file)
@@ -303,8 +303,6 @@ enum EAnimPlayState {
     AnimPlayStatePaused = 0x1
 };
 
-enum ETimingFunctionType { LinearTimingFunction, CubicBezierTimingFunction };
-
 enum EWhiteSpace {
     NORMAL, PRE, PRE_WRAP, PRE_LINE, NOWRAP, KHTML_NOWRAP
 };