1/*
2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "AnimationBase.h"
31
32#include "AnimationControllerPrivate.h"
33#include "CSSMutableStyleDeclaration.h"
34#include "CSSPropertyLonghand.h"
35#include "CSSPropertyNames.h"
36#include "CompositeAnimation.h"
37#include "Document.h"
38#include "EventNames.h"
39#include "FloatConversion.h"
40#include "Frame.h"
41#include "IdentityTransformOperation.h"
42#include "ImplicitAnimation.h"
43#include "KeyframeAnimation.h"
44#include "MatrixTransformOperation.h"
45#include "Matrix3DTransformOperation.h"
46#include "RenderBox.h"
47#include "RenderLayer.h"
48#include "RenderLayerBacking.h"
49#include "RenderStyle.h"
50#include "UnitBezier.h"
51#include <algorithm>
52#include <wtf/CurrentTime.h>
53
54using namespace std;
55
56namespace WebCore {
57
58// The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the
59// animation, the more precision we need in the timing function result to avoid ugly discontinuities.
60static inline double solveEpsilon(double duration)
61{
62    return 1.0 / (200.0 * duration);
63}
64
65static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
66{
67    // Convert from input time to parametric value in curve, then from
68    // that to output time.
69    UnitBezier bezier(p1x, p1y, p2x, p2y);
70    return bezier.solve(t, solveEpsilon(duration));
71}
72
73static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
74{
75    if (stepAtStart)
76        return min(1.0, (floor(numSteps * t) + 1) / numSteps);
77    return floor(numSteps * t) / numSteps;
78}
79
80static inline int blendFunc(const AnimationBase*, int from, int to, double progress)
81{
82    return int(from + (to - from) * progress);
83}
84
85static inline double blendFunc(const AnimationBase*, double from, double to, double progress)
86{
87    return from + (to - from) * progress;
88}
89
90static inline float blendFunc(const AnimationBase*, float from, float to, double progress)
91{
92    return narrowPrecisionToFloat(from + (to - from) * progress);
93}
94
95static inline Color blendFunc(const AnimationBase* anim, const Color& from, const Color& to, double progress)
96{
97    // We need to preserve the state of the valid flag at the end of the animation
98    if (progress == 1 && !to.isValid())
99        return Color();
100
101    // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor().
102    // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that.
103    Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0;
104    Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0;
105
106    Color premultBlended(blendFunc(anim, premultFrom.red(), premultTo.red(), progress),
107                 blendFunc(anim, premultFrom.green(), premultTo.green(), progress),
108                 blendFunc(anim, premultFrom.blue(), premultTo.blue(), progress),
109                 blendFunc(anim, premultFrom.alpha(), premultTo.alpha(), progress));
110
111    return Color(colorFromPremultipliedARGB(premultBlended.rgb()));
112}
113
114static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress)
115{
116    return to.blend(from, narrowPrecisionToFloat(progress));
117}
118
119static inline LengthSize blendFunc(const AnimationBase* anim, const LengthSize& from, const LengthSize& to, double progress)
120{
121    return LengthSize(blendFunc(anim, from.width(), to.width(), progress),
122                      blendFunc(anim, from.height(), to.height(), progress));
123}
124
125static inline IntSize blendFunc(const AnimationBase* anim, const IntSize& from, const IntSize& to, double progress)
126{
127    return IntSize(blendFunc(anim, from.width(), to.width(), progress),
128                   blendFunc(anim, from.height(), to.height(), progress));
129}
130
131static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, ShadowStyle to, double progress)
132{
133    if (from == to)
134        return to;
135
136    double fromVal = from == Normal ? 1 : 0;
137    double toVal = to == Normal ? 1 : 0;
138    double result = blendFunc(anim, fromVal, toVal, progress);
139    return result > 0 ? Normal : Inset;
140}
141
142static inline ShadowData* blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress)
143{
144    ASSERT(from && to);
145    if (from->style() != to->style())
146        return new ShadowData(*to);
147
148    return new ShadowData(blendFunc(anim, from->x(), to->x(), progress),
149                          blendFunc(anim, from->y(), to->y(), progress),
150                          blendFunc(anim, from->blur(), to->blur(), progress),
151                          blendFunc(anim, from->spread(), to->spread(), progress),
152                          blendFunc(anim, from->style(), to->style(), progress),
153                          from->isWebkitBoxShadow(),
154                          blendFunc(anim, from->color(), to->color(), progress));
155}
156
157static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress)
158{
159    TransformOperations result;
160
161    // If we have a transform function list, use that to do a per-function animation. Otherwise do a Matrix animation
162    if (anim->isTransformFunctionListValid()) {
163        unsigned fromSize = from.operations().size();
164        unsigned toSize = to.operations().size();
165        unsigned size = max(fromSize, toSize);
166        for (unsigned i = 0; i < size; i++) {
167            RefPtr<TransformOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
168            RefPtr<TransformOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
169            RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp.get(), progress) : (fromOp ? fromOp->blend(0, progress, true) : 0);
170            if (blendedOp)
171                result.operations().append(blendedOp);
172            else {
173                RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create();
174                if (progress > 0.5)
175                    result.operations().append(toOp ? toOp : identityOp);
176                else
177                    result.operations().append(fromOp ? fromOp : identityOp);
178            }
179        }
180    } else {
181        // Convert the TransformOperations into matrices
182        IntSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : IntSize();
183        TransformationMatrix fromT;
184        TransformationMatrix toT;
185        from.apply(size, fromT);
186        to.apply(size, toT);
187
188        toT.blend(fromT, progress);
189
190        // Append the result
191        result.operations().append(Matrix3DTransformOperation::create(toT));
192    }
193    return result;
194}
195
196static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress)
197{
198    // Any non-zero result means we consider the object to be visible.  Only at 0 do we consider the object to be
199    // invisible.   The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values.
200    double fromVal = from == VISIBLE ? 1. : 0.;
201    double toVal = to == VISIBLE ? 1. : 0.;
202    if (fromVal == toVal)
203        return to;
204    double result = blendFunc(anim, fromVal, toVal, progress);
205    return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
206}
207
208static inline LengthBox blendFunc(const AnimationBase* anim, const LengthBox& from, const LengthBox& to, double progress)
209{
210    // Length types have to match to animate
211    if (from.top().type() != to.top().type()
212        || from.right().type() != to.right().type()
213        || from.bottom().type() != to.bottom().type()
214        || from.left().type() != to.left().type())
215        return to;
216
217    LengthBox result(blendFunc(anim, from.top(), to.top(), progress),
218                     blendFunc(anim, from.right(), to.right(), progress),
219                     blendFunc(anim, from.bottom(), to.bottom(), progress),
220                     blendFunc(anim, from.left(), to.left(), progress));
221    return result;
222}
223
224class PropertyWrapperBase;
225
226static void addShorthandProperties();
227static PropertyWrapperBase* wrapperForProperty(int propertyID);
228
229class PropertyWrapperBase {
230    WTF_MAKE_NONCOPYABLE(PropertyWrapperBase); WTF_MAKE_FAST_ALLOCATED;
231public:
232    PropertyWrapperBase(int prop)
233        : m_prop(prop)
234    {
235    }
236
237    virtual ~PropertyWrapperBase() { }
238
239    virtual bool isShorthandWrapper() const { return false; }
240    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0;
241    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const = 0;
242
243    int property() const { return m_prop; }
244
245#if USE(ACCELERATED_COMPOSITING)
246    virtual bool animationIsAccelerated() const { return false; }
247#endif
248
249private:
250    int m_prop;
251};
252
253template <typename T>
254class PropertyWrapperGetter : public PropertyWrapperBase {
255public:
256    PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const)
257        : PropertyWrapperBase(prop)
258        , m_getter(getter)
259    {
260    }
261
262    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
263    {
264       // If the style pointers are the same, don't bother doing the test.
265       // If either is null, return false. If both are null, return true.
266       if ((!a && !b) || a == b)
267           return true;
268       if (!a || !b)
269            return false;
270        return (a->*m_getter)() == (b->*m_getter)();
271    }
272
273protected:
274    T (RenderStyle::*m_getter)() const;
275};
276
277template <typename T>
278class PropertyWrapper : public PropertyWrapperGetter<T> {
279public:
280    PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
281        : PropertyWrapperGetter<T>(prop, getter)
282        , m_setter(setter)
283    {
284    }
285
286    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
287    {
288        (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress));
289    }
290
291protected:
292    void (RenderStyle::*m_setter)(T);
293};
294
295#if USE(ACCELERATED_COMPOSITING)
296class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> {
297public:
298    PropertyWrapperAcceleratedOpacity()
299        : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)
300    {
301    }
302
303    virtual bool animationIsAccelerated() const { return true; }
304
305    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
306    {
307        float fromOpacity = a->opacity();
308
309        // This makes sure we put the object being animated into a RenderLayer during the animation
310        dst->setOpacity(blendFunc(anim, (fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), progress));
311    }
312};
313
314class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> {
315public:
316    PropertyWrapperAcceleratedTransform()
317        : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform)
318    {
319    }
320
321    virtual bool animationIsAccelerated() const { return true; }
322
323    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
324    {
325        dst->setTransform(blendFunc(anim, a->transform(), b->transform(), progress));
326    }
327};
328#endif // USE(ACCELERATED_COMPOSITING)
329
330class PropertyWrapperShadow : public PropertyWrapperBase {
331public:
332    PropertyWrapperShadow(int prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowData*, bool))
333        : PropertyWrapperBase(prop)
334        , m_getter(getter)
335        , m_setter(setter)
336    {
337    }
338
339    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
340    {
341        const ShadowData* shadowA = (a->*m_getter)();
342        const ShadowData* shadowB = (b->*m_getter)();
343
344        while (true) {
345            if (!shadowA && !shadowB)   // end of both lists
346                return true;
347
348            if (!shadowA || !shadowB)   // end of just one of the lists
349                return false;
350
351            if (*shadowA != *shadowB)
352                return false;
353
354            shadowA = shadowA->next();
355            shadowB = shadowB->next();
356        }
357
358        return true;
359    }
360
361    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
362    {
363        const ShadowData* shadowA = (a->*m_getter)();
364        const ShadowData* shadowB = (b->*m_getter)();
365        ShadowData defaultShadowData(0, 0, 0, 0, Normal, property() == CSSPropertyWebkitBoxShadow, Color::transparent);
366        ShadowData defaultInsetShadowData(0, 0, 0, 0, Inset, property() == CSSPropertyWebkitBoxShadow, Color::transparent);
367
368        ShadowData* newShadowData = 0;
369        ShadowData* lastShadow = 0;
370
371        while (shadowA || shadowB) {
372            const ShadowData* srcShadow = shadowA ? shadowA : (shadowB->style() == Inset ? &defaultInsetShadowData : &defaultShadowData);
373            const ShadowData* dstShadow = shadowB ? shadowB : (shadowA->style() == Inset ? &defaultInsetShadowData : &defaultShadowData);
374
375            ShadowData* blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress);
376            if (!lastShadow)
377                newShadowData = blendedShadow;
378            else
379                lastShadow->setNext(blendedShadow);
380
381            lastShadow = blendedShadow;
382
383            shadowA = shadowA ? shadowA->next() : 0;
384            shadowB = shadowB ? shadowB->next() : 0;
385        }
386
387        (dst->*m_setter)(newShadowData, false);
388    }
389
390private:
391    const ShadowData* (RenderStyle::*m_getter)() const;
392    void (RenderStyle::*m_setter)(ShadowData*, bool);
393};
394
395class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase {
396public:
397    PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
398        : PropertyWrapperBase(prop)
399        , m_getter(getter)
400        , m_setter(setter)
401    {
402    }
403
404    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
405    {
406        Color fromColor = (a->*m_getter)();
407        Color toColor = (b->*m_getter)();
408
409        if (!fromColor.isValid() && !toColor.isValid())
410            return true;
411
412        if (!fromColor.isValid())
413            fromColor = a->color();
414        if (!toColor.isValid())
415            toColor = b->color();
416
417        return fromColor == toColor;
418    }
419
420    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
421    {
422        Color fromColor = (a->*m_getter)();
423        Color toColor = (b->*m_getter)();
424
425        if (!fromColor.isValid() && !toColor.isValid())
426            return;
427
428        if (!fromColor.isValid())
429            fromColor = a->color();
430        if (!toColor.isValid())
431            toColor = b->color();
432        (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
433    }
434
435private:
436    const Color& (RenderStyle::*m_getter)() const;
437    void (RenderStyle::*m_setter)(const Color&);
438};
439
440// Wrapper base class for an animatable property in a FillLayer
441class FillLayerPropertyWrapperBase {
442public:
443    FillLayerPropertyWrapperBase()
444    {
445    }
446
447    virtual ~FillLayerPropertyWrapperBase() { }
448
449    virtual bool equals(const FillLayer* a, const FillLayer* b) const = 0;
450    virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const = 0;
451};
452
453template <typename T>
454class FillLayerPropertyWrapperGetter : public FillLayerPropertyWrapperBase {
455    WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter);
456public:
457    FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const)
458        : m_getter(getter)
459    {
460    }
461
462    virtual bool equals(const FillLayer* a, const FillLayer* b) const
463    {
464       // If the style pointers are the same, don't bother doing the test.
465       // If either is null, return false. If both are null, return true.
466       if ((!a && !b) || a == b)
467           return true;
468       if (!a || !b)
469            return false;
470        return (a->*m_getter)() == (b->*m_getter)();
471    }
472
473protected:
474    T (FillLayer::*m_getter)() const;
475};
476
477template <typename T>
478class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<T> {
479public:
480    FillLayerPropertyWrapper(T (FillLayer::*getter)() const, void (FillLayer::*setter)(T))
481        : FillLayerPropertyWrapperGetter<T>(getter)
482        , m_setter(setter)
483    {
484    }
485
486    virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const
487    {
488        (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T>::m_getter)(), progress));
489    }
490
491protected:
492    void (FillLayer::*m_setter)(T);
493};
494
495
496class FillLayersPropertyWrapper : public PropertyWrapperBase {
497public:
498    typedef const FillLayer* (RenderStyle::*LayersGetter)() const;
499    typedef FillLayer* (RenderStyle::*LayersAccessor)();
500
501    FillLayersPropertyWrapper(int prop, LayersGetter getter, LayersAccessor accessor)
502        : PropertyWrapperBase(prop)
503        , m_layersGetter(getter)
504        , m_layersAccessor(accessor)
505    {
506        switch (prop) {
507            case CSSPropertyBackgroundPositionX:
508            case CSSPropertyWebkitMaskPositionX:
509                m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::xPosition, &FillLayer::setXPosition);
510                break;
511            case CSSPropertyBackgroundPositionY:
512            case CSSPropertyWebkitMaskPositionY:
513                m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::yPosition, &FillLayer::setYPosition);
514                break;
515            case CSSPropertyBackgroundSize:
516            case CSSPropertyWebkitBackgroundSize:
517            case CSSPropertyWebkitMaskSize:
518                m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<LengthSize>(&FillLayer::sizeLength, &FillLayer::setSizeLength);
519                break;
520        }
521    }
522
523    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
524    {
525        const FillLayer* fromLayer = (a->*m_layersGetter)();
526        const FillLayer* toLayer = (b->*m_layersGetter)();
527
528        while (fromLayer && toLayer) {
529            if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer))
530                return false;
531
532            fromLayer = fromLayer->next();
533            toLayer = toLayer->next();
534        }
535
536        return true;
537    }
538
539    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
540    {
541        const FillLayer* aLayer = (a->*m_layersGetter)();
542        const FillLayer* bLayer = (b->*m_layersGetter)();
543        FillLayer* dstLayer = (dst->*m_layersAccessor)();
544
545        while (aLayer && bLayer && dstLayer) {
546            m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress);
547            aLayer = aLayer->next();
548            bLayer = bLayer->next();
549            dstLayer = dstLayer->next();
550        }
551    }
552
553private:
554    FillLayerPropertyWrapperBase* m_fillLayerPropertyWrapper;
555
556    LayersGetter m_layersGetter;
557    LayersAccessor m_layersAccessor;
558};
559
560class ShorthandPropertyWrapper : public PropertyWrapperBase {
561public:
562    ShorthandPropertyWrapper(int property, const CSSPropertyLonghand& longhand)
563        : PropertyWrapperBase(property)
564    {
565        for (unsigned i = 0; i < longhand.length(); ++i) {
566            PropertyWrapperBase* wrapper = wrapperForProperty(longhand.properties()[i]);
567            if (wrapper)
568                m_propertyWrappers.append(wrapper);
569        }
570    }
571
572    virtual bool isShorthandWrapper() const { return true; }
573
574    virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
575    {
576        Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
577        for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) {
578            if (!(*it)->equals(a, b))
579                return false;
580        }
581        return true;
582    }
583
584    virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
585    {
586        Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
587        for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it)
588            (*it)->blend(anim, dst, a, b, progress);
589    }
590
591    const Vector<PropertyWrapperBase*> propertyWrappers() const { return m_propertyWrappers; }
592
593private:
594    Vector<PropertyWrapperBase*> m_propertyWrappers;
595};
596
597
598static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0;
599static int gPropertyWrapperMap[numCSSProperties];
600
601static const int cInvalidPropertyWrapperIndex = -1;
602
603
604void AnimationBase::ensurePropertyMap()
605{
606    // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed?
607    if (gPropertyWrappers == 0) {
608        gPropertyWrappers = new Vector<PropertyWrapperBase*>();
609
610        // build the list of property wrappers to do the comparisons and blends
611        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft));
612        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight));
613        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop));
614        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom));
615
616        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth));
617        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth));
618        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth));
619
620        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight));
621        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight));
622        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight));
623
624        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth));
625        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth));
626        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth));
627        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth));
628        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft));
629        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight));
630        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop));
631        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom));
632        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft));
633        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight));
634        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop));
635        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom));
636        gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor));
637
638        gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor));
639
640        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
641        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
642        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
643        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
644
645        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers));
646        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers));
647        gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers));
648
649        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyFontSize, &RenderStyle::fontSize, &RenderStyle::setBlendedFontSize));
650        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth));
651        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap));
652        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount));
653        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth));
654        gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing));
655        gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing));
656        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex));
657        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::lineHeight, &RenderStyle::setLineHeight));
658        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset));
659        gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth));
660        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing));
661        gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing));
662        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent));
663
664        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective));
665        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX));
666        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY));
667        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX));
668        gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY));
669        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ));
670        gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius));
671        gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius));
672        gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius));
673        gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius));
674        gPropertyWrappers->append(new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility));
675        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoom));
676
677        gPropertyWrappers->append(new PropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip));
678
679#if USE(ACCELERATED_COMPOSITING)
680        gPropertyWrappers->append(new PropertyWrapperAcceleratedOpacity());
681        gPropertyWrappers->append(new PropertyWrapperAcceleratedTransform());
682#else
683        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity));
684        gPropertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform));
685#endif
686
687        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitColumnRuleColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor));
688        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextStrokeColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor));
689        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextFillColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor));
690        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderLeftColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor));
691        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderRightColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor));
692        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderTopColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor));
693        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderBottomColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor));
694        gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyOutlineColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor));
695
696        gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
697        gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
698        gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow));
699
700#if ENABLE(SVG)
701        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity));
702        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity));
703        gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity));
704#endif
705
706        // TODO:
707        //
708        //  CSSPropertyVerticalAlign
709        //
710        // Compound properties that have components that should be animatable:
711        //
712        //  CSSPropertyWebkitColumns
713        //  CSSPropertyWebkitBoxReflect
714
715        // Make sure unused slots have a value
716        for (unsigned int i = 0; i < static_cast<unsigned int>(numCSSProperties); ++i)
717            gPropertyWrapperMap[i] = cInvalidPropertyWrapperIndex;
718
719        // First we put the non-shorthand property wrappers into the map, so the shorthand-building
720        // code can find them.
721        size_t n = gPropertyWrappers->size();
722        for (unsigned int i = 0; i < n; ++i) {
723            ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties);
724            gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i;
725        }
726
727        // Now add the shorthand wrappers.
728        addShorthandProperties();
729    }
730}
731
732static void addPropertyWrapper(int propertyID, PropertyWrapperBase* wrapper)
733{
734    int propIndex = propertyID - firstCSSProperty;
735
736    ASSERT(gPropertyWrapperMap[propIndex] == cInvalidPropertyWrapperIndex);
737
738    unsigned wrapperIndex = gPropertyWrappers->size();
739    gPropertyWrappers->append(wrapper);
740    gPropertyWrapperMap[propIndex] = wrapperIndex;
741}
742
743static void addShorthandProperties()
744{
745    static const int animatableShorthandProperties[] = {
746        CSSPropertyBackground,      // for background-color, background-position
747        CSSPropertyBackgroundPosition,
748        CSSPropertyWebkitMask,      // for mask-position
749        CSSPropertyWebkitMaskPosition,
750        CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft,
751        CSSPropertyBorderColor,
752        CSSPropertyBorderRadius,
753        CSSPropertyBorderWidth,
754        CSSPropertyBorder,
755        CSSPropertyBorderSpacing,
756        CSSPropertyMargin,
757        CSSPropertyOutline,
758        CSSPropertyPadding,
759        CSSPropertyWebkitTextStroke,
760        CSSPropertyWebkitColumnRule,
761        CSSPropertyWebkitBorderRadius,
762        CSSPropertyWebkitTransformOrigin
763    };
764
765    for (size_t i = 0; i < WTF_ARRAY_LENGTH(animatableShorthandProperties); ++i) {
766        int propertyID = animatableShorthandProperties[i];
767        CSSPropertyLonghand longhand = longhandForProperty(propertyID);
768        if (longhand.length() > 0)
769            addPropertyWrapper(propertyID, new ShorthandPropertyWrapper(propertyID, longhand));
770    }
771
772    // 'font' is not in the shorthand map.
773    static const int animatableFontProperties[] = {
774        CSSPropertyFontSize,
775        CSSPropertyFontWeight
776    };
777
778    CSSPropertyLonghand fontLonghand(animatableFontProperties, WTF_ARRAY_LENGTH(animatableFontProperties));
779    addPropertyWrapper(CSSPropertyFont, new ShorthandPropertyWrapper(CSSPropertyFont, fontLonghand));
780}
781
782static PropertyWrapperBase* wrapperForProperty(int propertyID)
783{
784    int propIndex = propertyID - firstCSSProperty;
785    if (propIndex >= 0 && propIndex < numCSSProperties) {
786        int wrapperIndex = gPropertyWrapperMap[propIndex];
787        if (wrapperIndex >= 0)
788            return (*gPropertyWrappers)[wrapperIndex];
789    }
790    return 0;
791}
792
793AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim)
794    : m_animState(AnimationStateNew)
795    , m_isAnimating(false)
796    , m_startTime(0)
797    , m_pauseTime(-1)
798    , m_requestedStartTime(0)
799    , m_object(renderer)
800    , m_animation(const_cast<Animation*>(transition))
801    , m_compAnim(compAnim)
802    , m_isAccelerated(false)
803    , m_transformFunctionListValid(false)
804    , m_nextIterationDuration(-1)
805{
806    // Compute the total duration
807    m_totalDuration = -1;
808    if (m_animation->iterationCount() > 0)
809        m_totalDuration = m_animation->duration() * m_animation->iterationCount();
810}
811
812bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b)
813{
814    ensurePropertyMap();
815    if (prop == cAnimateAll) {
816        size_t n = gPropertyWrappers->size();
817        for (unsigned int i = 0; i < n; ++i) {
818            PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
819            // No point comparing shorthand wrappers for 'all'.
820            if (!wrapper->isShorthandWrapper() && !wrapper->equals(a, b))
821                return false;
822        }
823    } else {
824        PropertyWrapperBase* wrapper = wrapperForProperty(prop);
825        if (wrapper)
826            return wrapper->equals(a, b);
827    }
828    return true;
829}
830
831int AnimationBase::getPropertyAtIndex(int i, bool& isShorthand)
832{
833    ensurePropertyMap();
834    if (i < 0 || i >= static_cast<int>(gPropertyWrappers->size()))
835        return CSSPropertyInvalid;
836
837    PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
838    isShorthand = wrapper->isShorthandWrapper();
839    return wrapper->property();
840}
841
842int AnimationBase::getNumProperties()
843{
844    ensurePropertyMap();
845    return gPropertyWrappers->size();
846}
847
848// Returns true if we need to start animation timers
849bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress)
850{
851    ASSERT(prop != cAnimateAll);
852
853    ensurePropertyMap();
854    PropertyWrapperBase* wrapper = wrapperForProperty(prop);
855    if (wrapper) {
856        wrapper->blend(anim, dst, a, b, progress);
857#if USE(ACCELERATED_COMPOSITING)
858        return !wrapper->animationIsAccelerated() || !anim->isAccelerated();
859#else
860        return true;
861#endif
862    }
863
864    return false;
865}
866
867#if USE(ACCELERATED_COMPOSITING)
868bool AnimationBase::animationOfPropertyIsAccelerated(int prop)
869{
870    ensurePropertyMap();
871    PropertyWrapperBase* wrapper = wrapperForProperty(prop);
872    return wrapper ? wrapper->animationIsAccelerated() : false;
873}
874#endif
875
876static bool gatherEnclosingShorthandProperties(int property, PropertyWrapperBase* wrapper, HashSet<int>& propertySet)
877{
878    if (!wrapper->isShorthandWrapper())
879        return false;
880
881    ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper);
882
883    bool contained = false;
884    for (size_t i = 0; i < shorthandWrapper->propertyWrappers().size(); ++i) {
885        PropertyWrapperBase* currWrapper = shorthandWrapper->propertyWrappers()[i];
886
887        if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property)
888            contained = true;
889    }
890
891    if (contained)
892        propertySet.add(wrapper->property());
893
894    return contained;
895}
896
897// Note: this is inefficient. It's only called from pauseTransitionAtTime().
898HashSet<int> AnimationBase::animatableShorthandsAffectingProperty(int property)
899{
900    ensurePropertyMap();
901
902    HashSet<int> foundProperties;
903    for (int i = 0; i < getNumProperties(); ++i)
904        gatherEnclosingShorthandProperties(property, (*gPropertyWrappers)[i], foundProperties);
905
906    return foundProperties;
907}
908
909void AnimationBase::setNeedsStyleRecalc(Node* node)
910{
911    ASSERT(!node || (node->document() && !node->document()->inPageCache()));
912    if (node)
913        node->setNeedsStyleRecalc(SyntheticStyleChange);
914}
915
916double AnimationBase::duration() const
917{
918    return m_animation->duration();
919}
920
921bool AnimationBase::playStatePlaying() const
922{
923    return m_animation->playState() == AnimPlayStatePlaying;
924}
925
926bool AnimationBase::animationsMatch(const Animation* anim) const
927{
928    return m_animation->animationsMatch(anim);
929}
930
931void AnimationBase::updateStateMachine(AnimStateInput input, double param)
932{
933    if (!m_compAnim)
934        return;
935
936    // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state.
937    if (input == AnimationStateInputMakeNew) {
938        if (m_animState == AnimationStateStartWaitStyleAvailable)
939            m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
940        m_animState = AnimationStateNew;
941        m_startTime = 0;
942        m_pauseTime = -1;
943        m_requestedStartTime = 0;
944        m_nextIterationDuration = -1;
945        endAnimation();
946        return;
947    }
948
949    if (input == AnimationStateInputRestartAnimation) {
950        if (m_animState == AnimationStateStartWaitStyleAvailable)
951            m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
952        m_animState = AnimationStateNew;
953        m_startTime = 0;
954        m_pauseTime = -1;
955        m_requestedStartTime = 0;
956        m_nextIterationDuration = -1;
957        endAnimation();
958
959        if (!paused())
960            updateStateMachine(AnimationStateInputStartAnimation, -1);
961        return;
962    }
963
964    if (input == AnimationStateInputEndAnimation) {
965        if (m_animState == AnimationStateStartWaitStyleAvailable)
966            m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
967        m_animState = AnimationStateDone;
968        endAnimation();
969        return;
970    }
971
972    if (input == AnimationStateInputPauseOverride) {
973        if (m_animState == AnimationStateStartWaitResponse) {
974            // If we are in AnimationStateStartWaitResponse, the animation will get canceled before
975            // we get a response, so move to the next state.
976            endAnimation();
977            updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
978        }
979        return;
980    }
981
982    if (input == AnimationStateInputResumeOverride) {
983        if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) {
984            // Start the animation
985            startAnimation(beginAnimationUpdateTime() - m_startTime);
986        }
987        return;
988    }
989
990    // Execute state machine
991    switch (m_animState) {
992        case AnimationStateNew:
993            ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused);
994            if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) {
995                m_requestedStartTime = beginAnimationUpdateTime();
996                m_animState = AnimationStateStartWaitTimer;
997            }
998            break;
999        case AnimationStateStartWaitTimer:
1000            ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused);
1001
1002            if (input == AnimationStateInputStartTimerFired) {
1003                ASSERT(param >= 0);
1004                // Start timer has fired, tell the animation to start and wait for it to respond with start time
1005                m_animState = AnimationStateStartWaitStyleAvailable;
1006                m_compAnim->animationController()->addToAnimationsWaitingForStyle(this);
1007
1008                // Trigger a render so we can start the animation
1009                if (m_object)
1010                    m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
1011            } else {
1012                ASSERT(!paused());
1013                // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
1014                m_pauseTime = beginAnimationUpdateTime();
1015                m_animState = AnimationStatePausedWaitTimer;
1016            }
1017            break;
1018        case AnimationStateStartWaitStyleAvailable:
1019            ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused);
1020
1021            if (input == AnimationStateInputStyleAvailable) {
1022                // Start timer has fired, tell the animation to start and wait for it to respond with start time
1023                m_animState = AnimationStateStartWaitResponse;
1024
1025                overrideAnimations();
1026
1027                // Start the animation
1028                if (overridden()) {
1029                    // We won't try to start accelerated animations if we are overridden and
1030                    // just move on to the next state.
1031                    m_animState = AnimationStateStartWaitResponse;
1032                    m_isAccelerated = false;
1033                    updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
1034                } else {
1035                    double timeOffset = 0;
1036                    // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
1037                    if (m_animation->delay() < 0)
1038                        timeOffset = -m_animation->delay();
1039                    bool started = startAnimation(timeOffset);
1040
1041                    m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
1042                    m_isAccelerated = started;
1043                }
1044            } else {
1045                // We're waiting for the style to be available and we got a pause. Pause and wait
1046                m_pauseTime = beginAnimationUpdateTime();
1047                m_animState = AnimationStatePausedWaitStyleAvailable;
1048            }
1049            break;
1050        case AnimationStateStartWaitResponse:
1051            ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused);
1052
1053            if (input == AnimationStateInputStartTimeSet) {
1054                ASSERT(param >= 0);
1055                // We have a start time, set it, unless the startTime is already set
1056                if (m_startTime <= 0) {
1057                    m_startTime = param;
1058                    // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
1059                    if (m_animation->delay() < 0)
1060                        m_startTime += m_animation->delay();
1061                }
1062
1063                // Now that we know the start time, fire the start event.
1064                onAnimationStart(0); // The elapsedTime is 0.
1065
1066                // Decide whether to go into looping or ending state
1067                goIntoEndingOrLoopingState();
1068
1069                // Dispatch updateStyleIfNeeded so we can start the animation
1070                if (m_object)
1071                    m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
1072            } else {
1073                // We are pausing while waiting for a start response. Cancel the animation and wait. When
1074                // we unpause, we will act as though the start timer just fired
1075                m_pauseTime = beginAnimationUpdateTime();
1076                pauseAnimation(beginAnimationUpdateTime() - m_startTime);
1077                m_animState = AnimationStatePausedWaitResponse;
1078            }
1079            break;
1080        case AnimationStateLooping:
1081            ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused);
1082
1083            if (input == AnimationStateInputLoopTimerFired) {
1084                ASSERT(param >= 0);
1085                // Loop timer fired, loop again or end.
1086                onAnimationIteration(param);
1087
1088                // Decide whether to go into looping or ending state
1089                goIntoEndingOrLoopingState();
1090            } else {
1091                // We are pausing while running. Cancel the animation and wait
1092                m_pauseTime = beginAnimationUpdateTime();
1093                pauseAnimation(beginAnimationUpdateTime() - m_startTime);
1094                m_animState = AnimationStatePausedRun;
1095            }
1096            break;
1097        case AnimationStateEnding:
1098            ASSERT(input == AnimationStateInputEndTimerFired || input == AnimationStateInputPlayStatePaused);
1099
1100            if (input == AnimationStateInputEndTimerFired) {
1101
1102                ASSERT(param >= 0);
1103                // End timer fired, finish up
1104                onAnimationEnd(param);
1105
1106                m_animState = AnimationStateDone;
1107
1108                if (m_object) {
1109                    if (m_animation->fillsForwards())
1110                        m_animState = AnimationStateFillingForwards;
1111                    else
1112                        resumeOverriddenAnimations();
1113
1114                    // Fire off another style change so we can set the final value
1115                    m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
1116                }
1117            } else {
1118                // We are pausing while running. Cancel the animation and wait
1119                m_pauseTime = beginAnimationUpdateTime();
1120                pauseAnimation(beginAnimationUpdateTime() - m_startTime);
1121                m_animState = AnimationStatePausedRun;
1122            }
1123            // |this| may be deleted here
1124            break;
1125        case AnimationStatePausedWaitTimer:
1126            ASSERT(input == AnimationStateInputPlayStateRunning);
1127            ASSERT(paused());
1128            // Update the times
1129            m_startTime += beginAnimationUpdateTime() - m_pauseTime;
1130            m_pauseTime = -1;
1131
1132            // we were waiting for the start timer to fire, go back and wait again
1133            m_animState = AnimationStateNew;
1134            updateStateMachine(AnimationStateInputStartAnimation, 0);
1135            break;
1136        case AnimationStatePausedWaitResponse:
1137        case AnimationStatePausedWaitStyleAvailable:
1138        case AnimationStatePausedRun:
1139            // We treat these two cases the same. The only difference is that, when we are in
1140            // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
1141            // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
1142            // that we have already set the startTime and will ignore it.
1143            ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable);
1144            ASSERT(paused());
1145
1146            if (input == AnimationStateInputPlayStateRunning) {
1147                // Update the times
1148                if (m_animState == AnimationStatePausedRun)
1149                    m_startTime += beginAnimationUpdateTime() - m_pauseTime;
1150                else
1151                    m_startTime = 0;
1152                m_pauseTime = -1;
1153
1154                if (m_animState == AnimationStatePausedWaitStyleAvailable)
1155                    m_animState = AnimationStateStartWaitStyleAvailable;
1156                else {
1157                    // We were either running or waiting for a begin time response from the animation.
1158                    // Either way we need to restart the animation (possibly with an offset if we
1159                    // had already been running) and wait for it to start.
1160                    m_animState = AnimationStateStartWaitResponse;
1161
1162                    // Start the animation
1163                    if (overridden()) {
1164                        // We won't try to start accelerated animations if we are overridden and
1165                        // just move on to the next state.
1166                        updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
1167                        m_isAccelerated = true;
1168                    } else {
1169                        bool started = startAnimation(beginAnimationUpdateTime() - m_startTime);
1170                        m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
1171                        m_isAccelerated = started;
1172                    }
1173                }
1174                break;
1175            }
1176
1177            if (input == AnimationStateInputStartTimeSet) {
1178                ASSERT(m_animState == AnimationStatePausedWaitResponse);
1179
1180                // We are paused but we got the callback that notifies us that an accelerated animation started.
1181                // We ignore the start time and just move into the paused-run state.
1182                m_animState = AnimationStatePausedRun;
1183                ASSERT(m_startTime == 0);
1184                m_startTime = param;
1185                m_pauseTime += m_startTime;
1186                break;
1187            }
1188
1189            ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable);
1190            // We are paused but we got the callback that notifies us that style has been updated.
1191            // We move to the AnimationStatePausedWaitResponse state
1192            m_animState = AnimationStatePausedWaitResponse;
1193            overrideAnimations();
1194            break;
1195        case AnimationStateFillingForwards:
1196        case AnimationStateDone:
1197            // We're done. Stay in this state until we are deleted
1198            break;
1199    }
1200}
1201
1202void AnimationBase::fireAnimationEventsIfNeeded()
1203{
1204    if (!m_compAnim)
1205        return;
1206
1207    // If we are waiting for the delay time to expire and it has, go to the next state
1208    if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding)
1209        return;
1210
1211    // We have to make sure to keep a ref to the this pointer, because it could get destroyed
1212    // during an animation callback that might get called. Since the owner is a CompositeAnimation
1213    // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase
1214    // can still access the resources of its CompositeAnimation as needed.
1215    RefPtr<AnimationBase> protector(this);
1216    RefPtr<CompositeAnimation> compProtector(m_compAnim);
1217
1218    // Check for start timeout
1219    if (m_animState == AnimationStateStartWaitTimer) {
1220        if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay())
1221            updateStateMachine(AnimationStateInputStartTimerFired, 0);
1222        return;
1223    }
1224
1225    double elapsedDuration = beginAnimationUpdateTime() - m_startTime;
1226    // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that
1227    // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate().
1228    // Also check in getTimeToNextEvent().
1229    elapsedDuration = max(elapsedDuration, 0.0);
1230
1231    // Check for end timeout
1232    if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) {
1233        // We may still be in AnimationStateLooping if we've managed to skip a
1234        // whole iteration, in which case we should jump to the end state.
1235        m_animState = AnimationStateEnding;
1236
1237        // Fire an end event
1238        updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration);
1239    } else {
1240        // Check for iteration timeout
1241        if (m_nextIterationDuration < 0) {
1242            // Hasn't been set yet, set it
1243            double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
1244            m_nextIterationDuration = elapsedDuration + durationLeft;
1245        }
1246
1247        if (elapsedDuration >= m_nextIterationDuration) {
1248            // Set to the next iteration
1249            double previous = m_nextIterationDuration;
1250            double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
1251            m_nextIterationDuration = elapsedDuration + durationLeft;
1252
1253            // Send the event
1254            updateStateMachine(AnimationStateInputLoopTimerFired, previous);
1255        }
1256    }
1257}
1258
1259void AnimationBase::updatePlayState(EAnimPlayState playState)
1260{
1261    if (!m_compAnim)
1262        return;
1263
1264    // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended.
1265    // The state machine can be in one of two states: running, paused.
1266    // Set the state machine to the desired state.
1267    bool pause = playState == AnimPlayStatePaused || m_compAnim->suspended();
1268
1269    if (pause == paused() && !isNew())
1270        return;
1271
1272    updateStateMachine(pause ?  AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1);
1273}
1274
1275double AnimationBase::timeToNextService()
1276{
1277    // Returns the time at which next service is required. -1 means no service is required. 0 means
1278    // service is required now, and > 0 means service is required that many seconds in the future.
1279    if (paused() || isNew() || m_animState == AnimationStateFillingForwards)
1280        return -1;
1281
1282    if (m_animState == AnimationStateStartWaitTimer) {
1283        double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime);
1284        return max(timeFromNow, 0.0);
1285    }
1286
1287    fireAnimationEventsIfNeeded();
1288
1289    // In all other cases, we need service right away.
1290    return 0;
1291}
1292
1293double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const
1294{
1295    if (preActive())
1296        return 0;
1297
1298    double elapsedTime = getElapsedTime();
1299
1300    double dur = m_animation->duration();
1301    if (m_animation->iterationCount() > 0)
1302        dur *= m_animation->iterationCount();
1303
1304    if (postActive() || !m_animation->duration())
1305        return 1.0;
1306    if (m_animation->iterationCount() > 0 && elapsedTime >= dur)
1307        return (m_animation->iterationCount() % 2) ? 1.0 : 0.0;
1308
1309    // Compute the fractional time, taking into account direction.
1310    // There is no need to worry about iterations, we assume that we would have
1311    // short circuited above if we were done.
1312    double fractionalTime = elapsedTime / m_animation->duration();
1313    int integralTime = static_cast<int>(fractionalTime);
1314    fractionalTime -= integralTime;
1315
1316    if ((m_animation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1))
1317        fractionalTime = 1 - fractionalTime;
1318
1319    if (scale != 1 || offset)
1320        fractionalTime = (fractionalTime - offset) * scale;
1321
1322    if (!tf)
1323        tf = m_animation->timingFunction().get();
1324
1325    if (tf->isCubicBezierTimingFunction()) {
1326        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
1327        return solveCubicBezierFunction(ctf->x1(),
1328                                        ctf->y1(),
1329                                        ctf->x2(),
1330                                        ctf->y2(),
1331                                        fractionalTime, m_animation->duration());
1332    } else if (tf->isStepsTimingFunction()) {
1333        const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
1334        return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime);
1335    } else
1336        return fractionalTime;
1337}
1338
1339void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
1340{
1341    // Decide when the end or loop event needs to fire
1342    const double elapsedDuration = max(beginAnimationUpdateTime() - m_startTime, 0.0);
1343    double durationLeft = 0;
1344    double nextIterationTime = m_totalDuration;
1345
1346    if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) {
1347        durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0;
1348        nextIterationTime = elapsedDuration + durationLeft;
1349    }
1350
1351    if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) {
1352        // We are not at the end yet
1353        ASSERT(nextIterationTime > 0);
1354        isLooping = true;
1355    } else {
1356        // We are at the end
1357        isLooping = false;
1358    }
1359
1360    time = durationLeft;
1361}
1362
1363void AnimationBase::goIntoEndingOrLoopingState()
1364{
1365    double t;
1366    bool isLooping;
1367    getTimeToNextEvent(t, isLooping);
1368    m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
1369}
1370
1371void AnimationBase::freezeAtTime(double t)
1372{
1373    if (!m_compAnim)
1374        return;
1375
1376    if (!m_startTime) {
1377        // If we haven't started yet, just generate the start event now
1378        m_compAnim->animationController()->receivedStartTimeResponse(currentTime());
1379    }
1380
1381    ASSERT(m_startTime);        // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
1382    m_pauseTime = m_startTime + t - m_animation->delay();
1383
1384#if USE(ACCELERATED_COMPOSITING)
1385    if (m_object && m_object->hasLayer()) {
1386        RenderLayer* layer = toRenderBoxModelObject(m_object)->layer();
1387        if (layer->isComposited())
1388            layer->backing()->suspendAnimations(m_pauseTime);
1389    }
1390#endif
1391}
1392
1393double AnimationBase::beginAnimationUpdateTime() const
1394{
1395    if (!m_compAnim)
1396        return 0;
1397
1398    return m_compAnim->animationController()->beginAnimationUpdateTime();
1399}
1400
1401double AnimationBase::getElapsedTime() const
1402{
1403    if (paused())
1404        return m_pauseTime - m_startTime;
1405    if (m_startTime <= 0)
1406        return 0;
1407    if (postActive())
1408        return 1;
1409
1410    return beginAnimationUpdateTime() - m_startTime;
1411}
1412
1413void AnimationBase::setElapsedTime(double time)
1414{
1415    // FIXME: implement this method
1416    UNUSED_PARAM(time);
1417}
1418
1419void AnimationBase::play()
1420{
1421    // FIXME: implement this method
1422}
1423
1424void AnimationBase::pause()
1425{
1426    // FIXME: implement this method
1427}
1428
1429} // namespace WebCore
1430