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 
54 using namespace std;
55 
56 namespace 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.
solveEpsilon(double duration)60 static inline double solveEpsilon(double duration)
61 {
62     return 1.0 / (200.0 * duration);
63 }
64 
solveCubicBezierFunction(double p1x,double p1y,double p2x,double p2y,double t,double duration)65 static 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 
solveStepsFunction(int numSteps,bool stepAtStart,double t)73 static 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 
blendFunc(const AnimationBase *,int from,int to,double progress)80 static inline int blendFunc(const AnimationBase*, int from, int to, double progress)
81 {
82     return int(from + (to - from) * progress);
83 }
84 
blendFunc(const AnimationBase *,double from,double to,double progress)85 static inline double blendFunc(const AnimationBase*, double from, double to, double progress)
86 {
87     return from + (to - from) * progress;
88 }
89 
blendFunc(const AnimationBase *,float from,float to,double progress)90 static inline float blendFunc(const AnimationBase*, float from, float to, double progress)
91 {
92     return narrowPrecisionToFloat(from + (to - from) * progress);
93 }
94 
blendFunc(const AnimationBase * anim,const Color & from,const Color & to,double progress)95 static 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 
blendFunc(const AnimationBase *,const Length & from,const Length & to,double progress)114 static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress)
115 {
116     return to.blend(from, narrowPrecisionToFloat(progress));
117 }
118 
blendFunc(const AnimationBase * anim,const LengthSize & from,const LengthSize & to,double progress)119 static 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 
blendFunc(const AnimationBase * anim,const IntSize & from,const IntSize & to,double progress)125 static 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 
blendFunc(const AnimationBase * anim,ShadowStyle from,ShadowStyle to,double progress)131 static 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 
blendFunc(const AnimationBase * anim,const ShadowData * from,const ShadowData * to,double progress)142 static inline PassOwnPtr<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 adoptPtr(new ShadowData(*to));
147 
148     return adoptPtr(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 
blendFunc(const AnimationBase * anim,const TransformOperations & from,const TransformOperations & to,double progress)157 static 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) : PassRefPtr<TransformOperation>(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 
blendFunc(const AnimationBase * anim,EVisibility from,EVisibility to,double progress)196 static 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 
blendFunc(const AnimationBase * anim,const LengthBox & from,const LengthBox & to,double progress)208 static 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 
224 class PropertyWrapperBase;
225 
226 static void addShorthandProperties();
227 static PropertyWrapperBase* wrapperForProperty(int propertyID);
228 
229 class PropertyWrapperBase {
230     WTF_MAKE_NONCOPYABLE(PropertyWrapperBase); WTF_MAKE_FAST_ALLOCATED;
231 public:
PropertyWrapperBase(int prop)232     PropertyWrapperBase(int prop)
233         : m_prop(prop)
234     {
235     }
236 
~PropertyWrapperBase()237     virtual ~PropertyWrapperBase() { }
238 
isShorthandWrapper() const239     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 
property() const243     int property() const { return m_prop; }
244 
245 #if USE(ACCELERATED_COMPOSITING)
animationIsAccelerated() const246     virtual bool animationIsAccelerated() const { return false; }
247 #endif
248 
249 private:
250     int m_prop;
251 };
252 
253 template <typename T>
254 class PropertyWrapperGetter : public PropertyWrapperBase {
255 public:
PropertyWrapperGetter(int prop,T (RenderStyle::* getter)()const)256     PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const)
257         : PropertyWrapperBase(prop)
258         , m_getter(getter)
259     {
260     }
261 
equals(const RenderStyle * a,const RenderStyle * b) const262     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 
273 protected:
274     T (RenderStyle::*m_getter)() const;
275 };
276 
277 template <typename T>
278 class PropertyWrapper : public PropertyWrapperGetter<T> {
279 public:
PropertyWrapper(int prop,T (RenderStyle::* getter)()const,void (RenderStyle::* setter)(T))280     PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
281         : PropertyWrapperGetter<T>(prop, getter)
282         , m_setter(setter)
283     {
284     }
285 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const286     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 
291 protected:
292     void (RenderStyle::*m_setter)(T);
293 };
294 
295 #if USE(ACCELERATED_COMPOSITING)
296 class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> {
297 public:
PropertyWrapperAcceleratedOpacity()298     PropertyWrapperAcceleratedOpacity()
299         : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)
300     {
301     }
302 
animationIsAccelerated() const303     virtual bool animationIsAccelerated() const { return true; }
304 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const305     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 
314 class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> {
315 public:
PropertyWrapperAcceleratedTransform()316     PropertyWrapperAcceleratedTransform()
317         : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform)
318     {
319     }
320 
animationIsAccelerated() const321     virtual bool animationIsAccelerated() const { return true; }
322 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const323     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 
330 class PropertyWrapperShadow : public PropertyWrapperBase {
331 public:
PropertyWrapperShadow(int prop,const ShadowData * (RenderStyle::* getter)()const,void (RenderStyle::* setter)(PassOwnPtr<ShadowData>,bool))332     PropertyWrapperShadow(int prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassOwnPtr<ShadowData>, bool))
333         : PropertyWrapperBase(prop)
334         , m_getter(getter)
335         , m_setter(setter)
336     {
337     }
338 
equals(const RenderStyle * a,const RenderStyle * b) const339     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 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const361     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         OwnPtr<ShadowData> newShadowData;
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             OwnPtr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress);
376             ShadowData* blendedShadowPtr = blendedShadow.get();
377 
378             if (!lastShadow)
379                 newShadowData = blendedShadow.release();
380             else
381                 lastShadow->setNext(blendedShadow.release());
382 
383             lastShadow = blendedShadowPtr;
384 
385             shadowA = shadowA ? shadowA->next() : 0;
386             shadowB = shadowB ? shadowB->next() : 0;
387         }
388 
389         (dst->*m_setter)(newShadowData.release(), false);
390     }
391 
392 private:
393     const ShadowData* (RenderStyle::*m_getter)() const;
394     void (RenderStyle::*m_setter)(PassOwnPtr<ShadowData>, bool);
395 };
396 
397 class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase {
398 public:
PropertyWrapperMaybeInvalidColor(int prop,const Color & (RenderStyle::* getter)()const,void (RenderStyle::* setter)(const Color &))399     PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
400         : PropertyWrapperBase(prop)
401         , m_getter(getter)
402         , m_setter(setter)
403     {
404     }
405 
equals(const RenderStyle * a,const RenderStyle * b) const406     virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
407     {
408         Color fromColor = (a->*m_getter)();
409         Color toColor = (b->*m_getter)();
410 
411         if (!fromColor.isValid() && !toColor.isValid())
412             return true;
413 
414         if (!fromColor.isValid())
415             fromColor = a->color();
416         if (!toColor.isValid())
417             toColor = b->color();
418 
419         return fromColor == toColor;
420     }
421 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const422     virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
423     {
424         Color fromColor = (a->*m_getter)();
425         Color toColor = (b->*m_getter)();
426 
427         if (!fromColor.isValid() && !toColor.isValid())
428             return;
429 
430         if (!fromColor.isValid())
431             fromColor = a->color();
432         if (!toColor.isValid())
433             toColor = b->color();
434         (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
435     }
436 
437 private:
438     const Color& (RenderStyle::*m_getter)() const;
439     void (RenderStyle::*m_setter)(const Color&);
440 };
441 
442 // Wrapper base class for an animatable property in a FillLayer
443 class FillLayerPropertyWrapperBase {
444 public:
FillLayerPropertyWrapperBase()445     FillLayerPropertyWrapperBase()
446     {
447     }
448 
~FillLayerPropertyWrapperBase()449     virtual ~FillLayerPropertyWrapperBase() { }
450 
451     virtual bool equals(const FillLayer* a, const FillLayer* b) const = 0;
452     virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const = 0;
453 };
454 
455 template <typename T>
456 class FillLayerPropertyWrapperGetter : public FillLayerPropertyWrapperBase {
457     WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter);
458 public:
FillLayerPropertyWrapperGetter(T (FillLayer::* getter)()const)459     FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const)
460         : m_getter(getter)
461     {
462     }
463 
equals(const FillLayer * a,const FillLayer * b) const464     virtual bool equals(const FillLayer* a, const FillLayer* b) const
465     {
466        // If the style pointers are the same, don't bother doing the test.
467        // If either is null, return false. If both are null, return true.
468        if ((!a && !b) || a == b)
469            return true;
470        if (!a || !b)
471             return false;
472         return (a->*m_getter)() == (b->*m_getter)();
473     }
474 
475 protected:
476     T (FillLayer::*m_getter)() const;
477 };
478 
479 template <typename T>
480 class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<T> {
481 public:
FillLayerPropertyWrapper(T (FillLayer::* getter)()const,void (FillLayer::* setter)(T))482     FillLayerPropertyWrapper(T (FillLayer::*getter)() const, void (FillLayer::*setter)(T))
483         : FillLayerPropertyWrapperGetter<T>(getter)
484         , m_setter(setter)
485     {
486     }
487 
blend(const AnimationBase * anim,FillLayer * dst,const FillLayer * a,const FillLayer * b,double progress) const488     virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const
489     {
490         (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T>::m_getter)(), progress));
491     }
492 
493 protected:
494     void (FillLayer::*m_setter)(T);
495 };
496 
497 
498 class FillLayersPropertyWrapper : public PropertyWrapperBase {
499 public:
500     typedef const FillLayer* (RenderStyle::*LayersGetter)() const;
501     typedef FillLayer* (RenderStyle::*LayersAccessor)();
502 
FillLayersPropertyWrapper(int prop,LayersGetter getter,LayersAccessor accessor)503     FillLayersPropertyWrapper(int prop, LayersGetter getter, LayersAccessor accessor)
504         : PropertyWrapperBase(prop)
505         , m_layersGetter(getter)
506         , m_layersAccessor(accessor)
507     {
508         switch (prop) {
509             case CSSPropertyBackgroundPositionX:
510             case CSSPropertyWebkitMaskPositionX:
511                 m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::xPosition, &FillLayer::setXPosition);
512                 break;
513             case CSSPropertyBackgroundPositionY:
514             case CSSPropertyWebkitMaskPositionY:
515                 m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::yPosition, &FillLayer::setYPosition);
516                 break;
517             case CSSPropertyBackgroundSize:
518             case CSSPropertyWebkitBackgroundSize:
519             case CSSPropertyWebkitMaskSize:
520                 m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<LengthSize>(&FillLayer::sizeLength, &FillLayer::setSizeLength);
521                 break;
522         }
523     }
524 
equals(const RenderStyle * a,const RenderStyle * b) const525     virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
526     {
527         const FillLayer* fromLayer = (a->*m_layersGetter)();
528         const FillLayer* toLayer = (b->*m_layersGetter)();
529 
530         while (fromLayer && toLayer) {
531             if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer))
532                 return false;
533 
534             fromLayer = fromLayer->next();
535             toLayer = toLayer->next();
536         }
537 
538         return true;
539     }
540 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const541     virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
542     {
543         const FillLayer* aLayer = (a->*m_layersGetter)();
544         const FillLayer* bLayer = (b->*m_layersGetter)();
545         FillLayer* dstLayer = (dst->*m_layersAccessor)();
546 
547         while (aLayer && bLayer && dstLayer) {
548             m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress);
549             aLayer = aLayer->next();
550             bLayer = bLayer->next();
551             dstLayer = dstLayer->next();
552         }
553     }
554 
555 private:
556     FillLayerPropertyWrapperBase* m_fillLayerPropertyWrapper;
557 
558     LayersGetter m_layersGetter;
559     LayersAccessor m_layersAccessor;
560 };
561 
562 class ShorthandPropertyWrapper : public PropertyWrapperBase {
563 public:
ShorthandPropertyWrapper(int property,const CSSPropertyLonghand & longhand)564     ShorthandPropertyWrapper(int property, const CSSPropertyLonghand& longhand)
565         : PropertyWrapperBase(property)
566     {
567         for (unsigned i = 0; i < longhand.length(); ++i) {
568             PropertyWrapperBase* wrapper = wrapperForProperty(longhand.properties()[i]);
569             if (wrapper)
570                 m_propertyWrappers.append(wrapper);
571         }
572     }
573 
isShorthandWrapper() const574     virtual bool isShorthandWrapper() const { return true; }
575 
equals(const RenderStyle * a,const RenderStyle * b) const576     virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
577     {
578         Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
579         for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) {
580             if (!(*it)->equals(a, b))
581                 return false;
582         }
583         return true;
584     }
585 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const586     virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
587     {
588         Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
589         for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it)
590             (*it)->blend(anim, dst, a, b, progress);
591     }
592 
propertyWrappers() const593     const Vector<PropertyWrapperBase*> propertyWrappers() const { return m_propertyWrappers; }
594 
595 private:
596     Vector<PropertyWrapperBase*> m_propertyWrappers;
597 };
598 
599 
600 static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0;
601 static int gPropertyWrapperMap[numCSSProperties];
602 
603 static const int cInvalidPropertyWrapperIndex = -1;
604 
605 
ensurePropertyMap()606 void AnimationBase::ensurePropertyMap()
607 {
608     // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed?
609     if (gPropertyWrappers == 0) {
610         gPropertyWrappers = new Vector<PropertyWrapperBase*>();
611 
612         // build the list of property wrappers to do the comparisons and blends
613         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft));
614         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight));
615         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop));
616         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom));
617 
618         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth));
619         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth));
620         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth));
621 
622         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight));
623         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight));
624         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight));
625 
626         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth));
627         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth));
628         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth));
629         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth));
630         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft));
631         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight));
632         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop));
633         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom));
634         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft));
635         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight));
636         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop));
637         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom));
638         gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor));
639 
640         gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor));
641 
642         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
643         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
644         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
645         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers));
646 
647         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers));
648         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers));
649         gPropertyWrappers->append(new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers));
650 
651         gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyFontSize, &RenderStyle::fontSize, &RenderStyle::setBlendedFontSize));
652         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth));
653         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap));
654         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount));
655         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth));
656         gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing));
657         gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing));
658         gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex));
659         gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyOrphans, &RenderStyle::orphans, &RenderStyle::setOrphans));
660         gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWidows, &RenderStyle::widows, &RenderStyle::setWidows));
661         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::lineHeight, &RenderStyle::setLineHeight));
662         gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset));
663         gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth));
664         gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing));
665         gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing));
666         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent));
667 
668         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective));
669         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX));
670         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY));
671         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX));
672         gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY));
673         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ));
674         gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius));
675         gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius));
676         gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius));
677         gPropertyWrappers->append(new PropertyWrapper<const LengthSize&>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius));
678         gPropertyWrappers->append(new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility));
679         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoom));
680 
681         gPropertyWrappers->append(new PropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip));
682 
683 #if USE(ACCELERATED_COMPOSITING)
684         gPropertyWrappers->append(new PropertyWrapperAcceleratedOpacity());
685         gPropertyWrappers->append(new PropertyWrapperAcceleratedTransform());
686 #else
687         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity));
688         gPropertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform));
689 #endif
690 
691         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitColumnRuleColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor));
692         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextStrokeColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor));
693         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextFillColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor));
694         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderLeftColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor));
695         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderRightColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor));
696         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderTopColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor));
697         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderBottomColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor));
698         gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyOutlineColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor));
699 
700         gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
701         gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
702         gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow));
703 
704 #if ENABLE(SVG)
705         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity));
706         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity));
707         gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity));
708 #endif
709 
710         // TODO:
711         //
712         //  CSSPropertyVerticalAlign
713         //
714         // Compound properties that have components that should be animatable:
715         //
716         //  CSSPropertyWebkitColumns
717         //  CSSPropertyWebkitBoxReflect
718 
719         // Make sure unused slots have a value
720         for (unsigned int i = 0; i < static_cast<unsigned int>(numCSSProperties); ++i)
721             gPropertyWrapperMap[i] = cInvalidPropertyWrapperIndex;
722 
723         // First we put the non-shorthand property wrappers into the map, so the shorthand-building
724         // code can find them.
725         size_t n = gPropertyWrappers->size();
726         for (unsigned int i = 0; i < n; ++i) {
727             ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties);
728             gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i;
729         }
730 
731         // Now add the shorthand wrappers.
732         addShorthandProperties();
733     }
734 }
735 
addPropertyWrapper(int propertyID,PropertyWrapperBase * wrapper)736 static void addPropertyWrapper(int propertyID, PropertyWrapperBase* wrapper)
737 {
738     int propIndex = propertyID - firstCSSProperty;
739 
740     ASSERT(gPropertyWrapperMap[propIndex] == cInvalidPropertyWrapperIndex);
741 
742     unsigned wrapperIndex = gPropertyWrappers->size();
743     gPropertyWrappers->append(wrapper);
744     gPropertyWrapperMap[propIndex] = wrapperIndex;
745 }
746 
addShorthandProperties()747 static void addShorthandProperties()
748 {
749     static const int animatableShorthandProperties[] = {
750         CSSPropertyBackground,      // for background-color, background-position
751         CSSPropertyBackgroundPosition,
752         CSSPropertyWebkitMask,      // for mask-position
753         CSSPropertyWebkitMaskPosition,
754         CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft,
755         CSSPropertyBorderColor,
756         CSSPropertyBorderRadius,
757         CSSPropertyBorderWidth,
758         CSSPropertyBorder,
759         CSSPropertyBorderSpacing,
760         CSSPropertyMargin,
761         CSSPropertyOutline,
762         CSSPropertyPadding,
763         CSSPropertyWebkitTextStroke,
764         CSSPropertyWebkitColumnRule,
765         CSSPropertyWebkitBorderRadius,
766         CSSPropertyWebkitTransformOrigin
767     };
768 
769     for (size_t i = 0; i < WTF_ARRAY_LENGTH(animatableShorthandProperties); ++i) {
770         int propertyID = animatableShorthandProperties[i];
771         CSSPropertyLonghand longhand = longhandForProperty(propertyID);
772         if (longhand.length() > 0)
773             addPropertyWrapper(propertyID, new ShorthandPropertyWrapper(propertyID, longhand));
774     }
775 
776     // 'font' is not in the shorthand map.
777     static const int animatableFontProperties[] = {
778         CSSPropertyFontSize,
779         CSSPropertyFontWeight
780     };
781 
782     CSSPropertyLonghand fontLonghand(animatableFontProperties, WTF_ARRAY_LENGTH(animatableFontProperties));
783     addPropertyWrapper(CSSPropertyFont, new ShorthandPropertyWrapper(CSSPropertyFont, fontLonghand));
784 }
785 
wrapperForProperty(int propertyID)786 static PropertyWrapperBase* wrapperForProperty(int propertyID)
787 {
788     int propIndex = propertyID - firstCSSProperty;
789     if (propIndex >= 0 && propIndex < numCSSProperties) {
790         int wrapperIndex = gPropertyWrapperMap[propIndex];
791         if (wrapperIndex >= 0)
792             return (*gPropertyWrappers)[wrapperIndex];
793     }
794     return 0;
795 }
796 
AnimationBase(const Animation * transition,RenderObject * renderer,CompositeAnimation * compAnim)797 AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim)
798     : m_animState(AnimationStateNew)
799     , m_isAnimating(false)
800     , m_startTime(0)
801     , m_pauseTime(-1)
802     , m_requestedStartTime(0)
803     , m_object(renderer)
804     , m_animation(const_cast<Animation*>(transition))
805     , m_compAnim(compAnim)
806     , m_isAccelerated(false)
807     , m_transformFunctionListValid(false)
808     , m_nextIterationDuration(-1)
809 {
810     // Compute the total duration
811     m_totalDuration = -1;
812     if (m_animation->iterationCount() > 0)
813         m_totalDuration = m_animation->duration() * m_animation->iterationCount();
814 }
815 
propertiesEqual(int prop,const RenderStyle * a,const RenderStyle * b)816 bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b)
817 {
818     ensurePropertyMap();
819     if (prop == cAnimateAll) {
820         size_t n = gPropertyWrappers->size();
821         for (unsigned int i = 0; i < n; ++i) {
822             PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
823             // No point comparing shorthand wrappers for 'all'.
824             if (!wrapper->isShorthandWrapper() && !wrapper->equals(a, b))
825                 return false;
826         }
827     } else {
828         PropertyWrapperBase* wrapper = wrapperForProperty(prop);
829         if (wrapper)
830             return wrapper->equals(a, b);
831     }
832     return true;
833 }
834 
getPropertyAtIndex(int i,bool & isShorthand)835 int AnimationBase::getPropertyAtIndex(int i, bool& isShorthand)
836 {
837     ensurePropertyMap();
838     if (i < 0 || i >= static_cast<int>(gPropertyWrappers->size()))
839         return CSSPropertyInvalid;
840 
841     PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
842     isShorthand = wrapper->isShorthandWrapper();
843     return wrapper->property();
844 }
845 
getNumProperties()846 int AnimationBase::getNumProperties()
847 {
848     ensurePropertyMap();
849     return gPropertyWrappers->size();
850 }
851 
852 // Returns true if we need to start animation timers
blendProperties(const AnimationBase * anim,int prop,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress)853 bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress)
854 {
855     ASSERT(prop != cAnimateAll);
856 
857     ensurePropertyMap();
858     PropertyWrapperBase* wrapper = wrapperForProperty(prop);
859     if (wrapper) {
860         wrapper->blend(anim, dst, a, b, progress);
861 #if USE(ACCELERATED_COMPOSITING)
862         return !wrapper->animationIsAccelerated() || !anim->isAccelerated();
863 #else
864         return true;
865 #endif
866     }
867 
868     return false;
869 }
870 
871 #if USE(ACCELERATED_COMPOSITING)
animationOfPropertyIsAccelerated(int prop)872 bool AnimationBase::animationOfPropertyIsAccelerated(int prop)
873 {
874     ensurePropertyMap();
875     PropertyWrapperBase* wrapper = wrapperForProperty(prop);
876     return wrapper ? wrapper->animationIsAccelerated() : false;
877 }
878 #endif
879 
gatherEnclosingShorthandProperties(int property,PropertyWrapperBase * wrapper,HashSet<int> & propertySet)880 static bool gatherEnclosingShorthandProperties(int property, PropertyWrapperBase* wrapper, HashSet<int>& propertySet)
881 {
882     if (!wrapper->isShorthandWrapper())
883         return false;
884 
885     ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper);
886 
887     bool contained = false;
888     for (size_t i = 0; i < shorthandWrapper->propertyWrappers().size(); ++i) {
889         PropertyWrapperBase* currWrapper = shorthandWrapper->propertyWrappers()[i];
890 
891         if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property)
892             contained = true;
893     }
894 
895     if (contained)
896         propertySet.add(wrapper->property());
897 
898     return contained;
899 }
900 
901 // Note: this is inefficient. It's only called from pauseTransitionAtTime().
animatableShorthandsAffectingProperty(int property)902 HashSet<int> AnimationBase::animatableShorthandsAffectingProperty(int property)
903 {
904     ensurePropertyMap();
905 
906     HashSet<int> foundProperties;
907     for (int i = 0; i < getNumProperties(); ++i)
908         gatherEnclosingShorthandProperties(property, (*gPropertyWrappers)[i], foundProperties);
909 
910     return foundProperties;
911 }
912 
setNeedsStyleRecalc(Node * node)913 void AnimationBase::setNeedsStyleRecalc(Node* node)
914 {
915     ASSERT(!node || (node->document() && !node->document()->inPageCache()));
916     if (node)
917         node->setNeedsStyleRecalc(SyntheticStyleChange);
918 }
919 
duration() const920 double AnimationBase::duration() const
921 {
922     return m_animation->duration();
923 }
924 
playStatePlaying() const925 bool AnimationBase::playStatePlaying() const
926 {
927     return m_animation->playState() == AnimPlayStatePlaying;
928 }
929 
animationsMatch(const Animation * anim) const930 bool AnimationBase::animationsMatch(const Animation* anim) const
931 {
932     return m_animation->animationsMatch(anim);
933 }
934 
updateStateMachine(AnimStateInput input,double param)935 void AnimationBase::updateStateMachine(AnimStateInput input, double param)
936 {
937     if (!m_compAnim)
938         return;
939 
940     // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state.
941     if (input == AnimationStateInputMakeNew) {
942         if (m_animState == AnimationStateStartWaitStyleAvailable)
943             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
944         m_animState = AnimationStateNew;
945         m_startTime = 0;
946         m_pauseTime = -1;
947         m_requestedStartTime = 0;
948         m_nextIterationDuration = -1;
949         endAnimation();
950         return;
951     }
952 
953     if (input == AnimationStateInputRestartAnimation) {
954         if (m_animState == AnimationStateStartWaitStyleAvailable)
955             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
956         m_animState = AnimationStateNew;
957         m_startTime = 0;
958         m_pauseTime = -1;
959         m_requestedStartTime = 0;
960         m_nextIterationDuration = -1;
961         endAnimation();
962 
963         if (!paused())
964             updateStateMachine(AnimationStateInputStartAnimation, -1);
965         return;
966     }
967 
968     if (input == AnimationStateInputEndAnimation) {
969         if (m_animState == AnimationStateStartWaitStyleAvailable)
970             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
971         m_animState = AnimationStateDone;
972         endAnimation();
973         return;
974     }
975 
976     if (input == AnimationStateInputPauseOverride) {
977         if (m_animState == AnimationStateStartWaitResponse) {
978             // If we are in AnimationStateStartWaitResponse, the animation will get canceled before
979             // we get a response, so move to the next state.
980             endAnimation();
981             updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
982         }
983         return;
984     }
985 
986     if (input == AnimationStateInputResumeOverride) {
987         if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) {
988             // Start the animation
989             startAnimation(beginAnimationUpdateTime() - m_startTime);
990         }
991         return;
992     }
993 
994     // Execute state machine
995     switch (m_animState) {
996         case AnimationStateNew:
997             ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused);
998             if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) {
999                 m_requestedStartTime = beginAnimationUpdateTime();
1000                 m_animState = AnimationStateStartWaitTimer;
1001             }
1002             break;
1003         case AnimationStateStartWaitTimer:
1004             ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused);
1005 
1006             if (input == AnimationStateInputStartTimerFired) {
1007                 ASSERT(param >= 0);
1008                 // Start timer has fired, tell the animation to start and wait for it to respond with start time
1009                 m_animState = AnimationStateStartWaitStyleAvailable;
1010                 m_compAnim->animationController()->addToAnimationsWaitingForStyle(this);
1011 
1012                 // Trigger a render so we can start the animation
1013                 if (m_object)
1014                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
1015             } else {
1016                 ASSERT(!paused());
1017                 // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
1018                 m_pauseTime = beginAnimationUpdateTime();
1019                 m_animState = AnimationStatePausedWaitTimer;
1020             }
1021             break;
1022         case AnimationStateStartWaitStyleAvailable:
1023             ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused);
1024 
1025             if (input == AnimationStateInputStyleAvailable) {
1026                 // Start timer has fired, tell the animation to start and wait for it to respond with start time
1027                 m_animState = AnimationStateStartWaitResponse;
1028 
1029                 overrideAnimations();
1030 
1031                 // Start the animation
1032                 if (overridden()) {
1033                     // We won't try to start accelerated animations if we are overridden and
1034                     // just move on to the next state.
1035                     m_animState = AnimationStateStartWaitResponse;
1036                     m_isAccelerated = false;
1037                     updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
1038                 } else {
1039                     double timeOffset = 0;
1040                     // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
1041                     if (m_animation->delay() < 0)
1042                         timeOffset = -m_animation->delay();
1043                     bool started = startAnimation(timeOffset);
1044 
1045                     m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
1046                     m_isAccelerated = started;
1047                 }
1048             } else {
1049                 // We're waiting for the style to be available and we got a pause. Pause and wait
1050                 m_pauseTime = beginAnimationUpdateTime();
1051                 m_animState = AnimationStatePausedWaitStyleAvailable;
1052             }
1053             break;
1054         case AnimationStateStartWaitResponse:
1055             ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused);
1056 
1057             if (input == AnimationStateInputStartTimeSet) {
1058                 ASSERT(param >= 0);
1059                 // We have a start time, set it, unless the startTime is already set
1060                 if (m_startTime <= 0) {
1061                     m_startTime = param;
1062                     // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
1063                     if (m_animation->delay() < 0)
1064                         m_startTime += m_animation->delay();
1065                 }
1066 
1067                 // Now that we know the start time, fire the start event.
1068                 onAnimationStart(0); // The elapsedTime is 0.
1069 
1070                 // Decide whether to go into looping or ending state
1071                 goIntoEndingOrLoopingState();
1072 
1073                 // Dispatch updateStyleIfNeeded so we can start the animation
1074                 if (m_object)
1075                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
1076             } else {
1077                 // We are pausing while waiting for a start response. Cancel the animation and wait. When
1078                 // we unpause, we will act as though the start timer just fired
1079                 m_pauseTime = beginAnimationUpdateTime();
1080                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
1081                 m_animState = AnimationStatePausedWaitResponse;
1082             }
1083             break;
1084         case AnimationStateLooping:
1085             ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused);
1086 
1087             if (input == AnimationStateInputLoopTimerFired) {
1088                 ASSERT(param >= 0);
1089                 // Loop timer fired, loop again or end.
1090                 onAnimationIteration(param);
1091 
1092                 // Decide whether to go into looping or ending state
1093                 goIntoEndingOrLoopingState();
1094             } else {
1095                 // We are pausing while running. Cancel the animation and wait
1096                 m_pauseTime = beginAnimationUpdateTime();
1097                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
1098                 m_animState = AnimationStatePausedRun;
1099             }
1100             break;
1101         case AnimationStateEnding:
1102             ASSERT(input == AnimationStateInputEndTimerFired || input == AnimationStateInputPlayStatePaused);
1103 
1104             if (input == AnimationStateInputEndTimerFired) {
1105 
1106                 ASSERT(param >= 0);
1107                 // End timer fired, finish up
1108                 onAnimationEnd(param);
1109 
1110                 m_animState = AnimationStateDone;
1111 
1112                 if (m_object) {
1113                     if (m_animation->fillsForwards())
1114                         m_animState = AnimationStateFillingForwards;
1115                     else
1116                         resumeOverriddenAnimations();
1117 
1118                     // Fire off another style change so we can set the final value
1119                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
1120                 }
1121             } else {
1122                 // We are pausing while running. Cancel the animation and wait
1123                 m_pauseTime = beginAnimationUpdateTime();
1124                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
1125                 m_animState = AnimationStatePausedRun;
1126             }
1127             // |this| may be deleted here
1128             break;
1129         case AnimationStatePausedWaitTimer:
1130             ASSERT(input == AnimationStateInputPlayStateRunning);
1131             ASSERT(paused());
1132             // Update the times
1133             m_startTime += beginAnimationUpdateTime() - m_pauseTime;
1134             m_pauseTime = -1;
1135 
1136             // we were waiting for the start timer to fire, go back and wait again
1137             m_animState = AnimationStateNew;
1138             updateStateMachine(AnimationStateInputStartAnimation, 0);
1139             break;
1140         case AnimationStatePausedWaitResponse:
1141         case AnimationStatePausedWaitStyleAvailable:
1142         case AnimationStatePausedRun:
1143             // We treat these two cases the same. The only difference is that, when we are in
1144             // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
1145             // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
1146             // that we have already set the startTime and will ignore it.
1147             ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable);
1148             ASSERT(paused());
1149 
1150             if (input == AnimationStateInputPlayStateRunning) {
1151                 // Update the times
1152                 if (m_animState == AnimationStatePausedRun)
1153                     m_startTime += beginAnimationUpdateTime() - m_pauseTime;
1154                 else
1155                     m_startTime = 0;
1156                 m_pauseTime = -1;
1157 
1158                 if (m_animState == AnimationStatePausedWaitStyleAvailable)
1159                     m_animState = AnimationStateStartWaitStyleAvailable;
1160                 else {
1161                     // We were either running or waiting for a begin time response from the animation.
1162                     // Either way we need to restart the animation (possibly with an offset if we
1163                     // had already been running) and wait for it to start.
1164                     m_animState = AnimationStateStartWaitResponse;
1165 
1166                     // Start the animation
1167                     if (overridden()) {
1168                         // We won't try to start accelerated animations if we are overridden and
1169                         // just move on to the next state.
1170                         updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
1171                         m_isAccelerated = true;
1172                     } else {
1173                         bool started = startAnimation(beginAnimationUpdateTime() - m_startTime);
1174                         m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
1175                         m_isAccelerated = started;
1176                     }
1177                 }
1178                 break;
1179             }
1180 
1181             if (input == AnimationStateInputStartTimeSet) {
1182                 ASSERT(m_animState == AnimationStatePausedWaitResponse);
1183 
1184                 // We are paused but we got the callback that notifies us that an accelerated animation started.
1185                 // We ignore the start time and just move into the paused-run state.
1186                 m_animState = AnimationStatePausedRun;
1187                 ASSERT(m_startTime == 0);
1188                 m_startTime = param;
1189                 m_pauseTime += m_startTime;
1190                 break;
1191             }
1192 
1193             ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable);
1194             // We are paused but we got the callback that notifies us that style has been updated.
1195             // We move to the AnimationStatePausedWaitResponse state
1196             m_animState = AnimationStatePausedWaitResponse;
1197             overrideAnimations();
1198             break;
1199         case AnimationStateFillingForwards:
1200         case AnimationStateDone:
1201             // We're done. Stay in this state until we are deleted
1202             break;
1203     }
1204 }
1205 
fireAnimationEventsIfNeeded()1206 void AnimationBase::fireAnimationEventsIfNeeded()
1207 {
1208     if (!m_compAnim)
1209         return;
1210 
1211     // If we are waiting for the delay time to expire and it has, go to the next state
1212     if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding)
1213         return;
1214 
1215     // We have to make sure to keep a ref to the this pointer, because it could get destroyed
1216     // during an animation callback that might get called. Since the owner is a CompositeAnimation
1217     // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase
1218     // can still access the resources of its CompositeAnimation as needed.
1219     RefPtr<AnimationBase> protector(this);
1220     RefPtr<CompositeAnimation> compProtector(m_compAnim);
1221 
1222     // Check for start timeout
1223     if (m_animState == AnimationStateStartWaitTimer) {
1224         if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay())
1225             updateStateMachine(AnimationStateInputStartTimerFired, 0);
1226         return;
1227     }
1228 
1229     double elapsedDuration = beginAnimationUpdateTime() - m_startTime;
1230     // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that
1231     // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate().
1232     // Also check in getTimeToNextEvent().
1233     elapsedDuration = max(elapsedDuration, 0.0);
1234 
1235     // Check for end timeout
1236     if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) {
1237         // We may still be in AnimationStateLooping if we've managed to skip a
1238         // whole iteration, in which case we should jump to the end state.
1239         m_animState = AnimationStateEnding;
1240 
1241         // Fire an end event
1242         updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration);
1243     } else {
1244         // Check for iteration timeout
1245         if (m_nextIterationDuration < 0) {
1246             // Hasn't been set yet, set it
1247             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
1248             m_nextIterationDuration = elapsedDuration + durationLeft;
1249         }
1250 
1251         if (elapsedDuration >= m_nextIterationDuration) {
1252             // Set to the next iteration
1253             double previous = m_nextIterationDuration;
1254             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
1255             m_nextIterationDuration = elapsedDuration + durationLeft;
1256 
1257             // Send the event
1258             updateStateMachine(AnimationStateInputLoopTimerFired, previous);
1259         }
1260     }
1261 }
1262 
updatePlayState(EAnimPlayState playState)1263 void AnimationBase::updatePlayState(EAnimPlayState playState)
1264 {
1265     if (!m_compAnim)
1266         return;
1267 
1268     // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended.
1269     // The state machine can be in one of two states: running, paused.
1270     // Set the state machine to the desired state.
1271     bool pause = playState == AnimPlayStatePaused || m_compAnim->suspended();
1272 
1273     if (pause == paused() && !isNew())
1274         return;
1275 
1276     updateStateMachine(pause ?  AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1);
1277 }
1278 
timeToNextService()1279 double AnimationBase::timeToNextService()
1280 {
1281     // Returns the time at which next service is required. -1 means no service is required. 0 means
1282     // service is required now, and > 0 means service is required that many seconds in the future.
1283     if (paused() || isNew() || m_animState == AnimationStateFillingForwards)
1284         return -1;
1285 
1286     if (m_animState == AnimationStateStartWaitTimer) {
1287         double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime);
1288         return max(timeFromNow, 0.0);
1289     }
1290 
1291     fireAnimationEventsIfNeeded();
1292 
1293     // In all other cases, we need service right away.
1294     return 0;
1295 }
1296 
progress(double scale,double offset,const TimingFunction * tf) const1297 double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const
1298 {
1299     if (preActive())
1300         return 0;
1301 
1302     double elapsedTime = getElapsedTime();
1303 
1304     double dur = m_animation->duration();
1305     if (m_animation->iterationCount() > 0)
1306         dur *= m_animation->iterationCount();
1307 
1308     if (postActive() || !m_animation->duration())
1309         return 1.0;
1310     if (m_animation->iterationCount() > 0 && elapsedTime >= dur)
1311         return (m_animation->iterationCount() % 2) ? 1.0 : 0.0;
1312 
1313     // Compute the fractional time, taking into account direction.
1314     // There is no need to worry about iterations, we assume that we would have
1315     // short circuited above if we were done.
1316     double fractionalTime = elapsedTime / m_animation->duration();
1317     int integralTime = static_cast<int>(fractionalTime);
1318     fractionalTime -= integralTime;
1319 
1320     if ((m_animation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1))
1321         fractionalTime = 1 - fractionalTime;
1322 
1323     if (scale != 1 || offset)
1324         fractionalTime = (fractionalTime - offset) * scale;
1325 
1326     if (!tf)
1327         tf = m_animation->timingFunction().get();
1328 
1329     if (tf->isCubicBezierTimingFunction()) {
1330         const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
1331         return solveCubicBezierFunction(ctf->x1(),
1332                                         ctf->y1(),
1333                                         ctf->x2(),
1334                                         ctf->y2(),
1335                                         fractionalTime, m_animation->duration());
1336     } else if (tf->isStepsTimingFunction()) {
1337         const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
1338         return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime);
1339     } else
1340         return fractionalTime;
1341 }
1342 
getTimeToNextEvent(double & time,bool & isLooping) const1343 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
1344 {
1345     // Decide when the end or loop event needs to fire
1346     const double elapsedDuration = max(beginAnimationUpdateTime() - m_startTime, 0.0);
1347     double durationLeft = 0;
1348     double nextIterationTime = m_totalDuration;
1349 
1350     if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) {
1351         durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0;
1352         nextIterationTime = elapsedDuration + durationLeft;
1353     }
1354 
1355     if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) {
1356         // We are not at the end yet
1357         ASSERT(nextIterationTime > 0);
1358         isLooping = true;
1359     } else {
1360         // We are at the end
1361         isLooping = false;
1362     }
1363 
1364     time = durationLeft;
1365 }
1366 
goIntoEndingOrLoopingState()1367 void AnimationBase::goIntoEndingOrLoopingState()
1368 {
1369     double t;
1370     bool isLooping;
1371     getTimeToNextEvent(t, isLooping);
1372     m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
1373 }
1374 
freezeAtTime(double t)1375 void AnimationBase::freezeAtTime(double t)
1376 {
1377     if (!m_compAnim)
1378         return;
1379 
1380     if (!m_startTime) {
1381         // If we haven't started yet, just generate the start event now
1382         m_compAnim->animationController()->receivedStartTimeResponse(currentTime());
1383     }
1384 
1385     ASSERT(m_startTime);        // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
1386     m_pauseTime = m_startTime + t - m_animation->delay();
1387 
1388 #if USE(ACCELERATED_COMPOSITING)
1389     if (m_object && m_object->hasLayer()) {
1390         RenderLayer* layer = toRenderBoxModelObject(m_object)->layer();
1391         if (layer->isComposited())
1392             layer->backing()->suspendAnimations(m_pauseTime);
1393     }
1394 #endif
1395 }
1396 
beginAnimationUpdateTime() const1397 double AnimationBase::beginAnimationUpdateTime() const
1398 {
1399     if (!m_compAnim)
1400         return 0;
1401 
1402     return m_compAnim->animationController()->beginAnimationUpdateTime();
1403 }
1404 
getElapsedTime() const1405 double AnimationBase::getElapsedTime() const
1406 {
1407     if (paused())
1408         return m_pauseTime - m_startTime;
1409     if (m_startTime <= 0)
1410         return 0;
1411     if (postActive())
1412         return 1;
1413 
1414     return beginAnimationUpdateTime() - m_startTime;
1415 }
1416 
setElapsedTime(double time)1417 void AnimationBase::setElapsedTime(double time)
1418 {
1419     // FIXME: implement this method
1420     UNUSED_PARAM(time);
1421 }
1422 
play()1423 void AnimationBase::play()
1424 {
1425     // FIXME: implement this method
1426 }
1427 
pause()1428 void AnimationBase::pause()
1429 {
1430     // FIXME: implement this method
1431 }
1432 
1433 } // namespace WebCore
1434