1 /*
2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "AnimationBase.h"
31
32 #include "AnimationControllerPrivate.h"
33 #include "CSSMutableStyleDeclaration.h"
34 #include "CSSPropertyLonghand.h"
35 #include "CSSPropertyNames.h"
36 #include "CString.h"
37 #include "CompositeAnimation.h"
38 #include "Document.h"
39 #include "EventNames.h"
40 #include "FloatConversion.h"
41 #include "Frame.h"
42 #include "IdentityTransformOperation.h"
43 #include "ImplicitAnimation.h"
44 #include "KeyframeAnimation.h"
45 #include "MatrixTransformOperation.h"
46 #include "Matrix3DTransformOperation.h"
47 #include "RenderBox.h"
48 #include "RenderLayer.h"
49 #include "RenderLayerBacking.h"
50 #include "RenderStyle.h"
51 #include "UnitBezier.h"
52
53 #include <algorithm>
54
55 using namespace std;
56
57 namespace WebCore {
58
59 // The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the
60 // animation, the more precision we need in the timing function result to avoid ugly discontinuities.
solveEpsilon(double duration)61 static inline double solveEpsilon(double duration)
62 {
63 return 1.0 / (200.0 * duration);
64 }
65
solveCubicBezierFunction(double p1x,double p1y,double p2x,double p2y,double t,double duration)66 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
67 {
68 // Convert from input time to parametric value in curve, then from
69 // that to output time.
70 UnitBezier bezier(p1x, p1y, p2x, p2y);
71 return bezier.solve(t, solveEpsilon(duration));
72 }
73
blendFunc(const AnimationBase *,int from,int to,double progress)74 static inline int blendFunc(const AnimationBase*, int from, int to, double progress)
75 {
76 return int(from + (to - from) * progress);
77 }
78
blendFunc(const AnimationBase *,double from,double to,double progress)79 static inline double blendFunc(const AnimationBase*, double from, double to, double progress)
80 {
81 return from + (to - from) * progress;
82 }
83
blendFunc(const AnimationBase *,float from,float to,double progress)84 static inline float blendFunc(const AnimationBase*, float from, float to, double progress)
85 {
86 return narrowPrecisionToFloat(from + (to - from) * progress);
87 }
88
blendFunc(const AnimationBase * anim,const Color & from,const Color & to,double progress)89 static inline Color blendFunc(const AnimationBase* anim, const Color& from, const Color& to, double progress)
90 {
91 // We need to preserve the state of the valid flag at the end of the animation
92 if (progress == 1 && !to.isValid())
93 return Color();
94
95 return Color(blendFunc(anim, from.red(), to.red(), progress),
96 blendFunc(anim, from.green(), to.green(), progress),
97 blendFunc(anim, from.blue(), to.blue(), progress),
98 blendFunc(anim, from.alpha(), to.alpha(), progress));
99 }
100
blendFunc(const AnimationBase *,const Length & from,const Length & to,double progress)101 static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress)
102 {
103 return to.blend(from, progress);
104 }
105
blendFunc(const AnimationBase * anim,const LengthSize & from,const LengthSize & to,double progress)106 static inline LengthSize blendFunc(const AnimationBase* anim, const LengthSize& from, const LengthSize& to, double progress)
107 {
108 return LengthSize(blendFunc(anim, from.width(), to.width(), progress),
109 blendFunc(anim, from.height(), to.height(), progress));
110 }
111
blendFunc(const AnimationBase * anim,const IntSize & from,const IntSize & to,double progress)112 static inline IntSize blendFunc(const AnimationBase* anim, const IntSize& from, const IntSize& to, double progress)
113 {
114 return IntSize(blendFunc(anim, from.width(), to.width(), progress),
115 blendFunc(anim, from.height(), to.height(), progress));
116 }
117
blendFunc(const AnimationBase * anim,ShadowStyle from,ShadowStyle to,double progress)118 static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, ShadowStyle to, double progress)
119 {
120 if (from == to)
121 return to;
122
123 double fromVal = from == Normal ? 1 : 0;
124 double toVal = to == Normal ? 1 : 0;
125 double result = blendFunc(anim, fromVal, toVal, progress);
126 return result > 0 ? Normal : Inset;
127 }
128
blendFunc(const AnimationBase * anim,const ShadowData * from,const ShadowData * to,double progress)129 static inline ShadowData* blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress)
130 {
131 ASSERT(from && to);
132 return new ShadowData(blendFunc(anim, from->x, to->x, progress), blendFunc(anim, from->y, to->y, progress),
133 blendFunc(anim, from->blur, to->blur, progress), blendFunc(anim, from->spread, to->spread, progress),
134 blendFunc(anim, from->style, to->style, progress), blendFunc(anim, from->color, to->color, progress));
135 }
136
blendFunc(const AnimationBase * anim,const TransformOperations & from,const TransformOperations & to,double progress)137 static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress)
138 {
139 TransformOperations result;
140
141 // If we have a transform function list, use that to do a per-function animation. Otherwise do a Matrix animation
142 if (anim->isTransformFunctionListValid()) {
143 unsigned fromSize = from.operations().size();
144 unsigned toSize = to.operations().size();
145 unsigned size = max(fromSize, toSize);
146 for (unsigned i = 0; i < size; i++) {
147 RefPtr<TransformOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0;
148 RefPtr<TransformOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0;
149 RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp.get(), progress) : (fromOp ? fromOp->blend(0, progress, true) : 0);
150 if (blendedOp)
151 result.operations().append(blendedOp);
152 else {
153 RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create();
154 if (progress > 0.5)
155 result.operations().append(toOp ? toOp : identityOp);
156 else
157 result.operations().append(fromOp ? fromOp : identityOp);
158 }
159 }
160 } else {
161 // Convert the TransformOperations into matrices
162 IntSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : IntSize();
163 TransformationMatrix fromT;
164 TransformationMatrix toT;
165 from.apply(size, fromT);
166 to.apply(size, toT);
167
168 toT.blend(fromT, progress);
169
170 // Append the result
171 result.operations().append(Matrix3DTransformOperation::create(toT));
172 }
173 return result;
174 }
175
blendFunc(const AnimationBase * anim,EVisibility from,EVisibility to,double progress)176 static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress)
177 {
178 // Any non-zero result means we consider the object to be visible. Only at 0 do we consider the object to be
179 // invisible. The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values.
180 double fromVal = from == VISIBLE ? 1. : 0.;
181 double toVal = to == VISIBLE ? 1. : 0.;
182 if (fromVal == toVal)
183 return to;
184 double result = blendFunc(anim, fromVal, toVal, progress);
185 return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
186 }
187
188 class PropertyWrapperBase;
189
190 static void addShorthandProperties();
191 static PropertyWrapperBase* wrapperForProperty(int propertyID);
192
193 class PropertyWrapperBase {
194 public:
PropertyWrapperBase(int prop)195 PropertyWrapperBase(int prop)
196 : m_prop(prop)
197 {
198 }
199
~PropertyWrapperBase()200 virtual ~PropertyWrapperBase() { }
201
isShorthandWrapper() const202 virtual bool isShorthandWrapper() const { return false; }
203 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0;
204 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const = 0;
205
property() const206 int property() const { return m_prop; }
207
208 #if USE(ACCELERATED_COMPOSITING)
animationIsAccelerated() const209 virtual bool animationIsAccelerated() const { return false; }
210 #endif
211
212 private:
213 int m_prop;
214 };
215
216 template <typename T>
217 class PropertyWrapperGetter : public PropertyWrapperBase {
218 public:
PropertyWrapperGetter(int prop,T (RenderStyle::* getter)()const)219 PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const)
220 : PropertyWrapperBase(prop)
221 , m_getter(getter)
222 {
223 }
224
equals(const RenderStyle * a,const RenderStyle * b) const225 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
226 {
227 // If the style pointers are the same, don't bother doing the test.
228 // If either is null, return false. If both are null, return true.
229 if ((!a && !b) || a == b)
230 return true;
231 if (!a || !b)
232 return false;
233 return (a->*m_getter)() == (b->*m_getter)();
234 }
235
236 protected:
237 T (RenderStyle::*m_getter)() const;
238 };
239
240 template <typename T>
241 class PropertyWrapper : public PropertyWrapperGetter<T> {
242 public:
PropertyWrapper(int prop,T (RenderStyle::* getter)()const,void (RenderStyle::* setter)(T))243 PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T))
244 : PropertyWrapperGetter<T>(prop, getter)
245 , m_setter(setter)
246 {
247 }
248
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const249 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
250 {
251 (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress));
252 }
253
254 protected:
255 void (RenderStyle::*m_setter)(T);
256 };
257
258 #if USE(ACCELERATED_COMPOSITING)
259 class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> {
260 public:
PropertyWrapperAcceleratedOpacity()261 PropertyWrapperAcceleratedOpacity()
262 : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)
263 {
264 }
265
animationIsAccelerated() const266 virtual bool animationIsAccelerated() const { return true; }
267
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const268 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
269 {
270 float fromOpacity = a->opacity();
271
272 // This makes sure we put the object being animated into a RenderLayer during the animation
273 dst->setOpacity(blendFunc(anim, (fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), progress));
274 }
275 };
276
277 class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> {
278 public:
PropertyWrapperAcceleratedTransform()279 PropertyWrapperAcceleratedTransform()
280 : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform)
281 {
282 }
283
animationIsAccelerated() const284 virtual bool animationIsAccelerated() const { return true; }
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->setTransform(blendFunc(anim, a->transform(), b->transform(), progress));
289 }
290 };
291 #endif // USE(ACCELERATED_COMPOSITING)
292
293 class PropertyWrapperShadow : public PropertyWrapperGetter<ShadowData*> {
294 public:
PropertyWrapperShadow(int prop,ShadowData * (RenderStyle::* getter)()const,void (RenderStyle::* setter)(ShadowData *,bool))295 PropertyWrapperShadow(int prop, ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowData*, bool))
296 : PropertyWrapperGetter<ShadowData*>(prop, getter)
297 , m_setter(setter)
298 {
299 }
300
equals(const RenderStyle * a,const RenderStyle * b) const301 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
302 {
303 ShadowData* shadowA = (a->*m_getter)();
304 ShadowData* shadowB = (b->*m_getter)();
305
306 if ((!shadowA && shadowB) || (shadowA && !shadowB))
307 return false;
308 if (shadowA && shadowB && (*shadowA != *shadowB))
309 return false;
310 return true;
311 }
312
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const313 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
314 {
315 ShadowData* shadowA = (a->*m_getter)();
316 ShadowData* shadowB = (b->*m_getter)();
317 ShadowData defaultShadowData(0, 0, 0, 0, Normal, Color::transparent);
318
319 if (!shadowA)
320 shadowA = &defaultShadowData;
321 if (!shadowB)
322 shadowB = &defaultShadowData;
323
324 (dst->*m_setter)(blendFunc(anim, shadowA, shadowB, progress), false);
325 }
326
327 private:
328 void (RenderStyle::*m_setter)(ShadowData*, bool);
329 };
330
331 class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase {
332 public:
PropertyWrapperMaybeInvalidColor(int prop,const Color & (RenderStyle::* getter)()const,void (RenderStyle::* setter)(const Color &))333 PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&))
334 : PropertyWrapperBase(prop)
335 , m_getter(getter)
336 , m_setter(setter)
337 {
338 }
339
equals(const RenderStyle * a,const RenderStyle * b) const340 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
341 {
342 Color fromColor = (a->*m_getter)();
343 Color toColor = (b->*m_getter)();
344 if (!fromColor.isValid())
345 fromColor = a->color();
346 if (!toColor.isValid())
347 toColor = b->color();
348
349 return fromColor == toColor;
350 }
351
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const352 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
353 {
354 Color fromColor = (a->*m_getter)();
355 Color toColor = (b->*m_getter)();
356 if (!fromColor.isValid())
357 fromColor = a->color();
358 if (!toColor.isValid())
359 toColor = b->color();
360 (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress));
361 }
362
363 private:
364 const Color& (RenderStyle::*m_getter)() const;
365 void (RenderStyle::*m_setter)(const Color&);
366 };
367
368 class ShorthandPropertyWrapper : public PropertyWrapperBase {
369 public:
ShorthandPropertyWrapper(int property,const CSSPropertyLonghand & longhand)370 ShorthandPropertyWrapper(int property, const CSSPropertyLonghand& longhand)
371 : PropertyWrapperBase(property)
372 {
373 for (unsigned i = 0; i < longhand.length(); ++i) {
374 PropertyWrapperBase* wrapper = wrapperForProperty(longhand.properties()[i]);
375 if (wrapper)
376 m_propertyWrappers.append(wrapper);
377 }
378 }
379
isShorthandWrapper() const380 virtual bool isShorthandWrapper() const { return true; }
381
equals(const RenderStyle * a,const RenderStyle * b) const382 virtual bool equals(const RenderStyle* a, const RenderStyle* b) const
383 {
384 Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
385 for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) {
386 if (!(*it)->equals(a, b))
387 return false;
388 }
389 return true;
390 }
391
blend(const AnimationBase * anim,RenderStyle * dst,const RenderStyle * a,const RenderStyle * b,double progress) const392 virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const
393 {
394 Vector<PropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end();
395 for (Vector<PropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it)
396 (*it)->blend(anim, dst, a, b, progress);
397 }
398
399 private:
400 Vector<PropertyWrapperBase*> m_propertyWrappers;
401 };
402
403
404 static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0;
405 static int gPropertyWrapperMap[numCSSProperties];
406
407 static const int cInvalidPropertyWrapperIndex = -1;
408
409
ensurePropertyMap()410 static void ensurePropertyMap()
411 {
412 // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed?
413 if (gPropertyWrappers == 0) {
414 gPropertyWrappers = new Vector<PropertyWrapperBase*>();
415
416 // build the list of property wrappers to do the comparisons and blends
417 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft));
418 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight));
419 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop));
420 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom));
421
422 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth));
423 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinWidth, &RenderStyle::minWidth, &RenderStyle::setMinWidth));
424 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxWidth, &RenderStyle::maxWidth, &RenderStyle::setMaxWidth));
425
426 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight));
427 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMinHeight, &RenderStyle::minHeight, &RenderStyle::setMinHeight));
428 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMaxHeight, &RenderStyle::maxHeight, &RenderStyle::setMaxHeight));
429
430 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth));
431 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth));
432 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth));
433 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth));
434 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft));
435 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight));
436 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop));
437 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom));
438 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft));
439 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight));
440 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop));
441 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom));
442 gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor));
443
444 gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor));
445 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundXPosition, &RenderStyle::setBackgroundXPosition));
446 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundYPosition, &RenderStyle::setBackgroundYPosition));
447 gPropertyWrappers->append(new PropertyWrapper<LengthSize>(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundSize, &RenderStyle::setBackgroundSize));
448
449 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskXPosition, &RenderStyle::setMaskXPosition));
450 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskYPosition, &RenderStyle::setMaskYPosition));
451 gPropertyWrappers->append(new PropertyWrapper<LengthSize>(CSSPropertyWebkitMaskSize, &RenderStyle::maskSize, &RenderStyle::setMaskSize));
452
453 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyFontSize, &RenderStyle::fontSize, &RenderStyle::setBlendedFontSize));
454 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth));
455 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap));
456 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount));
457 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth));
458 gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing));
459 gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing));
460 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex));
461 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::lineHeight, &RenderStyle::setLineHeight));
462 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset));
463 gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth));
464 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing));
465 gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing));
466 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent));
467
468 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective));
469 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX));
470 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY));
471 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX));
472 gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY));
473 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ));
474 gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius));
475 gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius));
476 gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius));
477 gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius));
478 gPropertyWrappers->append(new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility));
479 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoom));
480
481 #if USE(ACCELERATED_COMPOSITING)
482 gPropertyWrappers->append(new PropertyWrapperAcceleratedOpacity());
483 gPropertyWrappers->append(new PropertyWrapperAcceleratedTransform());
484 #else
485 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity));
486 gPropertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform));
487 #endif
488
489 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitColumnRuleColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor));
490 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextStrokeColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor));
491 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextFillColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor));
492 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderLeftColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor));
493 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderRightColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor));
494 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderTopColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor));
495 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderBottomColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor));
496 gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyOutlineColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor));
497
498 // These are for shadows
499 gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow));
500 gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow));
501
502 #if ENABLE(SVG)
503 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity));
504 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity));
505 gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity));
506 #endif
507
508 // TODO:
509 //
510 // CSSPropertyVerticalAlign
511 //
512 // Compound properties that have components that should be animatable:
513 //
514 // CSSPropertyWebkitColumns
515 // CSSPropertyWebkitBoxReflect
516
517 // Make sure unused slots have a value
518 for (unsigned int i = 0; i < static_cast<unsigned int>(numCSSProperties); ++i)
519 gPropertyWrapperMap[i] = cInvalidPropertyWrapperIndex;
520
521 // First we put the non-shorthand property wrappers into the map, so the shorthand-building
522 // code can find them.
523 size_t n = gPropertyWrappers->size();
524 for (unsigned int i = 0; i < n; ++i) {
525 ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties);
526 gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i;
527 }
528
529 // Now add the shorthand wrappers.
530 addShorthandProperties();
531 }
532 }
533
addPropertyWrapper(int propertyID,PropertyWrapperBase * wrapper)534 static void addPropertyWrapper(int propertyID, PropertyWrapperBase* wrapper)
535 {
536 int propIndex = propertyID - firstCSSProperty;
537
538 ASSERT(gPropertyWrapperMap[propIndex] == cInvalidPropertyWrapperIndex);
539
540 unsigned wrapperIndex = gPropertyWrappers->size();
541 gPropertyWrappers->append(wrapper);
542 gPropertyWrapperMap[propIndex] = wrapperIndex;
543 }
544
addShorthandProperties()545 static void addShorthandProperties()
546 {
547 static const int animatableShorthandProperties[] = {
548 CSSPropertyBackground, // for background-color, background-position
549 CSSPropertyBackgroundPosition,
550 CSSPropertyWebkitMask, // for mask-position
551 CSSPropertyWebkitMaskPosition,
552 CSSPropertyBorderTop, CSSPropertyBorderRight, CSSPropertyBorderBottom, CSSPropertyBorderLeft,
553 CSSPropertyBorderColor,
554 CSSPropertyBorderWidth,
555 CSSPropertyBorder,
556 CSSPropertyBorderSpacing,
557 CSSPropertyMargin,
558 CSSPropertyOutline,
559 CSSPropertyPadding,
560 CSSPropertyWebkitTextStroke,
561 CSSPropertyWebkitColumnRule,
562 CSSPropertyWebkitBorderRadius,
563 CSSPropertyWebkitTransformOrigin
564 };
565
566 for (unsigned i = 0; i < sizeof(animatableShorthandProperties) / sizeof(animatableShorthandProperties[0]); ++i) {
567 int propertyID = animatableShorthandProperties[i];
568 CSSPropertyLonghand longhand = longhandForProperty(propertyID);
569 if (longhand.length() > 0)
570 addPropertyWrapper(propertyID, new ShorthandPropertyWrapper(propertyID, longhand));
571 }
572
573 // 'font' is not in the shorthand map.
574 static const int animatableFontProperties[] = {
575 CSSPropertyFontSize,
576 CSSPropertyFontWeight
577 };
578
579 CSSPropertyLonghand fontLonghand(animatableFontProperties, sizeof(animatableFontProperties) / sizeof(animatableFontProperties[0]));
580 addPropertyWrapper(CSSPropertyFont, new ShorthandPropertyWrapper(CSSPropertyFont, fontLonghand));
581 }
582
wrapperForProperty(int propertyID)583 static PropertyWrapperBase* wrapperForProperty(int propertyID)
584 {
585 int propIndex = propertyID - firstCSSProperty;
586 if (propIndex >= 0 && propIndex < numCSSProperties) {
587 int wrapperIndex = gPropertyWrapperMap[propIndex];
588 if (wrapperIndex >= 0)
589 return (*gPropertyWrappers)[wrapperIndex];
590 }
591 return 0;
592 }
593
AnimationBase(const Animation * transition,RenderObject * renderer,CompositeAnimation * compAnim)594 AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim)
595 : m_animState(AnimationStateNew)
596 , m_isAnimating(false)
597 , m_startTime(0)
598 , m_pauseTime(-1)
599 , m_requestedStartTime(0)
600 , m_object(renderer)
601 , m_animation(const_cast<Animation*>(transition))
602 , m_compAnim(compAnim)
603 , m_fallbackAnimating(false)
604 , m_transformFunctionListValid(false)
605 , m_nextIterationDuration(-1)
606 , m_next(0)
607 {
608 // Compute the total duration
609 m_totalDuration = -1;
610 if (m_animation->iterationCount() > 0)
611 m_totalDuration = m_animation->duration() * m_animation->iterationCount();
612 }
613
~AnimationBase()614 AnimationBase::~AnimationBase()
615 {
616 m_compAnim->animationController()->removeFromStyleAvailableWaitList(this);
617 m_compAnim->animationController()->removeFromStartTimeResponseWaitList(this);
618 }
619
propertiesEqual(int prop,const RenderStyle * a,const RenderStyle * b)620 bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b)
621 {
622 ensurePropertyMap();
623 if (prop == cAnimateAll) {
624 size_t n = gPropertyWrappers->size();
625 for (unsigned int i = 0; i < n; ++i) {
626 PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
627 // No point comparing shorthand wrappers for 'all'.
628 if (!wrapper->isShorthandWrapper() && !wrapper->equals(a, b))
629 return false;
630 }
631 } else {
632 PropertyWrapperBase* wrapper = wrapperForProperty(prop);
633 if (wrapper)
634 return wrapper->equals(a, b);
635 }
636 return true;
637 }
638
getPropertyAtIndex(int i,bool & isShorthand)639 int AnimationBase::getPropertyAtIndex(int i, bool& isShorthand)
640 {
641 ensurePropertyMap();
642 if (i < 0 || i >= static_cast<int>(gPropertyWrappers->size()))
643 return CSSPropertyInvalid;
644
645 PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i];
646 isShorthand = wrapper->isShorthandWrapper();
647 return wrapper->property();
648 }
649
getNumProperties()650 int AnimationBase::getNumProperties()
651 {
652 ensurePropertyMap();
653 return gPropertyWrappers->size();
654 }
655
656 // 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)657 bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress)
658 {
659 ASSERT(prop != cAnimateAll);
660
661 ensurePropertyMap();
662 PropertyWrapperBase* wrapper = wrapperForProperty(prop);
663 if (wrapper) {
664 wrapper->blend(anim, dst, a, b, progress);
665 #if USE(ACCELERATED_COMPOSITING)
666 return !wrapper->animationIsAccelerated() || anim->isFallbackAnimating();
667 #else
668 return true;
669 #endif
670 }
671
672 return false;
673 }
674
675 #if USE(ACCELERATED_COMPOSITING)
animationOfPropertyIsAccelerated(int prop)676 bool AnimationBase::animationOfPropertyIsAccelerated(int prop)
677 {
678 ensurePropertyMap();
679 PropertyWrapperBase* wrapper = wrapperForProperty(prop);
680 return wrapper ? wrapper->animationIsAccelerated() : false;
681 }
682 #endif
683
setNeedsStyleRecalc(Node * node)684 void AnimationBase::setNeedsStyleRecalc(Node* node)
685 {
686 ASSERT(!node || (node->document() && !node->document()->inPageCache()));
687 if (node)
688 node->setNeedsStyleRecalc(AnimationStyleChange);
689 }
690
duration() const691 double AnimationBase::duration() const
692 {
693 return m_animation->duration();
694 }
695
playStatePlaying() const696 bool AnimationBase::playStatePlaying() const
697 {
698 return m_animation->playState() == AnimPlayStatePlaying;
699 }
700
animationsMatch(const Animation * anim) const701 bool AnimationBase::animationsMatch(const Animation* anim) const
702 {
703 return m_animation->animationsMatch(anim);
704 }
705
updateStateMachine(AnimStateInput input,double param)706 void AnimationBase::updateStateMachine(AnimStateInput input, double param)
707 {
708 // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state.
709 if (input == AnimationStateInputMakeNew) {
710 if (m_animState == AnimationStateStartWaitStyleAvailable)
711 m_compAnim->animationController()->removeFromStyleAvailableWaitList(this);
712 m_animState = AnimationStateNew;
713 m_startTime = 0;
714 m_pauseTime = -1;
715 m_requestedStartTime = 0;
716 m_nextIterationDuration = -1;
717 endAnimation(false);
718 return;
719 }
720
721 if (input == AnimationStateInputRestartAnimation) {
722 if (m_animState == AnimationStateStartWaitStyleAvailable)
723 m_compAnim->animationController()->removeFromStyleAvailableWaitList(this);
724 m_animState = AnimationStateNew;
725 m_startTime = 0;
726 m_pauseTime = -1;
727 m_requestedStartTime = 0;
728 m_nextIterationDuration = -1;
729 endAnimation(false);
730
731 if (!paused())
732 updateStateMachine(AnimationStateInputStartAnimation, -1);
733 return;
734 }
735
736 if (input == AnimationStateInputEndAnimation) {
737 if (m_animState == AnimationStateStartWaitStyleAvailable)
738 m_compAnim->animationController()->removeFromStyleAvailableWaitList(this);
739 m_animState = AnimationStateDone;
740 endAnimation(true);
741 return;
742 }
743
744 if (input == AnimationStateInputPauseOverride) {
745 if (m_animState == AnimationStateStartWaitResponse) {
746 // If we are in AnimationStateStartWaitResponse, the animation will get canceled before
747 // we get a response, so move to the next state.
748 endAnimation(false);
749 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
750 }
751 return;
752 }
753
754 if (input == AnimationStateInputResumeOverride) {
755 if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) {
756 // Start the animation
757 startAnimation(m_startTime);
758 }
759 return;
760 }
761
762 // Execute state machine
763 switch (m_animState) {
764 case AnimationStateNew:
765 ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunnning || input == AnimationStateInputPlayStatePaused);
766 if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunnning) {
767 m_requestedStartTime = beginAnimationUpdateTime();
768 m_animState = AnimationStateStartWaitTimer;
769 }
770 break;
771 case AnimationStateStartWaitTimer:
772 ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused);
773
774 if (input == AnimationStateInputStartTimerFired) {
775 ASSERT(param >= 0);
776 // Start timer has fired, tell the animation to start and wait for it to respond with start time
777 m_animState = AnimationStateStartWaitStyleAvailable;
778 m_compAnim->animationController()->addToStyleAvailableWaitList(this);
779
780 // Trigger a render so we can start the animation
781 if (m_object)
782 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
783 } else {
784 ASSERT(!paused());
785 // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
786 m_pauseTime = beginAnimationUpdateTime();
787 m_animState = AnimationStatePausedWaitTimer;
788 }
789 break;
790 case AnimationStateStartWaitStyleAvailable:
791 ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused);
792
793 // Start timer has fired, tell the animation to start and wait for it to respond with start time
794 m_animState = AnimationStateStartWaitResponse;
795
796 overrideAnimations();
797
798 // Send start event, if needed
799 onAnimationStart(0); // The elapsedTime is always 0 here
800
801 // Start the animation
802 if (overridden()) {
803 // We won't try to start accelerated animations if we are overridden and
804 // just move on to the next state.
805 m_animState = AnimationStateStartWaitResponse;
806 m_fallbackAnimating = true;
807 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
808 }
809 else {
810 bool started = startAnimation(0);
811 m_compAnim->animationController()->addToStartTimeResponseWaitList(this, started);
812 m_fallbackAnimating = !started;
813 }
814 break;
815 case AnimationStateStartWaitResponse:
816 ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused);
817
818 if (input == AnimationStateInputStartTimeSet) {
819 ASSERT(param >= 0);
820 // We have a start time, set it, unless the startTime is already set
821 if (m_startTime <= 0)
822 m_startTime = param;
823
824 // Decide whether to go into looping or ending state
825 goIntoEndingOrLoopingState();
826
827 // Dispatch updateStyleIfNeeded so we can start the animation
828 if (m_object)
829 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
830 } else {
831 // We are pausing while waiting for a start response. Cancel the animation and wait. When
832 // we unpause, we will act as though the start timer just fired
833 m_pauseTime = -1;
834 endAnimation(false);
835 m_animState = AnimationStatePausedWaitResponse;
836 }
837 break;
838 case AnimationStateLooping:
839 ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused);
840
841 if (input == AnimationStateInputLoopTimerFired) {
842 ASSERT(param >= 0);
843 // Loop timer fired, loop again or end.
844 onAnimationIteration(param);
845
846 // Decide whether to go into looping or ending state
847 goIntoEndingOrLoopingState();
848 } else {
849 // We are pausing while running. Cancel the animation and wait
850 m_pauseTime = beginAnimationUpdateTime();
851 endAnimation(false);
852 m_animState = AnimationStatePausedRun;
853 }
854 break;
855 case AnimationStateEnding:
856 ASSERT(input == AnimationStateInputEndTimerFired || input == AnimationStateInputPlayStatePaused);
857
858 if (input == AnimationStateInputEndTimerFired) {
859 ASSERT(param >= 0);
860 // End timer fired, finish up
861 onAnimationEnd(param);
862
863 m_animState = AnimationStateDone;
864
865 if (m_object) {
866 resumeOverriddenAnimations();
867
868 // Fire off another style change so we can set the final value
869 m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
870 }
871 } else {
872 // We are pausing while running. Cancel the animation and wait
873 m_pauseTime = beginAnimationUpdateTime();
874 endAnimation(false);
875 m_animState = AnimationStatePausedRun;
876 }
877 // |this| may be deleted here
878 break;
879 case AnimationStatePausedWaitTimer:
880 ASSERT(input == AnimationStateInputPlayStateRunnning);
881 ASSERT(paused());
882 // Update the times
883 m_startTime += beginAnimationUpdateTime() - m_pauseTime;
884 m_pauseTime = -1;
885
886 // we were waiting for the start timer to fire, go back and wait again
887 m_animState = AnimationStateNew;
888 updateStateMachine(AnimationStateInputStartAnimation, 0);
889 break;
890 case AnimationStatePausedWaitResponse:
891 case AnimationStatePausedRun:
892 // We treat these two cases the same. The only difference is that, when we are in
893 // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
894 // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
895 // that we have already set the startTime and will ignore it.
896 ASSERT(input == AnimationStateInputPlayStateRunnning || input == AnimationStateInputStartTimeSet);
897 ASSERT(paused());
898
899 // If we are paused, but we get the callback that notifies us that an accelerated animation started,
900 // then we ignore the start time and just move into the paused-run state.
901 if (m_animState == AnimationStatePausedWaitResponse && input == AnimationStateInputStartTimeSet) {
902 m_animState = AnimationStatePausedRun;
903 ASSERT(m_startTime == 0);
904 m_startTime = param;
905 m_pauseTime += m_startTime;
906 break;
907 }
908
909 // Update the times
910 if (m_animState == AnimationStatePausedRun)
911 m_startTime += beginAnimationUpdateTime() - m_pauseTime;
912 else
913 m_startTime = 0;
914 m_pauseTime = -1;
915
916 // We were waiting for a begin time response from the animation, go back and wait again
917 m_animState = AnimationStateStartWaitResponse;
918
919 // Start the animation
920 if (overridden()) {
921 // We won't try to start accelerated animations if we are overridden and
922 // just move on to the next state.
923 updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
924 m_fallbackAnimating = true;
925 } else {
926 bool started = startAnimation(m_startTime);
927 m_compAnim->animationController()->addToStartTimeResponseWaitList(this, started);
928 m_fallbackAnimating = !started;
929 }
930 break;
931 case AnimationStateDone:
932 // We're done. Stay in this state until we are deleted
933 break;
934 }
935 }
936
fireAnimationEventsIfNeeded()937 void AnimationBase::fireAnimationEventsIfNeeded()
938 {
939 // If we are waiting for the delay time to expire and it has, go to the next state
940 if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding)
941 return;
942
943 // We have to make sure to keep a ref to the this pointer, because it could get destroyed
944 // during an animation callback that might get called. Since the owner is a CompositeAnimation
945 // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase
946 // can still access the resources of its CompositeAnimation as needed.
947 RefPtr<AnimationBase> protector(this);
948 RefPtr<CompositeAnimation> compProtector(m_compAnim);
949
950 // Check for start timeout
951 if (m_animState == AnimationStateStartWaitTimer) {
952 if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay())
953 updateStateMachine(AnimationStateInputStartTimerFired, 0);
954 return;
955 }
956
957 double elapsedDuration = beginAnimationUpdateTime() - m_startTime;
958 // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that
959 // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate().
960 // Also check in getTimeToNextEvent().
961 elapsedDuration = max(elapsedDuration, 0.0);
962
963 // Check for end timeout
964 if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) {
965 // Fire an end event
966 updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration);
967 } else {
968 // Check for iteration timeout
969 if (m_nextIterationDuration < 0) {
970 // Hasn't been set yet, set it
971 double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
972 m_nextIterationDuration = elapsedDuration + durationLeft;
973 }
974
975 if (elapsedDuration >= m_nextIterationDuration) {
976 // Set to the next iteration
977 double previous = m_nextIterationDuration;
978 double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
979 m_nextIterationDuration = elapsedDuration + durationLeft;
980
981 // Send the event
982 updateStateMachine(AnimationStateInputLoopTimerFired, previous);
983 }
984 }
985 }
986
updatePlayState(bool run)987 void AnimationBase::updatePlayState(bool run)
988 {
989 if (paused() == run || isNew())
990 updateStateMachine(run ? AnimationStateInputPlayStateRunnning : AnimationStateInputPlayStatePaused, -1);
991 }
992
timeToNextService()993 double AnimationBase::timeToNextService()
994 {
995 // Returns the time at which next service is required. -1 means no service is required. 0 means
996 // service is required now, and > 0 means service is required that many seconds in the future.
997 if (paused() || isNew())
998 return -1;
999
1000 if (m_animState == AnimationStateStartWaitTimer) {
1001 double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime);
1002 return max(timeFromNow, 0.0);
1003 }
1004
1005 fireAnimationEventsIfNeeded();
1006
1007 // In all other cases, we need service right away.
1008 return 0;
1009 }
1010
progress(double scale,double offset,const TimingFunction * tf) const1011 double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const
1012 {
1013 if (preActive())
1014 return 0;
1015
1016 double elapsedTime = getElapsedTime();
1017
1018 double dur = m_animation->duration();
1019 if (m_animation->iterationCount() > 0)
1020 dur *= m_animation->iterationCount();
1021
1022 if (postActive() || !m_animation->duration() || (m_animation->iterationCount() > 0 && elapsedTime >= dur))
1023 return 1.0;
1024
1025 // Compute the fractional time, taking into account direction.
1026 // There is no need to worry about iterations, we assume that we would have
1027 // short circuited above if we were done.
1028 double fractionalTime = elapsedTime / m_animation->duration();
1029 int integralTime = static_cast<int>(fractionalTime);
1030 fractionalTime -= integralTime;
1031
1032 if (m_animation->direction() && (integralTime & 1))
1033 fractionalTime = 1 - fractionalTime;
1034
1035 if (scale != 1 || offset)
1036 fractionalTime = (fractionalTime - offset) * scale;
1037
1038 if (!tf)
1039 tf = &m_animation->timingFunction();
1040
1041 if (tf->type() == LinearTimingFunction)
1042 return fractionalTime;
1043
1044 // Cubic bezier.
1045 double result = solveCubicBezierFunction(tf->x1(),
1046 tf->y1(),
1047 tf->x2(),
1048 tf->y2(),
1049 fractionalTime, m_animation->duration());
1050 return result;
1051 }
1052
getTimeToNextEvent(double & time,bool & isLooping) const1053 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
1054 {
1055 // Decide when the end or loop event needs to fire
1056 double totalDuration = -1;
1057 if (m_animation->iterationCount() > 0)
1058 totalDuration = m_animation->duration() * m_animation->iterationCount();
1059
1060 const double elapsedDuration = max(beginAnimationUpdateTime() - m_startTime, 0.0);
1061 double durationLeft = 0;
1062 double nextIterationTime = m_totalDuration;
1063
1064 if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) {
1065 durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0;
1066 nextIterationTime = elapsedDuration + durationLeft;
1067 }
1068
1069 if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) {
1070 // We are not at the end yet
1071 ASSERT(nextIterationTime > 0);
1072 isLooping = true;
1073 } else {
1074 // We are at the end
1075 isLooping = false;
1076 }
1077
1078 time = durationLeft;
1079 }
1080
goIntoEndingOrLoopingState()1081 void AnimationBase::goIntoEndingOrLoopingState()
1082 {
1083 double t;
1084 bool isLooping;
1085 getTimeToNextEvent(t, isLooping);
1086 m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
1087 }
1088
freezeAtTime(double t)1089 void AnimationBase::freezeAtTime(double t)
1090 {
1091 ASSERT(m_startTime); // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
1092 m_pauseTime = m_startTime + t - m_animation->delay();
1093
1094 #if USE(ACCELERATED_COMPOSITING)
1095 if (m_object && m_object->hasLayer()) {
1096 RenderLayer* layer = toRenderBoxModelObject(m_object)->layer();
1097 if (layer->isComposited())
1098 layer->backing()->suspendAnimations(m_pauseTime);
1099 }
1100 #endif
1101 }
1102
beginAnimationUpdateTime() const1103 double AnimationBase::beginAnimationUpdateTime() const
1104 {
1105 return m_compAnim->animationController()->beginAnimationUpdateTime();
1106 }
1107
getElapsedTime() const1108 double AnimationBase::getElapsedTime() const
1109 {
1110 if (paused())
1111 return m_pauseTime - m_startTime;
1112 if (m_startTime <= 0)
1113 return 0;
1114 if (postActive())
1115 return 1;
1116 return beginAnimationUpdateTime() - m_startTime;
1117 }
1118
1119 } // namespace WebCore
1120