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