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