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