[chromium] Create a base-class CCAnimation to represent compositor animations
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Jan 2012 04:18:02 +0000 (04:18 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Jan 2012 04:18:02 +0000 (04:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=73233

Source/WebCore:

Adds a kernel for running animations on the chromium compositor
thread.

Patch by Ian Vollick <vollick@chromium.org> on 2012-01-18
Reviewed by Kenneth Russell.

* WebCore.gypi:
* platform/graphics/chromium/cc/CCActiveAnimation.cpp: Added.
(WebCore::CCActiveAnimation::CCActiveAnimation):
(WebCore::CCActiveAnimation::setRunState):
(WebCore::CCActiveAnimation::isFinishedAt):
(WebCore::CCActiveAnimation::trimTimeToCurrentIteration):
* platform/graphics/chromium/cc/CCActiveAnimation.h: Added.
(WebCore::CCActiveAnimation::create):
(WebCore::CCActiveAnimation::~CCActiveAnimation):
(WebCore::CCActiveAnimation::group):
(WebCore::CCActiveAnimation::targetProperty):
(WebCore::CCActiveAnimation::runState):
(WebCore::CCActiveAnimation::iterations):
(WebCore::CCActiveAnimation::setIterations):
(WebCore::CCActiveAnimation::startTime):
(WebCore::CCActiveAnimation::setStartTime):
(WebCore::CCActiveAnimation::isFinished):
(WebCore::CCActiveAnimation::animationCurve):
* platform/graphics/chromium/cc/CCAnimationCurve.cpp: Added.
(WebCore::CCAnimationCurve::toFloatAnimationCurve):
(WebCore::CCAnimationCurve::toTransformAnimationCurve):
* platform/graphics/chromium/cc/CCAnimationCurve.h: Added.
(WebCore::CCAnimationCurve::~CCAnimationCurve):
(WebCore::CCFloatAnimationCurve::~CCFloatAnimationCurve):
(WebCore::CCFloatAnimationCurve::type):
(WebCore::CCTransformAnimationCurve::~CCTransformAnimationCurve):
(WebCore::CCTransformAnimationCurve::type):
* platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.cpp: Added.
(WebCore::CCLayerAnimationControllerImpl::create):
(WebCore::CCLayerAnimationControllerImpl::CCLayerAnimationControllerImpl):
(WebCore::CCLayerAnimationControllerImpl::animate):
(WebCore::CCLayerAnimationControllerImpl::add):
(WebCore::CCLayerAnimationControllerImpl::getActiveAnimation):
(WebCore::CCLayerAnimationControllerImpl::hasActiveAnimation):
(WebCore::CCLayerAnimationControllerImpl::startAnimationsWaitingForNextTick):
(WebCore::CCLayerAnimationControllerImpl::startAnimationsWaitingForStartTime):
(WebCore::CCLayerAnimationControllerImpl::startAnimationsWaitingForTargetAvailability):
(WebCore::CCLayerAnimationControllerImpl::resolveConflicts):
(WebCore::CCLayerAnimationControllerImpl::purgeFinishedAnimations):
(WebCore::CCLayerAnimationControllerImpl::tickAnimations):
* platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.h: Added.
(WebCore::CCLayerAnimationControllerImplClient::~CCLayerAnimationControllerImplClient):

Source/WebKit/chromium:

Patch by Ian Vollick <vollick@chromium.org> on 2012-01-18
Reviewed by Kenneth Russell.

* WebKit.gypi:
* tests/CCActiveAnimationTest.cpp: Added.
(WebCore::FakeFloatAnimation::duration):
(WebCore::FakeFloatAnimation::getValue):
(WebCore::createActiveAnimation):
(WebCore::TEST):
* tests/CCLayerAnimationControllerImplTest.cpp: Added.
(WebCore::FakeControllerClient::FakeControllerClient):
(WebCore::FakeControllerClient::~FakeControllerClient):
(WebCore::FakeControllerClient::opacity):
(WebCore::FakeControllerClient::setOpacity):
(WebCore::FakeControllerClient::transform):
(WebCore::FakeControllerClient::setTransform):
(WebCore::FakeControllerClient::animationControllerImplDidActivate):
(WebCore::FakeControllerClient::activeControllers):
(WebCore::FakeTransformTransition::FakeTransformTransition):
(WebCore::FakeTransformTransition::duration):
(WebCore::FakeTransformTransition::getValue):
(WebCore::FakeFloatTransition::FakeFloatTransition):
(WebCore::FakeFloatTransition::duration):
(WebCore::FakeFloatTransition::getValue):
(WebCore::TEST):

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

12 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.gypi
Source/WebCore/platform/graphics/chromium/cc/CCActiveAnimation.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/chromium/cc/CCActiveAnimation.h [new file with mode: 0644]
Source/WebCore/platform/graphics/chromium/cc/CCAnimationCurve.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/chromium/cc/CCAnimationCurve.h [new file with mode: 0644]
Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.h [new file with mode: 0644]
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/WebKit.gypi
Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp [new file with mode: 0644]
Source/WebKit/chromium/tests/CCLayerAnimationControllerImplTest.cpp [new file with mode: 0644]

index 06e851c..13852d2 100644 (file)
@@ -1,3 +1,56 @@
+2012-01-18  Ian Vollick  <vollick@chromium.org>
+
+        [chromium] Create a base-class CCAnimation to represent compositor animations
+        https://bugs.webkit.org/show_bug.cgi?id=73233
+
+        Adds a kernel for running animations on the chromium compositor
+        thread.
+
+        Reviewed by Kenneth Russell.
+
+        * WebCore.gypi:
+        * platform/graphics/chromium/cc/CCActiveAnimation.cpp: Added.
+        (WebCore::CCActiveAnimation::CCActiveAnimation):
+        (WebCore::CCActiveAnimation::setRunState):
+        (WebCore::CCActiveAnimation::isFinishedAt):
+        (WebCore::CCActiveAnimation::trimTimeToCurrentIteration):
+        * platform/graphics/chromium/cc/CCActiveAnimation.h: Added.
+        (WebCore::CCActiveAnimation::create):
+        (WebCore::CCActiveAnimation::~CCActiveAnimation):
+        (WebCore::CCActiveAnimation::group):
+        (WebCore::CCActiveAnimation::targetProperty):
+        (WebCore::CCActiveAnimation::runState):
+        (WebCore::CCActiveAnimation::iterations):
+        (WebCore::CCActiveAnimation::setIterations):
+        (WebCore::CCActiveAnimation::startTime):
+        (WebCore::CCActiveAnimation::setStartTime):
+        (WebCore::CCActiveAnimation::isFinished):
+        (WebCore::CCActiveAnimation::animationCurve):
+        * platform/graphics/chromium/cc/CCAnimationCurve.cpp: Added.
+        (WebCore::CCAnimationCurve::toFloatAnimationCurve):
+        (WebCore::CCAnimationCurve::toTransformAnimationCurve):
+        * platform/graphics/chromium/cc/CCAnimationCurve.h: Added.
+        (WebCore::CCAnimationCurve::~CCAnimationCurve):
+        (WebCore::CCFloatAnimationCurve::~CCFloatAnimationCurve):
+        (WebCore::CCFloatAnimationCurve::type):
+        (WebCore::CCTransformAnimationCurve::~CCTransformAnimationCurve):
+        (WebCore::CCTransformAnimationCurve::type):
+        * platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.cpp: Added.
+        (WebCore::CCLayerAnimationControllerImpl::create):
+        (WebCore::CCLayerAnimationControllerImpl::CCLayerAnimationControllerImpl):
+        (WebCore::CCLayerAnimationControllerImpl::animate):
+        (WebCore::CCLayerAnimationControllerImpl::add):
+        (WebCore::CCLayerAnimationControllerImpl::getActiveAnimation):
+        (WebCore::CCLayerAnimationControllerImpl::hasActiveAnimation):
+        (WebCore::CCLayerAnimationControllerImpl::startAnimationsWaitingForNextTick):
+        (WebCore::CCLayerAnimationControllerImpl::startAnimationsWaitingForStartTime):
+        (WebCore::CCLayerAnimationControllerImpl::startAnimationsWaitingForTargetAvailability):
+        (WebCore::CCLayerAnimationControllerImpl::resolveConflicts):
+        (WebCore::CCLayerAnimationControllerImpl::purgeFinishedAnimations):
+        (WebCore::CCLayerAnimationControllerImpl::tickAnimations):
+        * platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.h: Added.
+        (WebCore::CCLayerAnimationControllerImplClient::~CCLayerAnimationControllerImplClient):
+
 2012-01-18  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r105331.
index 1eea207..7b517d4 100644 (file)
             'platform/graphics/chromium/VideoLayerChromium.h',
             'platform/graphics/chromium/WebGLLayerChromium.cpp',
             'platform/graphics/chromium/WebGLLayerChromium.h',
+            'platform/graphics/chromium/cc/CCActiveAnimation.cpp',
+            'platform/graphics/chromium/cc/CCActiveAnimation.h',
+            'platform/graphics/chromium/cc/CCAnimationCurve.cpp',
+            'platform/graphics/chromium/cc/CCAnimationCurve.h',
             'platform/graphics/chromium/cc/CCCanvasDrawQuad.cpp',
             'platform/graphics/chromium/cc/CCCanvasDrawQuad.h',
             'platform/graphics/chromium/cc/CCCanvasLayerImpl.cpp',
             'platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp',
             'platform/graphics/chromium/cc/CCHeadsUpDisplay.h',
             'platform/graphics/chromium/cc/CCInputHandler.h',
+            'platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.h',
+            'platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.cpp',
             'platform/graphics/chromium/cc/CCLayerImpl.cpp',
             'platform/graphics/chromium/cc/CCLayerImpl.h',
             'platform/graphics/chromium/cc/CCLayerIterator.cpp',
diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCActiveAnimation.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCActiveAnimation.cpp
new file mode 100644 (file)
index 0000000..21e6cc7
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "cc/CCActiveAnimation.h"
+
+#include "cc/CCAnimationCurve.h"
+
+#include <cmath>
+
+namespace WebCore {
+
+CCActiveAnimation::CCActiveAnimation(PassOwnPtr<CCAnimationCurve> curve, GroupID group, TargetProperty targetProperty)
+    : m_animationCurve(curve)
+    , m_group(group)
+    , m_targetProperty(targetProperty)
+    , m_runState(WaitingForTargetAvailability)
+    , m_iterations(1)
+    , m_startTime(0)
+    , m_pauseTime(0)
+    , m_totalPausedTime(0)
+{
+}
+
+void CCActiveAnimation::setRunState(RunState runState, double now)
+{
+    if (runState == Running && m_runState == Paused)
+        m_totalPausedTime += now - m_pauseTime;
+    else if (runState == Paused)
+        m_pauseTime = now;
+    m_runState = runState;
+}
+
+bool CCActiveAnimation::isFinishedAt(double time) const
+{
+    if (m_runState == Finished || m_runState == Aborted)
+        return true;
+
+    return m_runState == Running
+        && m_iterations >= 0
+        && m_iterations * m_animationCurve->duration() <= time - startTime() - m_totalPausedTime;
+}
+
+double CCActiveAnimation::trimTimeToCurrentIteration(double now) const
+{
+    double trimmed = now;
+
+    // If we're paused, time is 'stuck' at the pause time.
+    if (m_runState == Paused && trimmed > m_pauseTime)
+        trimmed = m_pauseTime;
+
+    // Returned time should always be relative to the start time and should subtract
+    // all time spent paused.
+    trimmed -= m_startTime + m_totalPausedTime;
+
+    // Zero is always the start of the animation.
+    if (trimmed <= 0)
+        return 0;
+
+    // Always return zero if we have no iterations.
+    if (!m_iterations)
+        return 0;
+
+    // If less than an iteration duration, just return trimmed.
+    if (trimmed < m_animationCurve->duration())
+        return trimmed;
+
+    // If greater than or equal to the total duration, return iteration duration.
+    if (m_iterations >= 0 && trimmed >= m_animationCurve->duration() * m_iterations)
+        return m_animationCurve->duration();
+
+    // Finally, return x where trimmed = x + n * m_animation->duration() for some positive integer n.
+    return fmod(trimmed, m_animationCurve->duration());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCActiveAnimation.h b/Source/WebCore/platform/graphics/chromium/cc/CCActiveAnimation.h
new file mode 100644 (file)
index 0000000..c222279
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CCActiveAnimation_h
+#define CCActiveAnimation_h
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class CCAnimationCurve;
+
+// A CCActiveAnimation, contains all the state required to play a CCAnimationCurve.
+// Specifically, the affected property, the run state (paused, finished, etc.),
+// loop count, last pause time, and the total time spent paused.
+class CCActiveAnimation {
+public:
+    // Animations that must be run together are called 'grouped' and have the same GroupID
+    // Grouped animations are guaranteed to start at the same time and no other animations
+    // may animate any of the group's target properties until all animations in the
+    // group have finished animating. Note: an active animation's group id and target
+    // property uniquely identify that animation.
+    typedef int GroupID;
+
+    // Animations begin in one of the 'waiting' states. Animations waiting for the next tick
+    // will start the next time the controller animates. Animations waiting for target
+    // availibility will run as soon as their target property is free (and all the animations
+    // animating with it are also able to run). Animations waiting for their start time to
+    // come have be scheduled to run at a particular point in time. When this time arrives,
+    // the controller will move the animations into the Running state. Running animations
+    // may toggle between Running and Paused, and may be stopped by moving into either the
+    // Aborted or Finished states. A Finished animation was allowed to run to completion, but
+    // an Aborted animation was not.
+    enum RunState {
+        WaitingForNextTick = 1,
+        WaitingForTargetAvailability,
+        WaitingForStartTime,
+        Running,
+        Paused,
+        Finished,
+        Aborted
+    };
+
+    enum TargetProperty {
+        Transform = 1,
+        Opacity
+    };
+
+    static PassOwnPtr<CCActiveAnimation> create(PassOwnPtr<CCAnimationCurve> curve, GroupID group, TargetProperty targetProperty)
+    {
+        return adoptPtr(new CCActiveAnimation(curve, group, targetProperty));
+    }
+
+    virtual ~CCActiveAnimation() { }
+
+    GroupID group() const { return m_group; }
+    TargetProperty targetProperty() const { return m_targetProperty; }
+
+    RunState runState() const { return m_runState; }
+    void setRunState(RunState, double now);
+
+    // This is the number of times that the animation will play. If this
+    // value is zero the animation will not play. If it is negative, then
+    // the animation will loop indefinitely.
+    int iterations() const { return m_iterations; }
+    void setIterations(int n) { m_iterations = n; }
+
+    double startTime() const { return m_startTime; }
+    void setStartTime(double startTime) { m_startTime = startTime; }
+
+    bool isFinishedAt(double time) const;
+    bool isFinished() const { return m_runState == Finished || m_runState == Aborted; }
+
+    CCAnimationCurve* animationCurve() { return m_animationCurve.get(); }
+    const CCAnimationCurve* animationCurve() const { return m_animationCurve.get(); }
+
+    // Takes the given absolute time, and using the start time and the number
+    // of iterations, returns the relative time in the current iteration.
+    double trimTimeToCurrentIteration(double now) const;
+
+private:
+    CCActiveAnimation(PassOwnPtr<CCAnimationCurve>, GroupID, TargetProperty);
+
+    OwnPtr<CCAnimationCurve> m_animationCurve;
+    GroupID m_group;
+    TargetProperty m_targetProperty;
+    RunState m_runState;
+    int m_iterations;
+    double m_startTime;
+
+    // These are used in trimTimeToCurrentIteration to account for time
+    // spent while paused. This is not included in AnimationState since it
+    // there is absolutely no need for clients of this controller to know
+    // about these values.
+    double m_pauseTime;
+    double m_totalPausedTime;
+};
+
+} // namespace WebCore
+
+#endif // CCActiveAnimation_h
diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCAnimationCurve.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCAnimationCurve.cpp
new file mode 100644 (file)
index 0000000..58cdc6a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "cc/CCAnimationCurve.h"
+
+namespace WebCore {
+
+const CCFloatAnimationCurve* CCAnimationCurve::toFloatAnimationCurve() const
+{
+    ASSERT(type() ==  CCAnimationCurve::Float);
+    return static_cast<const CCFloatAnimationCurve*>(this);
+}
+
+const CCTransformAnimationCurve* CCAnimationCurve::toTransformAnimationCurve() const
+{
+    ASSERT(type() ==  CCAnimationCurve::Transform);
+    return static_cast<const CCTransformAnimationCurve*>(this);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCAnimationCurve.h b/Source/WebCore/platform/graphics/chromium/cc/CCAnimationCurve.h
new file mode 100644 (file)
index 0000000..de97e38
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CCAnimationCurve_h
+#define CCAnimationCurve_h
+
+namespace WebCore {
+
+class CCFloatAnimationCurve;
+class CCTransformAnimationCurve;
+class TransformOperations;
+
+// An animation curve is a function that returns a value given a time.
+// There are currently only two types of curve, float and transform.
+class CCAnimationCurve {
+public:
+    enum Type { Float, Transform };
+
+    virtual ~CCAnimationCurve() { }
+
+    virtual double duration() const = 0;
+    virtual Type type() const = 0;
+
+    const CCFloatAnimationCurve* toFloatAnimationCurve() const;
+    const CCTransformAnimationCurve* toTransformAnimationCurve() const;
+};
+
+class CCFloatAnimationCurve : public CCAnimationCurve {
+public:
+    virtual ~CCFloatAnimationCurve() { }
+
+    virtual float getValue(double t) const = 0;
+
+    // Partial CCAnimation implementation.
+    virtual Type type() const { return Float; }
+};
+
+class CCTransformAnimationCurve : public CCAnimationCurve {
+public:
+    virtual ~CCTransformAnimationCurve() { }
+
+    virtual TransformOperations getValue(double t) const = 0;
+
+    // Partial CCAnimation implementation.
+    virtual Type type() const { return Transform; }
+};
+
+} // namespace WebCore
+
+#endif // CCAnimation_h
diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.cpp
new file mode 100644 (file)
index 0000000..bbca9cb
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "cc/CCLayerAnimationControllerImpl.h"
+
+#include "TransformOperations.h"
+
+namespace WebCore {
+
+// A collection of properties. Used when deterimining if animations waiting for target
+// availibility are able to run.
+typedef HashSet<int> TargetProperties;
+
+PassOwnPtr<CCLayerAnimationControllerImpl> CCLayerAnimationControllerImpl::create(CCLayerAnimationControllerImplClient* client)
+{
+    return adoptPtr(new CCLayerAnimationControllerImpl(client));
+}
+
+CCLayerAnimationControllerImpl::CCLayerAnimationControllerImpl(CCLayerAnimationControllerImplClient* client)
+    : m_client(client)
+{
+}
+
+void CCLayerAnimationControllerImpl::animate(double frameBeginTimeSecs)
+{
+    startAnimationsWaitingForNextTick(frameBeginTimeSecs);
+    startAnimationsWaitingForStartTime(frameBeginTimeSecs);
+    startAnimationsWaitingForTargetAvailability(frameBeginTimeSecs);
+    resolveConflicts(frameBeginTimeSecs);
+    tickAnimations(frameBeginTimeSecs);
+    purgeFinishedAnimations();
+    startAnimationsWaitingForTargetAvailability(frameBeginTimeSecs);
+}
+
+void CCLayerAnimationControllerImpl::add(PassOwnPtr<CCActiveAnimation> anim)
+{
+    m_activeAnimations.append(anim);
+    if (m_client)
+        m_client->animationControllerImplDidActivate(this);
+}
+
+CCActiveAnimation* CCLayerAnimationControllerImpl::getActiveAnimation(CCActiveAnimation::GroupID group, CCActiveAnimation::TargetProperty property)
+{
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->group() == group && m_activeAnimations[i]->targetProperty() == property)
+            return m_activeAnimations[i].get();
+    }
+    return 0;
+}
+
+bool CCLayerAnimationControllerImpl::hasActiveAnimation() const
+{
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() != CCActiveAnimation::Finished && m_activeAnimations[i]->runState() != CCActiveAnimation::Aborted)
+            return true;
+    }
+    return false;
+}
+
+void CCLayerAnimationControllerImpl::startAnimationsWaitingForNextTick(double now)
+{
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForNextTick) {
+            m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, now);
+            m_activeAnimations[i]->setStartTime(now);
+        }
+    }
+}
+
+void CCLayerAnimationControllerImpl::startAnimationsWaitingForStartTime(double now)
+{
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForStartTime && m_activeAnimations[i]->startTime() <= now)
+            m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, now);
+    }
+}
+
+void CCLayerAnimationControllerImpl::startAnimationsWaitingForTargetAvailability(double now)
+{
+    // First collect running properties.
+    TargetProperties blockedProperties;
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() == CCActiveAnimation::Running || m_activeAnimations[i]->runState() == CCActiveAnimation::Finished)
+            blockedProperties.add(m_activeAnimations[i]->targetProperty());
+    }
+
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() == CCActiveAnimation::WaitingForTargetAvailability) {
+            // Collect all properties for animations with the same group id (they should all also be in the list of animations).
+            TargetProperties enqueuedProperties;
+            enqueuedProperties.add(m_activeAnimations[i]->targetProperty());
+            for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+                if (m_activeAnimations[i]->group() == m_activeAnimations[j]->group())
+                    enqueuedProperties.add(m_activeAnimations[j]->targetProperty());
+            }
+
+            // Check to see if intersection of the list of properties affected by the group and the list of currently
+            // blocked properties is null. In any case, the group's target properties need to be added to the list
+            // of blocked properties.
+            bool nullIntersection = true;
+            for (TargetProperties::iterator pIter = enqueuedProperties.begin(); pIter != enqueuedProperties.end(); ++pIter) {
+                if (!blockedProperties.add(*pIter).second)
+                    nullIntersection = false;
+            }
+
+            // If the intersection is null, then we are free to start the animations in the group.
+            if (nullIntersection) {
+                m_activeAnimations[i]->setRunState(CCActiveAnimation::Running, now);
+                m_activeAnimations[i]->setStartTime(now);
+                for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+                    if (m_activeAnimations[i]->group() == m_activeAnimations[j]->group()) {
+                        m_activeAnimations[j]->setRunState(CCActiveAnimation::Running, now);
+                        m_activeAnimations[j]->setStartTime(now);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void CCLayerAnimationControllerImpl::resolveConflicts(double now)
+{
+    // Find any animations that are animating the same property and resolve the
+    // confict. We could eventually blend, but for now we'll just abort the
+    // previous animation (where 'previous' means: (1) has a prior start time or
+    // (2) has an equal start time, but was added to the queue earlier, i.e.,
+    // has a lower index in m_activeAnimations).
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() == CCActiveAnimation::Running) {
+            for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+                if (m_activeAnimations[j]->runState() == CCActiveAnimation::Running && m_activeAnimations[i]->targetProperty() == m_activeAnimations[j]->targetProperty()) {
+                    if (m_activeAnimations[i]->startTime() > m_activeAnimations[j]->startTime())
+                        m_activeAnimations[j]->setRunState(CCActiveAnimation::Aborted, now);
+                    else
+                        m_activeAnimations[i]->setRunState(CCActiveAnimation::Aborted, now);
+                }
+            }
+        }
+    }
+}
+
+void CCLayerAnimationControllerImpl::purgeFinishedAnimations()
+{
+    // Each iteration, m_activeAnimations.size() decreases or i increments,
+    // guaranteeing progress towards loop termination.
+    size_t i = 0;
+    while (i < m_activeAnimations.size()) {
+        bool allAnimsWithSameIdAreFinished = false;
+        if (m_activeAnimations[i]->isFinished()) {
+            allAnimsWithSameIdAreFinished = true;
+            for (size_t j = i + 1; j < m_activeAnimations.size(); ++j) {
+                if (m_activeAnimations[i]->group() == m_activeAnimations[j]->group() && !m_activeAnimations[j]->isFinished()) {
+                    allAnimsWithSameIdAreFinished = false;
+                    break;
+                }
+            }
+        }
+        if (allAnimsWithSameIdAreFinished)
+            m_activeAnimations.remove(i);
+        else
+            i++;
+    }
+}
+
+void CCLayerAnimationControllerImpl::tickAnimations(double now)
+{
+    for (size_t i = 0; i < m_activeAnimations.size(); ++i) {
+        if (m_activeAnimations[i]->runState() == CCActiveAnimation::Running) {
+            double trimmed = m_activeAnimations[i]->trimTimeToCurrentIteration(now);
+            switch (m_activeAnimations[i]->targetProperty()) {
+
+            case CCActiveAnimation::Transform: {
+                const CCTransformAnimationCurve* transformAnimationCurve = m_activeAnimations[i]->animationCurve()->toTransformAnimationCurve();
+                const TransformOperations operations = transformAnimationCurve->getValue(trimmed);
+                if (m_activeAnimations[i]->isFinishedAt(now))
+                    m_activeAnimations[i]->setRunState(CCActiveAnimation::Finished, now);
+
+                // Decide here if absolute or relative. Absolute for now.
+                TransformationMatrix toApply;
+                operations.apply(FloatSize(), toApply);
+                m_client->setTransform(toApply);
+                break;
+            }
+
+            case CCActiveAnimation::Opacity: {
+                const CCFloatAnimationCurve* floatAnimationCurve = m_activeAnimations[i]->animationCurve()->toFloatAnimationCurve();
+                const float opacity = floatAnimationCurve->getValue(trimmed);
+                if (m_activeAnimations[i]->isFinishedAt(now))
+                    m_activeAnimations[i]->setRunState(CCActiveAnimation::Finished, now);
+
+                m_client->setOpacity(opacity);
+                break;
+            }
+
+            } // switch
+        } // if running
+    } // for each animation
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.h b/Source/WebCore/platform/graphics/chromium/cc/CCLayerAnimationControllerImpl.h
new file mode 100644 (file)
index 0000000..1ad223d
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CCLayerAnimationControllerImpl_h
+#define CCLayerAnimationControllerImpl_h
+
+#include "cc/CCActiveAnimation.h"
+#include "cc/CCAnimationCurve.h"
+
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CCLayerAnimationControllerImpl;
+class IntSize;
+class TransformationMatrix;
+class TransformOperations;
+
+class CCLayerAnimationControllerImplClient {
+public:
+    virtual ~CCLayerAnimationControllerImplClient() { }
+
+    virtual float opacity() const = 0;
+    virtual void setOpacity(float) = 0;
+    virtual const TransformationMatrix& transform() const = 0;
+    virtual void setTransform(const TransformationMatrix&) = 0;
+    virtual void animationControllerImplDidActivate(CCLayerAnimationControllerImpl*) = 0;
+};
+
+class CCLayerAnimationControllerImpl {
+public:
+    static PassOwnPtr<CCLayerAnimationControllerImpl> create(CCLayerAnimationControllerImplClient*);
+
+    void animate(double frameBeginTimeSecs);
+
+    void add(PassOwnPtr<CCActiveAnimation>);
+
+    // Returns the active animation in the given group, animating the given property if such an
+    // animation exists.
+    CCActiveAnimation* getActiveAnimation(CCActiveAnimation::GroupID, CCActiveAnimation::TargetProperty);
+
+    // Returns true if there are any animations that are neither finished nor aborted.
+    bool hasActiveAnimation() const;
+
+private:
+    // The animator is owned by the layer.
+    explicit CCLayerAnimationControllerImpl(CCLayerAnimationControllerImplClient*);
+
+    void startAnimationsWaitingForNextTick(double now);
+    void startAnimationsWaitingForStartTime(double now);
+    void startAnimationsWaitingForTargetAvailability(double now);
+    void resolveConflicts(double now);
+    void purgeFinishedAnimations();
+
+    void tickAnimations(double now);
+
+    CCLayerAnimationControllerImplClient* m_client;
+    Vector<OwnPtr<CCActiveAnimation> > m_activeAnimations;
+};
+
+} // namespace WebCore
+
+#endif // CCLayerAnimationControllerImpl_h
+
index 7f29e9f..6789826 100644 (file)
@@ -1,3 +1,33 @@
+2012-01-18  Ian Vollick  <vollick@chromium.org>
+
+        [chromium] Create a base-class CCAnimation to represent compositor animations
+        https://bugs.webkit.org/show_bug.cgi?id=73233
+
+        Reviewed by Kenneth Russell.
+
+        * WebKit.gypi:
+        * tests/CCActiveAnimationTest.cpp: Added.
+        (WebCore::FakeFloatAnimation::duration):
+        (WebCore::FakeFloatAnimation::getValue):
+        (WebCore::createActiveAnimation):
+        (WebCore::TEST):
+        * tests/CCLayerAnimationControllerImplTest.cpp: Added.
+        (WebCore::FakeControllerClient::FakeControllerClient):
+        (WebCore::FakeControllerClient::~FakeControllerClient):
+        (WebCore::FakeControllerClient::opacity):
+        (WebCore::FakeControllerClient::setOpacity):
+        (WebCore::FakeControllerClient::transform):
+        (WebCore::FakeControllerClient::setTransform):
+        (WebCore::FakeControllerClient::animationControllerImplDidActivate):
+        (WebCore::FakeControllerClient::activeControllers):
+        (WebCore::FakeTransformTransition::FakeTransformTransition):
+        (WebCore::FakeTransformTransition::duration):
+        (WebCore::FakeTransformTransition::getValue):
+        (WebCore::FakeFloatTransition::FakeFloatTransition):
+        (WebCore::FakeFloatTransition::duration):
+        (WebCore::FakeFloatTransition::getValue):
+        (WebCore::TEST):
+
 2012-01-18  James Robinson  <jamesr@chromium.org>
 
         Unreviewed, rolling out r105366.
