• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 
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) : 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)(ShadowData *,bool))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 
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         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 
390 private:
391     const ShadowData* (RenderStyle::*m_getter)() const;
392     void (RenderStyle::*m_setter)(ShadowData*, bool);
393 };
394 
395 class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase {
396 public:
PropertyWrapperMaybeInvalidColor(int prop,const Color & (RenderStyle::* getter)()const,void (RenderStyle::* setter)(const Color &))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 
equals(const RenderStyle * a,const RenderStyle * b) const404     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 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const420     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 
435 private:
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
441 class FillLayerPropertyWrapperBase {
442 public:
FillLayerPropertyWrapperBase()443     FillLayerPropertyWrapperBase()
444     {
445     }
446 
~FillLayerPropertyWrapperBase()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 
453 template <typename T>
454 class FillLayerPropertyWrapperGetter : public FillLayerPropertyWrapperBase {
455     WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter);
456 public:
FillLayerPropertyWrapperGetter(T (FillLayer::* getter)()const)457     FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const)
458         : m_getter(getter)
459     {
460     }
461 
equals(const FillLayer * a,const FillLayer * b) const462     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 
473 protected:
474     T (FillLayer::*m_getter)() const;
475 };
476 
477 template <typename T>
478 class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<T> {
479 public:
FillLayerPropertyWrapper(T (FillLayer::* getter)()const,void (FillLayer::* setter)(T))480     FillLayerPropertyWrapper(T (FillLayer::*getter)() const, void (FillLayer::*setter)(T))
481         : FillLayerPropertyWrapperGetter<T>(getter)
482         , m_setter(setter)
483     {
484     }
485 
blend(const AnimationBase * anim,FillLayer * dst,const FillLayer * a,const FillLayer * b,double progress) const486     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 
491 protected:
492     void (FillLayer::*m_setter)(T);
493 };
494 
495 
496 class FillLayersPropertyWrapper : public PropertyWrapperBase {
497 public:
498     typedef const FillLayer* (RenderStyle::*LayersGetter)() const;
499     typedef FillLayer* (RenderStyle::*LayersAccessor)();
500 
FillLayersPropertyWrapper(int prop,LayersGetter getter,LayersAccessor accessor)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 
equals(const RenderStyle * a,const RenderStyle * b) const523     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 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const539     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 
553 private:
554     FillLayerPropertyWrapperBase* m_fillLayerPropertyWrapper;
555 
556     LayersGetter m_layersGetter;
557     LayersAccessor m_layersAccessor;
558 };
559 
560 class ShorthandPropertyWrapper : public PropertyWrapperBase {
561 public:
ShorthandPropertyWrapper(int property,const CSSPropertyLonghand & longhand)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 
isShorthandWrapper() const572     virtual bool isShorthandWrapper() const { return true; }
573 
equals(const RenderStyle * a,const RenderStyle * b) const574     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 
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const584     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 
propertyWrappers() const591     const Vector<PropertyWrapperBase*> propertyWrappers() const { return m_propertyWrappers; }
592 
593 private:
594     Vector<PropertyWrapperBase*> m_propertyWrappers;
595 };
596 
597 
598 static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0;
599 static int gPropertyWrapperMap[numCSSProperties];
600 
601 static const int cInvalidPropertyWrapperIndex = -1;
602 
603 
ensurePropertyMap()604 void 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 
addPropertyWrapper(int propertyID,PropertyWrapperBase * wrapper)732 static 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 
addShorthandProperties()743 static 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 
wrapperForProperty(int propertyID)782 static 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 
AnimationBase(const Animation * transition,RenderObject * renderer,CompositeAnimation * compAnim)793 AnimationBase::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 
propertiesEqual(int prop,const RenderStyle * a,const RenderStyle * b)812 bool 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 
getPropertyAtIndex(int i,bool & isShorthand)831 int 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 
getNumProperties()842 int AnimationBase::getNumProperties()
843 {
844     ensurePropertyMap();
845     return gPropertyWrappers->size();
846 }
847 
848 // 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)849 bool 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)
animationOfPropertyIsAccelerated(int prop)868 bool AnimationBase::animationOfPropertyIsAccelerated(int prop)
869 {
870     ensurePropertyMap();
871     PropertyWrapperBase* wrapper = wrapperForProperty(prop);
872     return wrapper ? wrapper->animationIsAccelerated() : false;
873 }
874 #endif
875 
gatherEnclosingShorthandProperties(int property,PropertyWrapperBase * wrapper,HashSet<int> & propertySet)876 static 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().
animatableShorthandsAffectingProperty(int property)898 HashSet<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 
setNeedsStyleRecalc(Node * node)909 void AnimationBase::setNeedsStyleRecalc(Node* node)
910 {
911     ASSERT(!node || (node->document() && !node->document()->inPageCache()));
912     if (node)
913         node->setNeedsStyleRecalc(SyntheticStyleChange);
914 }
915 
duration() const916 double AnimationBase::duration() const
917 {
918     return m_animation->duration();
919 }
920 
playStatePlaying() const921 bool AnimationBase::playStatePlaying() const
922 {
923     return m_animation->playState() == AnimPlayStatePlaying;
924 }
925 
animationsMatch(const Animation * anim) const926 bool AnimationBase::animationsMatch(const Animation* anim) const
927 {
928     return m_animation->animationsMatch(anim);
929 }
930 
updateStateMachine(AnimStateInput input,double param)931 void 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 
fireAnimationEventsIfNeeded()1202 void 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 
updatePlayState(EAnimPlayState playState)1259 void 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 
timeToNextService()1275 double 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 
progress(double scale,double offset,const TimingFunction * tf) const1293 double 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 
getTimeToNextEvent(double & time,bool & isLooping) const1339 void 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 
goIntoEndingOrLoopingState()1363 void AnimationBase::goIntoEndingOrLoopingState()
1364 {
1365     double t;
1366     bool isLooping;
1367     getTimeToNextEvent(t, isLooping);
1368     m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
1369 }
1370 
freezeAtTime(double t)1371 void 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 
beginAnimationUpdateTime() const1393 double AnimationBase::beginAnimationUpdateTime() const
1394 {
1395     if (!m_compAnim)
1396         return 0;
1397 
1398     return m_compAnim->animationController()->beginAnimationUpdateTime();
1399 }
1400 
getElapsedTime() const1401 double 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 
setElapsedTime(double time)1413 void AnimationBase::setElapsedTime(double time)
1414 {
1415     // FIXME: implement this method
1416     UNUSED_PARAM(time);
1417 }
1418 
play()1419 void AnimationBase::play()
1420 {
1421     // FIXME: implement this method
1422 }
1423 
pause()1424 void AnimationBase::pause()
1425 {
1426     // FIXME: implement this method
1427 }
1428 
1429 } // namespace WebCore
1430