255feac7db7e9c3aadb64a2b8d50b061a63feac0
[WebKit-https.git] / WebCore / page / AnimationController.cpp
1 /*
2  * Copyright (C) 2007 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 "AnimationController.h"
31
32 #include "CSSPropertyNames.h"
33 #include "Document.h"
34 #include "FloatConversion.h"
35 #include "Frame.h"
36 #include "RenderObject.h"
37 #include "RenderStyle.h"
38 #include "SystemTime.h"
39 #include "Timer.h"
40
41 namespace WebCore {
42
43 static const double cAnimationTimerDelay = 0.025;
44
45 struct CurveData {
46     CurveData(double p1x, double p1y, double p2x, double p2y)
47     {
48         // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
49         cx = 3.0 * p1x;
50         bx = 3.0 * (p2x - p1x) - cx;
51         ax = 1.0 - cx -bx;
52          
53         cy = 3.0 * p1y;
54         by = 3.0 * (p2y - p1y) - cy;
55         ay = 1.0 - cy - by;
56     }
57     
58     double sampleCurveX(double t)
59     {
60         // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
61         return ((ax * t + bx) * t + cx) * t;
62     }
63     
64     double sampleCurveY(double t)
65     {
66         return ((ay * t + by) * t + cy) * t;
67     }
68     
69     double sampleCurveDerivativeX(double t)
70     {
71         return (3.0 * ax * t + 2.0 * bx) * t + cx;
72     }
73     
74     // Given an x value, find a parametric value it came from.
75     double solveCurveX(double x, double epsilon)
76     {
77         double t0;
78         double t1;
79         double t2;
80         double x2;
81         double d2;
82         int i;
83
84         // First try a few iterations of Newton's method -- normally very fast.
85         for (t2 = x, i = 0; i < 8; i++) {
86             x2 = sampleCurveX(t2) - x;
87             if (fabs (x2) < epsilon)
88                 return t2;
89             d2 = sampleCurveDerivativeX(t2);
90             if (fabs(d2) < 1e-6)
91                 break;
92             t2 = t2 - x2 / d2;
93         }
94
95         // Fall back to the bisection method for reliability.
96         t0 = 0.0;
97         t1 = 1.0;
98         t2 = x;
99
100         if (t2 < t0)
101             return t0;
102         if (t2 > t1)
103             return t1;
104
105         while (t0 < t1) {
106             x2 = sampleCurveX(t2);
107             if (fabs(x2 - x) < epsilon)
108                 return t2;
109             if (x > x2)
110                 t0 = t2;
111             else
112                 t1 = t2;
113             t2 = (t1 - t0) * .5 + t0;
114         }
115
116         // Failure.
117         return t2;
118     }
119     
120 private:
121     double ax;
122     double bx;
123     double cx;
124     
125     double ay;
126     double by;
127     double cy;
128 };
129
130 // The epsilon value we pass to solveCurveX given that the animation is going to run over |dur| seconds. The longer the
131 // animation, the more precision we need in the timing function result to avoid ugly discontinuities.
132 static inline double solveEpsilon(double duration) { return 1. / (200. * duration); }
133
134 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
135 {
136     // Convert from input time to parametric value in curve, then from
137     // that to output time.
138     CurveData c(p1x, p1y, p2x, p2y);
139     t = c.solveCurveX(t, solveEpsilon(duration));
140     t = c.sampleCurveY(t);
141     return t;
142 }
143
144 class CompositeImplicitAnimation;
145
146 class ImplicitAnimation : public Noncopyable {
147 public:
148     ImplicitAnimation(const Transition*);
149     ~ImplicitAnimation();
150
151     void animate(CompositeImplicitAnimation*, RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle);
152
153     void reset(RenderObject*, RenderStyle* from, RenderStyle* to);
154     
155     double progress() const;
156
157     bool finished() const { return m_finished; }
158
159 private:
160     // The two styles that we are blending.
161     RenderStyle* m_fromStyle;
162     RenderStyle* m_toStyle;
163
164     int m_property;
165     TimingFunction m_function;
166     double m_duration;
167
168     int m_repeatCount;
169     int m_iteration;
170     
171     bool m_finished;    
172     double m_startTime;
173     bool m_paused;
174     double m_pauseTime;
175 };
176
177 class CompositeImplicitAnimation : public Noncopyable {
178 public:
179     ~CompositeImplicitAnimation() { deleteAllValues(m_animations); }
180
181     RenderStyle* animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
182
183     bool animating() const;
184
185     bool hasAnimationForProperty(int prop) const { return m_animations.contains(prop); }
186
187     void reset(RenderObject*);
188
189 private:
190     HashMap<int, ImplicitAnimation*> m_animations;
191 };
192
193 ImplicitAnimation::ImplicitAnimation(const Transition* transition)
194 : m_fromStyle(0)
195 , m_toStyle(0)
196 , m_property(transition->transitionProperty())
197 , m_function(transition->transitionTimingFunction())
198 , m_duration(transition->transitionDuration() / 1000.0)
199 , m_repeatCount(transition->transitionRepeatCount())
200 , m_iteration(0)
201 , m_finished(false)
202 , m_startTime(currentTime())
203 , m_paused(false)
204 , m_pauseTime(m_startTime)
205 {
206 }
207
208 ImplicitAnimation::~ImplicitAnimation()
209 {
210     ASSERT(!m_fromStyle && !m_toStyle);
211 }
212
213 void ImplicitAnimation::reset(RenderObject* renderer, RenderStyle* from, RenderStyle* to)
214 {
215     if (m_fromStyle)
216         m_fromStyle->deref(renderer->renderArena());
217     if (m_toStyle)
218         m_toStyle->deref(renderer->renderArena());
219     m_fromStyle = from;
220     if (m_fromStyle)
221         m_fromStyle->ref();
222     m_toStyle = to;
223     if (m_toStyle)
224         m_toStyle->ref();
225     m_finished = false;
226     if (from || to)
227         m_startTime = currentTime();
228 }
229
230 double ImplicitAnimation::progress() const
231 {
232     double elapsedTime = currentTime() - m_startTime;
233     
234     if (m_finished || !m_duration || elapsedTime >= m_duration)
235         return 1.0;
236
237     if (m_function.type() == LinearTimingFunction)
238         return elapsedTime / m_duration;
239     
240     // Cubic bezier.
241     return solveCubicBezierFunction(m_function.x1(), m_function.y1(), 
242                                     m_function.x2(), m_function.y2(),
243                                     elapsedTime / m_duration, m_duration);
244 }
245
246 static inline int blendFunc(int from, int to, double progress)
247 {  
248     return int(from + (to - from) * progress);
249 }
250
251 static inline double blendFunc(double from, double to, double progress)
252 {  
253     return from + (to - from) * progress;
254 }
255
256 static inline float blendFunc(float from, float to, double progress)
257 {  
258     return narrowPrecisionToFloat(from + (to - from) * progress);
259 }
260
261 static inline Color blendFunc(const Color& from, const Color& to, double progress)
262 {  
263     return Color(blendFunc(from.red(), to.red(), progress),
264                  blendFunc(from.green(), to.green(), progress),
265                  blendFunc(from.blue(), to.blue(), progress),
266                  blendFunc(from.alpha(), to.alpha(), progress));
267 }
268
269 static inline Length blendFunc(const Length& from, const Length& to, double progress)
270 {  
271     return to.blend(from, progress);
272 }
273
274 static inline IntSize blendFunc(const IntSize& from, const IntSize& to, double progress)
275 {  
276     return IntSize(blendFunc(from.width(), to.width(), progress),
277                    blendFunc(from.height(), to.height(), progress));
278 }
279
280 static inline ShadowData* blendFunc(const ShadowData* from, const ShadowData* to, double progress)
281 {  
282     ASSERT(from && to);
283     return new ShadowData(blendFunc(from->x, to->x, progress), blendFunc(from->y, to->y, progress), blendFunc(from->blur, to->blur, progress), blendFunc(from->color, to->color, progress));
284 }
285
286 static inline TransformOperations blendFunc(const TransformOperations& from, const TransformOperations& to, double progress)
287 {    
288     // Blend any operations whose types actually match up.  Otherwise don't bother.
289     unsigned fromSize = from.size();
290     unsigned toSize = to.size();
291     unsigned size = max(fromSize, toSize);
292     TransformOperations result;
293     for (unsigned i = 0; i < size; i++) {
294         TransformOperation* fromOp = i < fromSize ? from[i].get() : 0;
295         TransformOperation* toOp = i < toSize ? to[i].get() : 0;
296         TransformOperation* blendedOp = toOp ? toOp->blend(fromOp, progress) : fromOp->blend(0, progress, true);
297         result.append(blendedOp);
298     }
299     return result;
300 }
301
302 static inline EVisibility blendFunc(EVisibility from, EVisibility to, double progress)
303 {
304     if (from == to || from != VISIBLE && to != VISIBLE)
305         return to;
306     
307     // Any non-zero result means we consider the object to be visible.  Only at 0 do we consider the object to be
308     // invisible.   The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values.
309     double fromVal = from == VISIBLE ? 1. : 0.;
310     double toVal = to == VISIBLE ? 1. : 0.;
311     double result = blendFunc(fromVal, toVal, progress);
312     return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
313 }
314
315 #define BLEND(prop, getter, setter) \
316     if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) \
317         reset(renderer, currentStyle, targetStyle); \
318     \
319     if ((m_property == cAnimateAll && !animation->hasAnimationForProperty(prop)) || m_property == prop) { \
320         if (m_fromStyle->getter() != m_toStyle->getter()) {\
321             m_finished = false; \
322             if (!animatedStyle) \
323                 animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
324             animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\
325             if (m_property == prop) \
326                 return; \
327         }\
328     }\
329
330 #define BLEND_SHADOW(prop, getter, setter) \
331     if (m_property == prop && (!m_toStyle->getter() || !targetStyle->getter() || *m_toStyle->getter() != *targetStyle->getter())) \
332         reset(renderer, currentStyle, targetStyle); \
333     \
334     if ((m_property == cAnimateAll && !animation->hasAnimationForProperty(prop)) || m_property == prop) { \
335         if (m_fromStyle->getter() && m_toStyle->getter() && *m_fromStyle->getter() != *m_toStyle->getter()) {\
336             m_finished = false; \
337             if (!animatedStyle) \
338                 animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
339             animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\
340             if (m_property == prop) \
341                 return; \
342         }\
343     }
344
345 void ImplicitAnimation::animate(CompositeImplicitAnimation* animation, RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle)
346 {
347     // FIXME: If we have no transition-property, then the only way to tell if our goal state changed is to check
348     // every single animatable property.  For now we'll just diff the styles to ask that question,
349     // but we should really exclude non-animatable properties.
350     if (!m_toStyle || (m_property == cAnimateAll && targetStyle->diff(m_toStyle)))
351         reset(renderer, currentStyle, targetStyle);
352
353     // FIXME: Blow up shorthands so that they can be honored.
354     m_finished = true;
355     BLEND(CSS_PROP_LEFT, left, setLeft);
356     BLEND(CSS_PROP_RIGHT, right, setRight);
357     BLEND(CSS_PROP_TOP, top, setTop);
358     BLEND(CSS_PROP_BOTTOM, bottom, setBottom);
359     BLEND(CSS_PROP_WIDTH, width, setWidth);
360     BLEND(CSS_PROP_HEIGHT, height, setHeight);
361     BLEND(CSS_PROP_BORDER_LEFT_WIDTH, borderLeftWidth, setBorderLeftWidth);
362     BLEND(CSS_PROP_BORDER_RIGHT_WIDTH, borderRightWidth, setBorderRightWidth);
363     BLEND(CSS_PROP_BORDER_TOP_WIDTH, borderTopWidth, setBorderTopWidth);
364     BLEND(CSS_PROP_BORDER_BOTTOM_WIDTH, borderBottomWidth, setBorderBottomWidth);
365     BLEND(CSS_PROP_MARGIN_LEFT, marginLeft, setMarginLeft);
366     BLEND(CSS_PROP_MARGIN_RIGHT, marginRight, setMarginRight);
367     BLEND(CSS_PROP_MARGIN_TOP, marginTop, setMarginTop);
368     BLEND(CSS_PROP_MARGIN_BOTTOM, marginBottom, setMarginBottom);
369     BLEND(CSS_PROP_PADDING_LEFT, paddingLeft, setPaddingLeft);
370     BLEND(CSS_PROP_PADDING_RIGHT, paddingRight, setPaddingRight);
371     BLEND(CSS_PROP_PADDING_TOP, paddingTop, setPaddingTop);
372     BLEND(CSS_PROP_PADDING_BOTTOM, paddingBottom, setPaddingBottom);
373     BLEND(CSS_PROP_OPACITY, opacity, setOpacity);
374     BLEND(CSS_PROP_COLOR, color, setColor);
375     BLEND(CSS_PROP_BACKGROUND_COLOR, backgroundColor, setBackgroundColor);
376     BLEND(CSS_PROP__WEBKIT_COLUMN_RULE_COLOR, columnRuleColor, setColumnRuleColor);
377     BLEND(CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH, columnRuleWidth, setColumnRuleWidth);
378     BLEND(CSS_PROP__WEBKIT_COLUMN_GAP, columnGap, setColumnGap);
379     BLEND(CSS_PROP__WEBKIT_COLUMN_COUNT, columnCount, setColumnCount);
380     BLEND(CSS_PROP__WEBKIT_COLUMN_WIDTH, columnWidth, setColumnWidth);
381     BLEND(CSS_PROP__WEBKIT_TEXT_STROKE_COLOR, textStrokeColor, setTextStrokeColor);
382     BLEND(CSS_PROP__WEBKIT_TEXT_FILL_COLOR, textFillColor, setTextFillColor);
383     BLEND(CSS_PROP__WEBKIT_BORDER_HORIZONTAL_SPACING, horizontalBorderSpacing, setHorizontalBorderSpacing);
384     BLEND(CSS_PROP__WEBKIT_BORDER_VERTICAL_SPACING, verticalBorderSpacing, setVerticalBorderSpacing);
385     BLEND(CSS_PROP_BORDER_LEFT_COLOR, borderLeftColor, setBorderLeftColor);
386     BLEND(CSS_PROP_BORDER_RIGHT_COLOR, borderRightColor, setBorderRightColor);
387     BLEND(CSS_PROP_BORDER_TOP_COLOR, borderTopColor, setBorderTopColor);
388     BLEND(CSS_PROP_BORDER_BOTTOM_COLOR, borderBottomColor, setBorderBottomColor);
389     BLEND(CSS_PROP_Z_INDEX, zIndex, setZIndex);
390     BLEND(CSS_PROP_LINE_HEIGHT, lineHeight, setLineHeight);
391     BLEND(CSS_PROP_OUTLINE_COLOR, outlineColor, setOutlineColor);
392     BLEND(CSS_PROP_OUTLINE_OFFSET, outlineOffset, setOutlineOffset);
393     BLEND(CSS_PROP_OUTLINE_WIDTH, outlineWidth, setOutlineWidth);
394     BLEND(CSS_PROP_LETTER_SPACING, letterSpacing, setLetterSpacing);
395     BLEND(CSS_PROP_WORD_SPACING, wordSpacing, setWordSpacing);
396     BLEND_SHADOW(CSS_PROP__WEBKIT_BOX_SHADOW, boxShadow, setBoxShadow);
397     BLEND_SHADOW(CSS_PROP_TEXT_SHADOW, textShadow, setTextShadow);
398     BLEND(CSS_PROP__WEBKIT_TRANSFORM, transform, setTransform);
399     BLEND(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_X, transformOriginX, setTransformOriginX);
400     BLEND(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_Y, transformOriginY, setTransformOriginY);
401     BLEND(CSS_PROP__WEBKIT_BORDER_TOP_LEFT_RADIUS, borderTopLeftRadius, setBorderTopLeftRadius);
402     BLEND(CSS_PROP__WEBKIT_BORDER_TOP_RIGHT_RADIUS, borderTopRightRadius, setBorderTopRightRadius);
403     BLEND(CSS_PROP__WEBKIT_BORDER_BOTTOM_LEFT_RADIUS, borderBottomLeftRadius, setBorderBottomLeftRadius);
404     BLEND(CSS_PROP__WEBKIT_BORDER_BOTTOM_RIGHT_RADIUS, borderBottomRightRadius, setBorderBottomRightRadius);
405     BLEND(CSS_PROP_VISIBILITY, visibility, setVisibility);
406 }
407
408 RenderStyle* CompositeImplicitAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
409 {
410     // Get the animation layers from the target style.
411     // For each one, we need to create a new animation unless one exists already (later occurrences of duplicate
412     // triggers in the layer list get ignored).
413     if (m_animations.isEmpty()) {
414         for (const Transition* transition = targetStyle->transitions(); transition; transition = transition->next()) {
415             int property = transition->transitionProperty();
416             int duration = transition->transitionDuration();
417             int repeatCount = transition->transitionRepeatCount();
418             if (property && duration && repeatCount && !m_animations.contains(property)) {
419                 ImplicitAnimation* animation = new ImplicitAnimation(transition);
420                 m_animations.set(property, animation);
421             }
422         }
423     }
424
425     // Now that we have animation objects ready, let them know about the new goal state.  We want them
426     // to fill in a RenderStyle*& only if needed.
427     RenderStyle* result = 0;
428     HashMap<int, ImplicitAnimation*>::iterator end = m_animations.end();
429     for (HashMap<int, ImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it)
430         it->second->animate(this, renderer, currentStyle, targetStyle, result);
431     
432     if (result)
433         return result;
434
435     return targetStyle;
436 }
437
438 bool CompositeImplicitAnimation::animating() const
439 {
440     HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end();
441     for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it)
442         if (!it->second->finished())
443             return true;
444     return false;
445 }
446
447 void CompositeImplicitAnimation::reset(RenderObject* renderer)
448 {
449     HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end();
450     for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it)
451         it->second->reset(renderer, 0, 0);
452 }
453
454 class AnimationControllerPrivate {
455 public:
456     AnimationControllerPrivate(Frame*);
457     ~AnimationControllerPrivate();
458
459     CompositeImplicitAnimation* get(RenderObject*);
460     bool clear(RenderObject*);
461     
462     void timerFired(Timer<AnimationControllerPrivate>*);
463     void updateTimer();
464
465     bool hasImplicitAnimations() const { return !m_animations.isEmpty(); }
466
467 private:
468     HashMap<RenderObject*, CompositeImplicitAnimation*> m_animations;
469     Timer<AnimationControllerPrivate> m_timer;
470     Frame* m_frame;
471 };
472
473 AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
474     : m_timer(this, &AnimationControllerPrivate::timerFired)
475     , m_frame(frame)
476 {
477 }
478
479 AnimationControllerPrivate::~AnimationControllerPrivate()
480 {
481     deleteAllValues(m_animations);
482 }
483
484 CompositeImplicitAnimation* AnimationControllerPrivate::get(RenderObject* renderer)
485 {
486     CompositeImplicitAnimation* animation = m_animations.get(renderer);
487     if (!animation) {
488         animation = new CompositeImplicitAnimation();
489         m_animations.set(renderer, animation);
490     }
491     return animation;
492 }
493
494 bool AnimationControllerPrivate::clear(RenderObject* renderer)
495 {
496     CompositeImplicitAnimation* animation = m_animations.take(renderer);
497     if (!animation)
498         return false;
499     animation->reset(renderer);
500     delete animation;
501     return true;
502 }
503
504 void AnimationControllerPrivate::updateTimer()
505 {
506     bool animating = false;
507     HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end();
508     for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
509         if (it->second->animating()) {
510             animating = true;
511             break;
512         }
513     }
514     
515     if (animating) {
516         if (!m_timer.isActive())
517             m_timer.startRepeating(cAnimationTimerDelay);
518     } else if (m_timer.isActive())
519         m_timer.stop();
520 }
521
522 void AnimationControllerPrivate::timerFired(Timer<AnimationControllerPrivate>* timer)
523 {
524     // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate
525     // updateRendering.  It will then call back to us with new information.
526     bool animating = false;
527     HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end();
528     for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
529         if (it->second->animating()) {
530             animating = true;
531             it->first->element()->setChanged();
532         }
533     }
534     
535     m_frame->document()->updateRendering();
536     
537     updateTimer();
538 }
539
540 AnimationController::AnimationController(Frame* frame)
541 :m_data(new AnimationControllerPrivate(frame))
542 {
543
544 }
545
546 AnimationController::~AnimationController()
547 {
548     delete m_data;
549 }
550
551 void AnimationController::cancelImplicitAnimations(RenderObject* renderer)
552 {
553     if (!m_data->hasImplicitAnimations())
554         return;
555
556     if (m_data->clear(renderer))
557         renderer->element()->setChanged();
558 }
559
560 RenderStyle* AnimationController::updateImplicitAnimations(RenderObject* renderer, RenderStyle* newStyle)
561 {
562     // Fetch our current set of implicit animations from a hashtable.  We then compare them
563     // against the animations in the style and make sure we're in sync.  If destination values
564     // have changed, we reset the animation.  We then do a blend to get new values and we return
565     // a new style.
566     ASSERT(renderer->element()); // FIXME: We do not animate generated content yet.
567     
568     CompositeImplicitAnimation* animation = m_data->get(renderer);
569     RenderStyle* result = animation->animate(renderer, renderer->style(), newStyle);
570     m_data->updateTimer();
571     return result;
572 }
573
574 void AnimationController::suspendAnimations()
575 {
576     // FIXME: Walk the whole hashtable and call pause on each animation.
577     // Kill our timer.
578 }
579
580 void AnimationController::resumeAnimations()
581 {
582     // FIXME: Walk the whole hashtable and call resume on each animation.
583     // Start our timer.
584 }
585
586 }