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