Reviewed by Sam.
[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 #include "UnitBezier.h"
41
42 namespace WebCore {
43
44 static const double cAnimationTimerDelay = 0.025;
45
46 // The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the
47 // animation, the more precision we need in the timing function result to avoid ugly discontinuities.
48 static inline double solveEpsilon(double duration) { return 1. / (200. * duration); }
49
50 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
51 {
52     // Convert from input time to parametric value in curve, then from
53     // that to output time.
54     UnitBezier bezier(p1x, p1y, p2x, p2y);
55     return bezier.solve(t, solveEpsilon(duration));
56 }
57
58 class CompositeImplicitAnimation;
59
60 class ImplicitAnimation : public Noncopyable {
61 public:
62     ImplicitAnimation(const Transition*);
63     ~ImplicitAnimation();
64
65     void animate(CompositeImplicitAnimation*, RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle);
66
67     void reset(RenderObject*, RenderStyle* from, RenderStyle* to);
68     
69     double progress() const;
70
71     int property() const { return m_property; }
72     
73     bool finished() const { return m_finished; }
74     void setFinished(bool f) { m_finished = f; }
75     
76     bool stale() const { return m_stale; }
77     void setStale() { m_stale = true; }
78
79 private:
80     // The two styles that we are blending.
81     RenderStyle* m_fromStyle;
82     RenderStyle* m_toStyle;
83
84     int m_property;
85     TimingFunction m_function;
86     double m_duration;
87
88     int m_repeatCount;
89     int m_iteration;
90     
91     bool m_finished;    
92     double m_startTime;
93     bool m_paused;
94     double m_pauseTime;
95     
96     bool m_stale;
97 };
98
99 class CompositeImplicitAnimation : public Noncopyable {
100 public:
101     ~CompositeImplicitAnimation() { deleteAllValues(m_animations); }
102
103     RenderStyle* animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
104
105     bool animating() const;
106
107     void reset(RenderObject*);
108
109 private:
110     HashMap<int, ImplicitAnimation*> m_animations;
111 };
112
113 ImplicitAnimation::ImplicitAnimation(const Transition* transition)
114 : m_fromStyle(0)
115 , m_toStyle(0)
116 , m_property(transition->property())
117 , m_function(transition->timingFunction())
118 , m_duration(transition->duration() / 1000.0)
119 , m_repeatCount(transition->repeatCount())
120 , m_iteration(0)
121 , m_finished(false)
122 , m_startTime(currentTime())
123 , m_paused(false)
124 , m_pauseTime(m_startTime)
125 , m_stale(false)
126 {
127 }
128
129 ImplicitAnimation::~ImplicitAnimation()
130 {
131     ASSERT(!m_fromStyle && !m_toStyle);
132 }
133
134 void ImplicitAnimation::reset(RenderObject* renderer, RenderStyle* from, RenderStyle* to)
135 {
136     setFinished(false);
137
138     if (m_fromStyle)
139         m_fromStyle->deref(renderer->renderArena());
140     if (m_toStyle)
141         m_toStyle->deref(renderer->renderArena());
142     m_fromStyle = from;
143     if (m_fromStyle)
144         m_fromStyle->ref();
145     m_toStyle = to;
146     if (m_toStyle)
147         m_toStyle->ref();
148     if (from || to)
149         m_startTime = currentTime();
150         
151     // If we are stale, attempt to update to a new transition using the new |to| style.  If we are able to find a new transition,
152     // then we will unmark ourselves for death.
153     if (stale() && from && to) {
154          for (const Transition* transition = to->transitions(); transition; transition = transition->next()) {
155             if (m_property != transition->property())
156                 continue;
157             int duration = transition->duration();
158             int repeatCount = transition->repeatCount();
159             if (duration && repeatCount) {
160                 m_duration = duration / 1000.0;
161                 m_repeatCount = repeatCount;
162                 m_stale = false;
163                 break;
164             }
165         }
166     }
167     
168     // If we did not find a new transition, then mark ourselves as being finished so that we can be removed.
169     if (stale())
170         setFinished(true);
171 }
172
173 double ImplicitAnimation::progress() const
174 {
175     double elapsedTime = currentTime() - m_startTime;
176     
177     if (m_finished || !m_duration || elapsedTime >= m_duration)
178         return 1.0;
179
180     if (m_function.type() == LinearTimingFunction)
181         return elapsedTime / m_duration;
182     
183     // Cubic bezier.
184     return solveCubicBezierFunction(m_function.x1(), m_function.y1(), 
185                                     m_function.x2(), m_function.y2(),
186                                     elapsedTime / m_duration, m_duration);
187 }
188
189 static inline int blendFunc(int from, int to, double progress)
190 {  
191     return int(from + (to - from) * progress);
192 }
193
194 static inline double blendFunc(double from, double to, double progress)
195 {  
196     return from + (to - from) * progress;
197 }
198
199 static inline float blendFunc(float from, float to, double progress)
200 {  
201     return narrowPrecisionToFloat(from + (to - from) * progress);
202 }
203
204 static inline Color blendFunc(const Color& from, const Color& to, double progress)
205 {  
206     return Color(blendFunc(from.red(), to.red(), progress),
207                  blendFunc(from.green(), to.green(), progress),
208                  blendFunc(from.blue(), to.blue(), progress),
209                  blendFunc(from.alpha(), to.alpha(), progress));
210 }
211
212 static inline Length blendFunc(const Length& from, const Length& to, double progress)
213 {  
214     return to.blend(from, progress);
215 }
216
217 static inline IntSize blendFunc(const IntSize& from, const IntSize& to, double progress)
218 {  
219     return IntSize(blendFunc(from.width(), to.width(), progress),
220                    blendFunc(from.height(), to.height(), progress));
221 }
222
223 static inline ShadowData* blendFunc(const ShadowData* from, const ShadowData* to, double progress)
224 {  
225     ASSERT(from && to);
226     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));
227 }
228
229 static inline TransformOperations blendFunc(const TransformOperations& from, const TransformOperations& to, double progress)
230 {    
231     // Blend any operations whose types actually match up.  Otherwise don't bother.
232     unsigned fromSize = from.size();
233     unsigned toSize = to.size();
234     unsigned size = max(fromSize, toSize);
235     TransformOperations result;
236     for (unsigned i = 0; i < size; i++) {
237         TransformOperation* fromOp = i < fromSize ? from[i].get() : 0;
238         TransformOperation* toOp = i < toSize ? to[i].get() : 0;
239         TransformOperation* blendedOp = toOp ? toOp->blend(fromOp, progress) : fromOp->blend(0, progress, true);
240         result.append(blendedOp);
241     }
242     return result;
243 }
244
245 static inline EVisibility blendFunc(EVisibility from, EVisibility to, double progress)
246 {
247     // Any non-zero result means we consider the object to be visible.  Only at 0 do we consider the object to be
248     // invisible.   The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values.
249     double fromVal = from == VISIBLE ? 1. : 0.;
250     double toVal = to == VISIBLE ? 1. : 0.;
251     if (fromVal == toVal)
252         return to;
253     double result = blendFunc(fromVal, toVal, progress);
254     return result > 0. ? VISIBLE : (to != VISIBLE ? to : from);
255 }
256
257 #define BLEND(prop, getter, setter) \
258     if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) {\
259         reset(renderer, currentStyle, targetStyle); \
260         if (stale()) \
261             return; \
262     } \
263     if (m_property == cAnimateAll || m_property == prop) { \
264         if (m_fromStyle->getter() != m_toStyle->getter()) {\
265             setFinished(false); \
266             if (!animatedStyle) \
267                 animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
268             animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\
269             if (m_property == prop) \
270                 return; \
271         }\
272     }\
273
274 #define BLEND_MAYBE_INVALID_COLOR(prop, getter, setter) \
275     if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) { \
276         reset(renderer, currentStyle, targetStyle); \
277         if (stale()) \
278             return; \
279     } \
280     if (m_property == cAnimateAll || m_property == prop) { \
281         Color fromColor = m_fromStyle->getter(); \
282         Color toColor = m_toStyle->getter(); \
283         if (fromColor.isValid() || toColor.isValid()) { \
284             if (!fromColor.isValid()) \
285                 fromColor = m_fromStyle->color(); \
286             if (!toColor.isValid()) \
287                 toColor = m_toStyle->color(); \
288             if (fromColor != toColor) {\
289                 setFinished(false); \
290                 if (!animatedStyle) \
291                     animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
292                 animatedStyle->setter(blendFunc(fromColor, toColor, progress()));\
293                 if (m_property == prop) \
294                     return; \
295             }\
296         }\
297     }\
298
299 #define BLEND_SHADOW(prop, getter, setter) \
300     if (m_property == prop && (!m_toStyle->getter() || !targetStyle->getter() || *m_toStyle->getter() != *targetStyle->getter())) { \
301         reset(renderer, currentStyle, targetStyle); \
302         if (stale()) \
303             return; \
304     } \
305     if (m_property == cAnimateAll || m_property == prop) { \
306         if (m_fromStyle->getter() && m_toStyle->getter() && *m_fromStyle->getter() != *m_toStyle->getter()) {\
307             setFinished(false); \
308             if (!animatedStyle) \
309                 animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \
310             animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\
311             if (m_property == prop) \
312                 return; \
313         }\
314     }
315
316 void ImplicitAnimation::animate(CompositeImplicitAnimation* animation, RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle)
317 {
318     // FIXME: If we have no transition-property, then the only way to tell if our goal state changed is to check
319     // every single animatable property.  For now we'll just diff the styles to ask that question,
320     // but we should really exclude non-animatable properties.
321     if (!m_toStyle || (m_property == cAnimateAll && targetStyle->diff(m_toStyle))) {
322         reset(renderer, currentStyle, targetStyle);
323         if (stale())
324             return;
325     }
326
327     // FIXME: Blow up shorthands so that they can be honored.
328     setFinished(true);
329     BLEND(CSSPropertyLeft, left, setLeft);
330     BLEND(CSSPropertyRight, right, setRight);
331     BLEND(CSSPropertyTop, top, setTop);
332     BLEND(CSSPropertyBottom, bottom, setBottom);
333     BLEND(CSSPropertyWidth, width, setWidth);
334     BLEND(CSSPropertyHeight, height, setHeight);
335     BLEND(CSSPropertyBorderLeftWidth, borderLeftWidth, setBorderLeftWidth);
336     BLEND(CSSPropertyBorderRightWidth, borderRightWidth, setBorderRightWidth);
337     BLEND(CSSPropertyBorderTopWidth, borderTopWidth, setBorderTopWidth);
338     BLEND(CSSPropertyBorderBottomWidth, borderBottomWidth, setBorderBottomWidth);
339     BLEND(CSSPropertyMarginLeft, marginLeft, setMarginLeft);
340     BLEND(CSSPropertyMarginRight, marginRight, setMarginRight);
341     BLEND(CSSPropertyMarginTop, marginTop, setMarginTop);
342     BLEND(CSSPropertyMarginBottom, marginBottom, setMarginBottom);
343     BLEND(CSSPropertyPaddingLeft, paddingLeft, setPaddingLeft);
344     BLEND(CSSPropertyPaddingRight, paddingRight, setPaddingRight);
345     BLEND(CSSPropertyPaddingTop, paddingTop, setPaddingTop);
346     BLEND(CSSPropertyPaddingBottom, paddingBottom, setPaddingBottom);
347     BLEND(CSSPropertyOpacity, opacity, setOpacity);
348     BLEND(CSSPropertyColor, color, setColor);
349     BLEND(CSSPropertyBackgroundColor, backgroundColor, setBackgroundColor);
350     BLEND_MAYBE_INVALID_COLOR(CSSPropertyWebkitColumnRuleColor, columnRuleColor, setColumnRuleColor);
351     BLEND(CSSPropertyWebkitColumnRuleWidth, columnRuleWidth, setColumnRuleWidth);
352     BLEND(CSSPropertyWebkitColumnGap, columnGap, setColumnGap);
353     BLEND(CSSPropertyWebkitColumnCount, columnCount, setColumnCount);
354     BLEND(CSSPropertyWebkitColumnWidth, columnWidth, setColumnWidth);
355     BLEND_MAYBE_INVALID_COLOR(CSSPropertyWebkitTextStrokeColor, textStrokeColor, setTextStrokeColor);
356     BLEND_MAYBE_INVALID_COLOR(CSSPropertyWebkitTextFillColor, textFillColor, setTextFillColor);
357     BLEND(CSSPropertyWebkitBorderHorizontalSpacing, horizontalBorderSpacing, setHorizontalBorderSpacing);
358     BLEND(CSSPropertyWebkitBorderVerticalSpacing, verticalBorderSpacing, setVerticalBorderSpacing);
359     BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderLeftColor, borderLeftColor, setBorderLeftColor);
360     BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderRightColor, borderRightColor, setBorderRightColor);
361     BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderTopColor, borderTopColor, setBorderTopColor);
362     BLEND_MAYBE_INVALID_COLOR(CSSPropertyBorderBottomColor, borderBottomColor, setBorderBottomColor);
363     BLEND(CSSPropertyZIndex, zIndex, setZIndex);
364     BLEND(CSSPropertyLineHeight, lineHeight, setLineHeight);
365     BLEND_MAYBE_INVALID_COLOR(CSSPropertyOutlineColor, outlineColor, setOutlineColor);
366     BLEND(CSSPropertyOutlineOffset, outlineOffset, setOutlineOffset);
367     BLEND(CSSPropertyOutlineWidth, outlineWidth, setOutlineWidth);
368     BLEND(CSSPropertyLetterSpacing, letterSpacing, setLetterSpacing);
369     BLEND(CSSPropertyWordSpacing, wordSpacing, setWordSpacing);
370     BLEND_SHADOW(CSSPropertyWebkitBoxShadow, boxShadow, setBoxShadow);
371     BLEND_SHADOW(CSSPropertyTextShadow, textShadow, setTextShadow);
372     BLEND(CSSPropertyWebkitTransform, transform, setTransform);
373     BLEND(CSSPropertyWebkitTransformOriginX, transformOriginX, setTransformOriginX);
374     BLEND(CSSPropertyWebkitTransformOriginY, transformOriginY, setTransformOriginY);
375     BLEND(CSSPropertyWebkitBorderTopLeftRadius, borderTopLeftRadius, setBorderTopLeftRadius);
376     BLEND(CSSPropertyWebkitBorderTopRightRadius, borderTopRightRadius, setBorderTopRightRadius);
377     BLEND(CSSPropertyWebkitBorderBottomLeftRadius, borderBottomLeftRadius, setBorderBottomLeftRadius);
378     BLEND(CSSPropertyWebkitBorderBottomRightRadius, borderBottomRightRadius, setBorderBottomRightRadius);
379     BLEND(CSSPropertyVisibility, visibility, setVisibility);
380     BLEND(CSSPropertyZoom, zoom, setZoom);
381 }
382
383 RenderStyle* CompositeImplicitAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle)
384 {
385     const Transition* currentTransitions = currentStyle->transitions();
386     const Transition* targetTransitions = targetStyle->transitions();
387     bool transitionsChanged = m_animations.isEmpty() || (currentTransitions != targetTransitions && !(currentTransitions && targetTransitions && *currentTransitions == *targetTransitions));
388
389     if (transitionsChanged) {
390         HashMap<int, ImplicitAnimation*>::iterator end = m_animations.end();
391          
392         for (HashMap<int, ImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it)
393             // Mark any running animations as stale if the set of transitions changed.  Stale animations continue
394             // to blend on a timer, and then remove themselves when finished.
395             it->second->setStale();
396
397         // If our transitions changed we need to add new animations for any transitions that
398         // don't exist yet.  If a new transition conflicts with a currently running stale transition, then we will not add it.
399         // The stale transition is responsible for updating itself to the new transition if it ever gets reset or finishes.
400         for (const Transition* transition = targetTransitions; transition; transition = transition->next()) {
401             int property = transition->property();
402             int duration = transition->duration();
403             int repeatCount = transition->repeatCount();
404             if (property && duration && repeatCount && !m_animations.contains(property)) {
405                 ImplicitAnimation* animation = new ImplicitAnimation(transition);
406                 m_animations.set(property, animation);
407             }
408         }
409     }
410
411     // Now that we have animation objects ready, let them know about the new goal state.  We want them
412     // to fill in a RenderStyle*& only if needed.
413     RenderStyle* result = 0;
414     HashMap<int, ImplicitAnimation*>::iterator end = m_animations.end();
415     Vector<int> obsoleteTransitions;
416     
417     // Look at the "all" animation first.
418     ImplicitAnimation* allAnimation = m_animations.get(cAnimateAll);
419     if (allAnimation) {
420         allAnimation->animate(this, renderer, currentStyle, targetStyle, result);
421
422         // If the animation is done and we are marked as stale, then we can be removed after the
423         // iteration of the hashmap is finished.
424         if (allAnimation->finished() && allAnimation->stale())
425             obsoleteTransitions.append(cAnimateAll);
426     }
427     
428     // Now look at the specialized animations.
429     for (HashMap<int, ImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
430         // Skip over the "all" animation, since we animated it already.
431         if (it->second->property() == cAnimateAll)
432             continue;
433         
434         it->second->animate(this, renderer, currentStyle, targetStyle, result);
435
436         // If the animation is done and we are marked as stale, then we can be removed after the
437         // iteration of the hashmap is finished.
438         if (it->second->finished() && it->second->stale())
439             obsoleteTransitions.append(it->second->property());
440
441     }
442     
443     // Now cull out any stale animations that are finished.
444     for (unsigned i = 0; i < obsoleteTransitions.size(); ++i) {
445         ImplicitAnimation* animation = m_animations.take(obsoleteTransitions[i]);
446         animation->reset(renderer, 0, 0);
447         delete animation;
448     }
449
450     if (result)
451         return result;
452
453     return targetStyle;
454 }
455
456 bool CompositeImplicitAnimation::animating() const
457 {
458     HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end();
459     for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it)
460         if (!it->second->finished())
461             return true;
462     return false;
463 }
464
465 void CompositeImplicitAnimation::reset(RenderObject* renderer)
466 {
467     HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end();
468     for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it)
469         it->second->reset(renderer, 0, 0);
470 }
471
472 class AnimationControllerPrivate {
473 public:
474     AnimationControllerPrivate(Frame*);
475     ~AnimationControllerPrivate();
476
477     CompositeImplicitAnimation* get(RenderObject*, RenderStyle*);
478     bool clear(RenderObject*);
479     
480     void timerFired(Timer<AnimationControllerPrivate>*);
481     void updateTimer();
482
483     bool hasImplicitAnimations() const { return !m_animations.isEmpty(); }
484
485 private:
486     HashMap<RenderObject*, CompositeImplicitAnimation*> m_animations;
487     Timer<AnimationControllerPrivate> m_timer;
488     Frame* m_frame;
489 };
490
491 AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
492     : m_timer(this, &AnimationControllerPrivate::timerFired)
493     , m_frame(frame)
494 {
495 }
496
497 AnimationControllerPrivate::~AnimationControllerPrivate()
498 {
499     deleteAllValues(m_animations);
500 }
501
502 CompositeImplicitAnimation* AnimationControllerPrivate::get(RenderObject* renderer, RenderStyle* targetStyle)
503 {
504     CompositeImplicitAnimation* animation = m_animations.get(renderer);
505     if (!animation && targetStyle->transitions()) {
506         animation = new CompositeImplicitAnimation();
507         m_animations.set(renderer, animation);
508     }
509     return animation;
510 }
511
512 bool AnimationControllerPrivate::clear(RenderObject* renderer)
513 {
514     CompositeImplicitAnimation* animation = m_animations.take(renderer);
515     if (!animation)
516         return false;
517     animation->reset(renderer);
518     delete animation;
519     return true;
520 }
521
522 void AnimationControllerPrivate::updateTimer()
523 {
524     bool animating = false;
525     HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end();
526     for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
527         if (it->second->animating()) {
528             animating = true;
529             break;
530         }
531     }
532     
533     if (animating) {
534         if (!m_timer.isActive())
535             m_timer.startRepeating(cAnimationTimerDelay);
536     } else if (m_timer.isActive())
537         m_timer.stop();
538 }
539
540 void AnimationControllerPrivate::timerFired(Timer<AnimationControllerPrivate>* timer)
541 {
542     // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate
543     // updateRendering.  It will then call back to us with new information.
544     bool animating = false;
545     HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end();
546     for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) {
547         if (it->second->animating()) {
548             animating = true;
549             it->first->element()->setChanged();
550         }
551     }
552     
553     m_frame->document()->updateRendering();
554     
555     updateTimer();
556 }
557
558 AnimationController::AnimationController(Frame* frame)
559 :m_data(new AnimationControllerPrivate(frame))
560 {
561
562 }
563
564 AnimationController::~AnimationController()
565 {
566     delete m_data;
567 }
568
569 void AnimationController::cancelImplicitAnimations(RenderObject* renderer)
570 {
571     if (!m_data->hasImplicitAnimations())
572         return;
573
574     if (m_data->clear(renderer))
575         renderer->element()->setChanged();
576 }
577
578 RenderStyle* AnimationController::updateImplicitAnimations(RenderObject* renderer, RenderStyle* newStyle)
579 {
580     // Fetch our current set of implicit animations from a hashtable.  We then compare them
581     // against the animations in the style and make sure we're in sync.  If destination values
582     // have changed, we reset the animation.  We then do a blend to get new values and we return
583     // a new style.
584     ASSERT(renderer->element()); // FIXME: We do not animate generated content yet.
585
586     CompositeImplicitAnimation* animation = m_data->get(renderer, newStyle);
587     if (!animation && !newStyle->transitions())
588         return newStyle;
589
590     RenderStyle* result = animation->animate(renderer, renderer->style(), newStyle);
591     m_data->updateTimer();
592     return result;
593 }
594
595 void AnimationController::suspendAnimations()
596 {
597     // FIXME: Walk the whole hashtable and call pause on each animation.
598     // Kill our timer.
599 }
600
601 void AnimationController::resumeAnimations()
602 {
603     // FIXME: Walk the whole hashtable and call resume on each animation.
604     // Start our timer.
605 }
606
607 }