index 0db4846..2503a9c 100644 (file)
             'tests/ArenaTestHelpers.h',
             'tests/AssociatedURLLoaderTest.cpp',
             'tests/Canvas2DLayerChromiumTest.cpp',
+            'tests/CCActiveAnimationTest.cpp',
             'tests/CCDamageTrackerTest.cpp',
             'tests/CCDelayBasedTimeSourceTest.cpp',
             'tests/CCFrameRateControllerTest.cpp',
+            'tests/CCLayerAnimationControllerImplTest.cpp',
             'tests/CCLayerImplTest.cpp',
             'tests/CCLayerIteratorTest.cpp',
             'tests/CCLayerQuadTest.cpp',
diff --git a/Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp b/Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp
new file mode 100644 (file)
index 0000000..3e93d95
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "cc/CCActiveAnimation.h"
+
+#include "cc/CCAnimationCurve.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class FakeFloatAnimation : public CCFloatAnimationCurve {
+public:
+    virtual double duration() const { return 1; }
+    virtual float getValue(double now) const { return 0; }
+};
+
+PassOwnPtr<CCActiveAnimation> createActiveAnimation(int iterations)
+{
+    OwnPtr<CCActiveAnimation> toReturn(CCActiveAnimation::create(adoptPtr(new FakeFloatAnimation), 1, CCActiveAnimation::Opacity));
+    toReturn->setIterations(iterations);
+    return toReturn.release();
+}
+
+TEST(CCActiveAnimationTest, TrimTimeZeroIterations)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(-1));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(1));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeOneIteration)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(-1));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+    EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1));
+    EXPECT_EQ(1, anim->trimTimeToCurrentIteration(2));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeInfiniteIterations)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(-1));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+    EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(1));
+    EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1.5));
+}
+
+TEST(CCActiveAnimationTest, TrimTimeStartTime)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+    anim->setStartTime(4);
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(4));
+    EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(4.5));
+    EXPECT_EQ(1, anim->trimTimeToCurrentIteration(5));
+    EXPECT_EQ(1, anim->trimTimeToCurrentIteration(6));
+}
+
+TEST(CCActiveAnimationTest, TrimTimePauseResume)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+    anim->setRunState(CCActiveAnimation::Running, 0);
+    EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0));
+    EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5));
+    anim->setRunState(CCActiveAnimation::Paused, 0.5);
+    EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024));
+    anim->setRunState(CCActiveAnimation::Running, 1024);
+    EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024));
+    EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1024.5));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtZeroIterations)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0));
+    anim->setRunState(CCActiveAnimation::Running, 0);
+    EXPECT_FALSE(anim->isFinishedAt(-1));
+    EXPECT_TRUE(anim->isFinishedAt(0));
+    EXPECT_TRUE(anim->isFinishedAt(1));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtOneIteration)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+    anim->setRunState(CCActiveAnimation::Running, 0);
+    EXPECT_FALSE(anim->isFinishedAt(-1));
+    EXPECT_FALSE(anim->isFinishedAt(0));
+    EXPECT_TRUE(anim->isFinishedAt(1));
+    EXPECT_TRUE(anim->isFinishedAt(2));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtInfiniteIterations)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(-1));
+    anim->setRunState(CCActiveAnimation::Running, 0);
+    EXPECT_FALSE(anim->isFinishedAt(0));
+    EXPECT_FALSE(anim->isFinishedAt(0.5));
+    EXPECT_FALSE(anim->isFinishedAt(1));
+    EXPECT_FALSE(anim->isFinishedAt(1.5));
+}
+
+TEST(CCActiveAnimationTest, IsFinishedAtNotRunning)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0));
+    anim->setRunState(CCActiveAnimation::Running, 0);
+    EXPECT_TRUE(anim->isFinishedAt(0));
+    anim->setRunState(CCActiveAnimation::Paused, 0);
+    EXPECT_FALSE(anim->isFinishedAt(0));
+    anim->setRunState(CCActiveAnimation::WaitingForNextTick, 0);
+    EXPECT_FALSE(anim->isFinishedAt(0));
+    anim->setRunState(CCActiveAnimation::WaitingForTargetAvailability, 0);
+    EXPECT_FALSE(anim->isFinishedAt(0));
+    anim->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+    EXPECT_FALSE(anim->isFinishedAt(0));
+    anim->setRunState(CCActiveAnimation::Finished, 0);
+    EXPECT_TRUE(anim->isFinishedAt(0));
+    anim->setRunState(CCActiveAnimation::Aborted, 0);
+    EXPECT_TRUE(anim->isFinishedAt(0));
+}
+
+TEST(CCActiveAnimationTest, IsFinished)
+{
+    OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1));
+    anim->setRunState(CCActiveAnimation::Running, 0);
+    EXPECT_FALSE(anim->isFinished());
+    anim->setRunState(CCActiveAnimation::Paused, 0);
+    EXPECT_FALSE(anim->isFinished());
+    anim->setRunState(CCActiveAnimation::WaitingForNextTick, 0);
+    EXPECT_FALSE(anim->isFinished());
+    anim->setRunState(CCActiveAnimation::WaitingForTargetAvailability, 0);
+    EXPECT_FALSE(anim->isFinished());
+    anim->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+    EXPECT_FALSE(anim->isFinished());
+    anim->setRunState(CCActiveAnimation::Finished, 0);
+    EXPECT_TRUE(anim->isFinished());
+    anim->setRunState(CCActiveAnimation::Aborted, 0);
+    EXPECT_TRUE(anim->isFinished());
+}
+
+} // namespace WebCore
diff --git a/Source/WebKit/chromium/tests/CCLayerAnimationControllerImplTest.cpp b/Source/WebKit/chromium/tests/CCLayerAnimationControllerImplTest.cpp
new file mode 100644 (file)
index 0000000..3d50d06
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "cc/CCLayerAnimationControllerImpl.h"
+
+#include "TransformOperations.h"
+#include "cc/CCAnimationCurve.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+
+namespace {
+
+class FakeControllerClient : public CCLayerAnimationControllerImplClient {
+public:
+    FakeControllerClient() : m_opacity(0) { }
+    virtual ~FakeControllerClient() { }
+
+    virtual float opacity() const { return m_opacity; }
+    virtual void setOpacity(float opacity) { m_opacity = opacity; }
+    virtual const TransformationMatrix& transform() const { return m_transform; }
+    virtual void setTransform(const TransformationMatrix& transform) { m_transform = transform; }
+    virtual void animationControllerImplDidActivate(CCLayerAnimationControllerImpl* controller)
+    {
+        m_activeControllers.append(controller);
+    }
+
+    Vector<CCLayerAnimationControllerImpl*>& activeControllers() { return m_activeControllers; }
+
+private:
+    float m_opacity;
+    TransformationMatrix m_transform;
+    Vector<CCLayerAnimationControllerImpl*> m_activeControllers;
+};
+
+class FakeTransformTransition : public CCTransformAnimationCurve {
+public:
+    FakeTransformTransition(double duration) : m_duration(duration) { }
+    virtual double duration() const { return m_duration; }
+    virtual TransformOperations getValue(double time) const
+    {
+        return TransformOperations();
+    }
+
+private:
+    double m_duration;
+};
+
+class FakeFloatTransition : public CCFloatAnimationCurve {
+public:
+    FakeFloatTransition(double duration, float from, float to)
+        : m_duration(duration)
+        , m_from(from)
+        , m_to(to)
+    {
+    }
+
+    virtual double duration() const { return m_duration; }
+    virtual float getValue(double time) const
+    {
+        time /= m_duration;
+        if (time >= 1)
+            time = 1;
+        return (1 - time) * m_from + time * m_to;
+    }
+
+private:
+    double m_duration;
+    float m_from;
+    float m_to;
+};
+
+// Tests that transitioning opacity from 0 to 1 works as expected.
+TEST(CCLayerAnimationControllerImplTest, TrivialTransition)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+    controller->add(toAdd.release());
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(1);
+    EXPECT_EQ(1, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests that two queued animations affecting the same property run in sequence.
+TEST(CCLayerAnimationControllerImplTest, TrivialQueuing)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 1, 0.5f)), 2, CCActiveAnimation::Opacity));
+
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(1);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(1, dummy.opacity());
+    controller->animate(2);
+    EXPECT_EQ(0.5f, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests interrupting a transition with another transition.
+TEST(CCLayerAnimationControllerImplTest, Interrupt)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 1, 0.5f)), 2, CCActiveAnimation::Opacity));
+    toAdd->setRunState(CCActiveAnimation::WaitingForNextTick, 0);
+    controller->add(toAdd.release());
+
+    controller->animate(0.5); // second anim starts NOW.
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(1, dummy.opacity());
+    controller->animate(1.5);
+    EXPECT_EQ(0.5f, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling two animations to run together when only one property is free.
+TEST(CCLayerAnimationControllerImplTest, ScheduleTogetherWhenAPropertyIsBlocked)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeTransformTransition(1)), 1, CCActiveAnimation::Transform));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeTransformTransition(1)), 2, CCActiveAnimation::Transform));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 2, CCActiveAnimation::Opacity));
+
+    controller->animate(0);
+    EXPECT_EQ(0, dummy.opacity());
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    controller->animate(1);
+    // Should not have started the float transition yet.
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    // The the float animation should have started at time 1 and should be done.
+    controller->animate(2);
+    EXPECT_EQ(1, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling two animations to run together with different lengths and another
+// animation queued to start when the shorter animation finishes (should wait
+// for both to finish).
+TEST(CCLayerAnimationControllerImplTest, ScheduleTogetherWithAnAnimWaiting)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeTransformTransition(2)), 1, CCActiveAnimation::Transform));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 1, 0.5f)), 2, CCActiveAnimation::Opacity));
+
+    // Anims with id 1 should both start now.
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    // The opacity animation should have finished at time 1, but the group
+    // of animations with id 1 don't finish until time 2 because of the length
+    // of the transform animation.
+    controller->animate(2);
+    // Should not have started the float transition yet.
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(1, dummy.opacity());
+
+    // The the second opacity animation should start at time 2 and should be
+    // done by time 3
+    controller->animate(3);
+    EXPECT_EQ(0.5f, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling an animation to start in the future.
+TEST(CCLayerAnimationControllerImplTest, ScheduleAnimation)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+    toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+    toAdd->setStartTime(1);
+    controller->add(toAdd.release());
+
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(1);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(2);
+    EXPECT_EQ(1, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling an animation to start in the future that's interrupting a running animation.
+TEST(CCLayerAnimationControllerImplTest, ScheduledAnimationInterruptsRunningAnimation)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0.5f, 0)), 2, CCActiveAnimation::Opacity));
+    toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+    toAdd->setStartTime(1);
+    controller->add(toAdd.release());
+
+    // First 2s opacity transition should start immediately.
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(0.5);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.25f, dummy.opacity());
+    controller->animate(1);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.5f, dummy.opacity());
+    controller->animate(2);
+    EXPECT_EQ(0, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Tests scheduling an animation to start in the future that interrupts a running animation
+// and there is yet another animation queued to start later.
+TEST(CCLayerAnimationControllerImplTest, ScheduledAnimationInterruptsRunningAnimationWithAnimInQueue)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(2, 0.5f, 0)), 2, CCActiveAnimation::Opacity));
+    toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0);
+    toAdd->setStartTime(1);
+    controller->add(toAdd.release());
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 0.75f)), 3, CCActiveAnimation::Opacity));
+
+    // First 2s opacity transition should start immediately.
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(0.5);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.25f, dummy.opacity());
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    controller->animate(1);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.5f, dummy.opacity());
+    controller->animate(3);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(4);
+    EXPECT_EQ(0.75f, dummy.opacity());
+    EXPECT_FALSE(controller->hasActiveAnimation());
+}
+
+// Test that a looping animation loops and for the correct number of iterations.
+TEST(CCLayerAnimationControllerImplTest, TrivialLooping)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+    toAdd->setIterations(3);
+    controller->add(toAdd.release());
+
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(1.25);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.25f, dummy.opacity());
+    controller->animate(1.75);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+    controller->animate(2.25);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.25f, dummy.opacity());
+    controller->animate(2.75);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+    controller->animate(3);
+    EXPECT_FALSE(controller->hasActiveAnimation());
+    EXPECT_EQ(1, dummy.opacity());
+
+    // Just be extra sure.
+    controller->animate(4);
+    EXPECT_EQ(1, dummy.opacity());
+}
+
+// Test that an infinitely looping animation does indeed go until aborted.
+TEST(CCLayerAnimationControllerImplTest, InfiniteLooping)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    const int id = 1;
+    OwnPtr<CCActiveAnimation> toAdd(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity));
+    toAdd->setIterations(-1);
+    controller->add(toAdd.release());
+
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(1.25);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.25f, dummy.opacity());
+    controller->animate(1.75);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+
+    controller->animate(1073741824.25);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.25f, dummy.opacity());
+    controller->animate(1073741824.75);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+
+    EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+    controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 0.75f);
+    EXPECT_FALSE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+}
+
+// Test that pausing and resuming work as expected.
+TEST(CCLayerAnimationControllerImplTest, PauseResume)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    const int id = 1;
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity));
+
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(0.5);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.5f, dummy.opacity());
+
+    EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+    controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Paused, 0.5f);
+
+    controller->animate(1024);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.5f, dummy.opacity());
+
+    EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+    controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Running, 1024);
+
+    controller->animate(1024.25);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+    controller->animate(1024.5);
+    EXPECT_FALSE(controller->hasActiveAnimation());
+    EXPECT_EQ(1, dummy.opacity());
+}
+
+TEST(CCLayerAnimationControllerImplTest, AbortAGroupedAnimation)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    const int id = 1;
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeTransformTransition(1)), id, CCActiveAnimation::Transform));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(2, 0, 1)), id, CCActiveAnimation::Opacity));
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 1, 0.75f)), 2, CCActiveAnimation::Opacity));
+
+    controller->animate(0);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0, dummy.opacity());
+    controller->animate(1);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(0.5f, dummy.opacity());
+
+    EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity));
+    controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 1);
+    controller->animate(1);
+    EXPECT_TRUE(controller->hasActiveAnimation());
+    EXPECT_EQ(1, dummy.opacity());
+    controller->animate(2);
+    EXPECT_TRUE(!controller->hasActiveAnimation());
+    EXPECT_EQ(0.75f, dummy.opacity());
+}
+
+// Tests that adding an animation to the controller calls the appropriate callback on the controller client
+// (in this case, adding the controller to the list of active controller).
+TEST(CCLayerAnimationControllerImplTest, DidActivate)
+{
+    FakeControllerClient dummy;
+    OwnPtr<CCLayerAnimationControllerImpl> controller(
+        CCLayerAnimationControllerImpl::create(&dummy));
+
+    EXPECT_EQ(size_t(0), dummy.activeControllers().size());
+
+    controller->add(CCActiveAnimation::create(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity));
+
+    EXPECT_EQ(size_t(1), dummy.activeControllers().size());
+    EXPECT_EQ(controller.get(), dummy.activeControllers()[0]);
+}
+
+} // namespace