• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "AnimationController.h"
31 
32 #include "AnimationBase.h"
33 #include "AnimationControllerPrivate.h"
34 #include "CSSParser.h"
35 #include "CompositeAnimation.h"
36 #include "EventNames.h"
37 #include "Frame.h"
38 #include "RenderView.h"
39 #include "WebKitAnimationEvent.h"
40 #include "WebKitTransitionEvent.h"
41 #include <wtf/CurrentTime.h>
42 #include <wtf/UnusedParam.h>
43 
44 namespace WebCore {
45 
46 static const double cAnimationTimerDelay = 0.025;
47 static const double cBeginAnimationUpdateTimeNotSet = -1;
48 
AnimationControllerPrivate(Frame * frame)49 AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
50     : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired)
51     , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired)
52     , m_frame(frame)
53     , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet)
54     , m_styleAvailableWaiters(0)
55     , m_lastStyleAvailableWaiter(0)
56     , m_responseWaiters(0)
57     , m_lastResponseWaiter(0)
58     , m_waitingForResponse(false)
59 {
60 }
61 
~AnimationControllerPrivate()62 AnimationControllerPrivate::~AnimationControllerPrivate()
63 {
64 }
65 
accessCompositeAnimation(RenderObject * renderer)66 PassRefPtr<CompositeAnimation> AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer)
67 {
68     RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer);
69     if (!animation) {
70         animation = CompositeAnimation::create(this);
71         m_compositeAnimations.set(renderer, animation);
72     }
73     return animation;
74 }
75 
clear(RenderObject * renderer)76 bool AnimationControllerPrivate::clear(RenderObject* renderer)
77 {
78     // Return false if we didn't do anything OR we are suspended (so we don't try to
79     // do a setNeedsStyleRecalc() when suspended).
80     PassRefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer);
81     if (!animation)
82         return false;
83     animation->clearRenderer();
84     return animation->isSuspended();
85 }
86 
updateAnimationTimer(bool callSetChanged)87 void AnimationControllerPrivate::updateAnimationTimer(bool callSetChanged/* = false*/)
88 {
89     double needsService = -1;
90     bool calledSetChanged = false;
91 
92     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
93     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
94         CompositeAnimation* compAnim = it->second.get();
95         if (!compAnim->isSuspended() && compAnim->hasAnimations()) {
96             double t = compAnim->timeToNextService();
97             if (t != -1 && (t < needsService || needsService == -1))
98                 needsService = t;
99             if (needsService == 0) {
100                 if (callSetChanged) {
101                     Node* node = it->first->node();
102                     ASSERT(!node || (node->document() && !node->document()->inPageCache()));
103                     node->setNeedsStyleRecalc(SyntheticStyleChange);
104                     calledSetChanged = true;
105                 }
106                 else
107                     break;
108             }
109         }
110     }
111 
112     if (calledSetChanged)
113         m_frame->document()->updateStyleIfNeeded();
114 
115     // If we want service immediately, we start a repeating timer to reduce the overhead of starting
116     if (needsService == 0) {
117         if (!m_animationTimer.isActive() || m_animationTimer.repeatInterval() == 0)
118             m_animationTimer.startRepeating(cAnimationTimerDelay);
119         return;
120     }
121 
122     // If we don't need service, we want to make sure the timer is no longer running
123     if (needsService < 0) {
124         if (m_animationTimer.isActive())
125             m_animationTimer.stop();
126         return;
127     }
128 
129     // Otherwise, we want to start a one-shot timer so we get here again
130     if (m_animationTimer.isActive())
131         m_animationTimer.stop();
132     m_animationTimer.startOneShot(needsService);
133 }
134 
updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate> *)135 void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>*)
136 {
137     // fire all the events
138     Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = m_eventsToDispatch.end();
139     for (Vector<EventToDispatch>::const_iterator it = m_eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) {
140         if (it->eventType == eventNames().webkitTransitionEndEvent)
141             it->element->dispatchEvent(WebKitTransitionEvent::create(it->eventType, it->name, it->elapsedTime));
142         else
143             it->element->dispatchEvent(WebKitAnimationEvent::create(it->eventType, it->name, it->elapsedTime));
144     }
145 
146     m_eventsToDispatch.clear();
147 
148     // call setChanged on all the elements
149     Vector<RefPtr<Node> >::const_iterator nodeChangesToDispatchEnd = m_nodeChangesToDispatch.end();
150     for (Vector<RefPtr<Node> >::const_iterator it = m_nodeChangesToDispatch.begin(); it != nodeChangesToDispatchEnd; ++it)
151         (*it)->setNeedsStyleRecalc(SyntheticStyleChange);
152 
153     m_nodeChangesToDispatch.clear();
154 
155     if (m_frame)
156         m_frame->document()->updateStyleIfNeeded();
157 }
158 
startUpdateStyleIfNeededDispatcher()159 void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher()
160 {
161     if (!m_updateStyleIfNeededDispatcher.isActive())
162         m_updateStyleIfNeededDispatcher.startOneShot(0);
163 }
164 
addEventToDispatch(PassRefPtr<Element> element,const AtomicString & eventType,const String & name,double elapsedTime)165 void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime)
166 {
167     m_eventsToDispatch.grow(m_eventsToDispatch.size()+1);
168     EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1];
169     event.element = element;
170     event.eventType = eventType;
171     event.name = name;
172     event.elapsedTime = elapsedTime;
173 
174     startUpdateStyleIfNeededDispatcher();
175 }
176 
addNodeChangeToDispatch(PassRefPtr<Node> node)177 void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node)
178 {
179     ASSERT(!node || (node->document() && !node->document()->inPageCache()));
180     if (!node)
181         return;
182 
183     m_nodeChangesToDispatch.append(node);
184     startUpdateStyleIfNeededDispatcher();
185 }
186 
animationTimerFired(Timer<AnimationControllerPrivate> *)187 void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>*)
188 {
189     // Make sure animationUpdateTime is updated, so that it is current even if no
190     // styleChange has happened (e.g. accelerated animations)
191     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
192 
193     // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate
194     // updateStyleIfNeeded.  It will then call back to us with new information.
195     updateAnimationTimer(true);
196 }
197 
isAnimatingPropertyOnRenderer(RenderObject * renderer,CSSPropertyID property,bool isRunningNow) const198 bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const
199 {
200     RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer);
201     if (!animation)
202         return false;
203 
204     return animation->isAnimatingProperty(property, isRunningNow);
205 }
206 
suspendAnimations(Document * document)207 void AnimationControllerPrivate::suspendAnimations(Document* document)
208 {
209     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
210 
211     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
212     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
213         RenderObject* renderer = it->first;
214         if (renderer->document() == document) {
215             CompositeAnimation* compAnim = it->second.get();
216             compAnim->suspendAnimations();
217         }
218     }
219 
220     updateAnimationTimer();
221 }
222 
resumeAnimations(Document * document)223 void AnimationControllerPrivate::resumeAnimations(Document* document)
224 {
225     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
226 
227     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
228     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
229         RenderObject* renderer = it->first;
230         if (renderer->document() == document) {
231             CompositeAnimation* compAnim = it->second.get();
232             compAnim->resumeAnimations();
233         }
234     }
235 
236     updateAnimationTimer();
237 }
238 
pauseAnimationAtTime(RenderObject * renderer,const String & name,double t)239 bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t)
240 {
241     if (!renderer)
242         return false;
243 
244     RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer);
245     if (!compAnim)
246         return false;
247 
248     if (compAnim->pauseAnimationAtTime(name, t)) {
249         renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
250         startUpdateStyleIfNeededDispatcher();
251         return true;
252     }
253 
254     return false;
255 }
256 
pauseTransitionAtTime(RenderObject * renderer,const String & property,double t)257 bool AnimationControllerPrivate::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t)
258 {
259     if (!renderer)
260         return false;
261 
262     RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer);
263     if (!compAnim)
264         return false;
265 
266     if (compAnim->pauseTransitionAtTime(cssPropertyID(property), t)) {
267         renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
268         startUpdateStyleIfNeededDispatcher();
269         return true;
270     }
271 
272     return false;
273 }
274 
beginAnimationUpdateTime()275 double AnimationControllerPrivate::beginAnimationUpdateTime()
276 {
277     if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet)
278         m_beginAnimationUpdateTime = currentTime();
279     return m_beginAnimationUpdateTime;
280 }
281 
endAnimationUpdate()282 void AnimationControllerPrivate::endAnimationUpdate()
283 {
284     styleAvailable();
285     if (!m_waitingForResponse)
286         startTimeResponse(beginAnimationUpdateTime());
287 }
288 
receivedStartTimeResponse(double time)289 void AnimationControllerPrivate::receivedStartTimeResponse(double time)
290 {
291     m_waitingForResponse = false;
292     startTimeResponse(time);
293 }
294 
getAnimatedStyleForRenderer(RenderObject * renderer)295 PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderObject* renderer)
296 {
297     if (!renderer)
298         return 0;
299 
300     RefPtr<CompositeAnimation> rendererAnimations = m_compositeAnimations.get(renderer);
301     if (!rendererAnimations)
302         return renderer->style();
303 
304     // Make sure animationUpdateTime is updated, so that it is current even if no
305     // styleChange has happened (e.g. accelerated animations).
306     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
307     RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle();
308     if (!animatingStyle)
309         animatingStyle = renderer->style();
310 
311     return animatingStyle.release();
312 }
313 
numberOfActiveAnimations() const314 unsigned AnimationControllerPrivate::numberOfActiveAnimations() const
315 {
316     unsigned count = 0;
317 
318     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
319     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
320         CompositeAnimation* compAnim = it->second.get();
321         count += compAnim->numberOfActiveAnimations();
322     }
323 
324     return count;
325 }
326 
addToStyleAvailableWaitList(AnimationBase * animation)327 void AnimationControllerPrivate::addToStyleAvailableWaitList(AnimationBase* animation)
328 {
329     ASSERT(!animation->next());
330 
331     if (m_styleAvailableWaiters)
332         m_lastStyleAvailableWaiter->setNext(animation);
333     else
334         m_styleAvailableWaiters = animation;
335 
336     m_lastStyleAvailableWaiter = animation;
337     animation->setNext(0);
338 }
339 
removeFromStyleAvailableWaitList(AnimationBase * animationToRemove)340 void AnimationControllerPrivate::removeFromStyleAvailableWaitList(AnimationBase* animationToRemove)
341 {
342     AnimationBase* prevAnimation = 0;
343     for (AnimationBase* animation = m_styleAvailableWaiters; animation; animation = animation->next()) {
344         if (animation == animationToRemove) {
345             if (prevAnimation)
346                 prevAnimation->setNext(animation->next());
347             else
348                 m_styleAvailableWaiters = animation->next();
349 
350             if (m_lastStyleAvailableWaiter == animation)
351                 m_lastStyleAvailableWaiter = prevAnimation;
352 
353             animationToRemove->setNext(0);
354         }
355     }
356 }
357 
styleAvailable()358 void AnimationControllerPrivate::styleAvailable()
359 {
360     // Go through list of waiters and send them on their way
361     for (AnimationBase* animation = m_styleAvailableWaiters; animation; ) {
362         AnimationBase* nextAnimation = animation->next();
363         animation->setNext(0);
364         animation->styleAvailable();
365         animation = nextAnimation;
366     }
367 
368     m_styleAvailableWaiters = 0;
369     m_lastStyleAvailableWaiter = 0;
370 }
371 
addToStartTimeResponseWaitList(AnimationBase * animation,bool willGetResponse)372 void AnimationControllerPrivate::addToStartTimeResponseWaitList(AnimationBase* animation, bool willGetResponse)
373 {
374     // If willGetResponse is true, it means this animation is actually waiting for a response
375     // (which will come in as a call to notifyAnimationStarted()).
376     // In that case we don't need to add it to this list. We just set a waitingForAResponse flag
377     // which says we are waiting for the response. If willGetResponse is false, this animation
378     // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for
379     // another animation to which it will sync.
380     //
381     // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is
382     // true. If so, we just return and will do our work when the first notifyXXXStarted() call
383     // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will
384     // do our work right away. In both cases we call the onAnimationStartResponse() method
385     // on each animation. In the first case we send in the time we got from notifyXXXStarted().
386     // In the second case, we just pass in the beginAnimationUpdateTime().
387     //
388     // This will synchronize all software and accelerated animations started in the same
389     // updateStyleIfNeeded cycle.
390     //
391     ASSERT(!animation->next());
392 
393     if (willGetResponse)
394         m_waitingForResponse = true;
395 
396     if (m_responseWaiters)
397         m_lastResponseWaiter->setNext(animation);
398     else
399         m_responseWaiters = animation;
400 
401     m_lastResponseWaiter = animation;
402     animation->setNext(0);
403 }
404 
removeFromStartTimeResponseWaitList(AnimationBase * animationToRemove)405 void AnimationControllerPrivate::removeFromStartTimeResponseWaitList(AnimationBase* animationToRemove)
406 {
407     AnimationBase* prevAnimation = 0;
408     for (AnimationBase* animation = m_responseWaiters; animation; animation = animation->next()) {
409         if (animation == animationToRemove) {
410             if (prevAnimation)
411                 prevAnimation->setNext(animation->next());
412             else
413                 m_responseWaiters = animation->next();
414 
415             if (m_lastResponseWaiter == animation)
416                 m_lastResponseWaiter = prevAnimation;
417 
418             animationToRemove->setNext(0);
419         }
420         prevAnimation = animation;
421     }
422 }
423 
startTimeResponse(double time)424 void AnimationControllerPrivate::startTimeResponse(double time)
425 {
426     // Go through list of waiters and send them on their way
427     for (AnimationBase* animation = m_responseWaiters; animation; ) {
428         AnimationBase* nextAnimation = animation->next();
429         animation->setNext(0);
430         animation->onAnimationStartResponse(time);
431         animation = nextAnimation;
432     }
433 
434     m_responseWaiters = 0;
435     m_lastResponseWaiter = 0;
436 }
437 
AnimationController(Frame * frame)438 AnimationController::AnimationController(Frame* frame)
439     : m_data(new AnimationControllerPrivate(frame))
440 {
441 }
442 
~AnimationController()443 AnimationController::~AnimationController()
444 {
445     delete m_data;
446 }
447 
cancelAnimations(RenderObject * renderer)448 void AnimationController::cancelAnimations(RenderObject* renderer)
449 {
450     if (!m_data->hasAnimations())
451         return;
452 
453     if (m_data->clear(renderer)) {
454         Node* node = renderer->node();
455         ASSERT(!node || (node->document() && !node->document()->inPageCache()));
456         node->setNeedsStyleRecalc(SyntheticStyleChange);
457     }
458 }
459 
updateAnimations(RenderObject * renderer,RenderStyle * newStyle)460 PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle)
461 {
462     // Don't do anything if we're in the cache
463     if (!renderer->document() || renderer->document()->inPageCache())
464         return newStyle;
465 
466     RenderStyle* oldStyle = renderer->style();
467 
468     if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions()))
469         return newStyle;
470 
471     // Don't run transitions when printing.
472     if (renderer->view()->printing())
473         return newStyle;
474 
475     // Fetch our current set of implicit animations from a hashtable.  We then compare them
476     // against the animations in the style and make sure we're in sync.  If destination values
477     // have changed, we reset the animation.  We then do a blend to get new values and we return
478     // a new style.
479     ASSERT(renderer->node()); // FIXME: We do not animate generated content yet.
480 
481     RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer);
482     RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle);
483 
484     m_data->updateAnimationTimer();
485 
486     if (blendedStyle != newStyle) {
487         // If the animations/transitions change opacity or transform, we need to update
488         // the style to impose the stacking rules. Note that this is also
489         // done in CSSStyleSelector::adjustRenderStyle().
490         if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform()))
491             blendedStyle->setZIndex(0);
492     }
493     return blendedStyle.release();
494 }
495 
getAnimatedStyleForRenderer(RenderObject * renderer)496 PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer)
497 {
498     return m_data->getAnimatedStyleForRenderer(renderer);
499 }
500 
notifyAnimationStarted(RenderObject *,double startTime)501 void AnimationController::notifyAnimationStarted(RenderObject*, double startTime)
502 {
503     m_data->receivedStartTimeResponse(startTime);
504 }
505 
pauseAnimationAtTime(RenderObject * renderer,const String & name,double t)506 bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t)
507 {
508     return m_data->pauseAnimationAtTime(renderer, name, t);
509 }
510 
numberOfActiveAnimations() const511 unsigned AnimationController::numberOfActiveAnimations() const
512 {
513     return m_data->numberOfActiveAnimations();
514 }
515 
pauseTransitionAtTime(RenderObject * renderer,const String & property,double t)516 bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t)
517 {
518     return m_data->pauseTransitionAtTime(renderer, property, t);
519 }
520 
isAnimatingPropertyOnRenderer(RenderObject * renderer,CSSPropertyID property,bool isRunningNow) const521 bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const
522 {
523     return m_data->isAnimatingPropertyOnRenderer(renderer, property, isRunningNow);
524 }
525 
suspendAnimations(Document * document)526 void AnimationController::suspendAnimations(Document* document)
527 {
528     m_data->suspendAnimations(document);
529 }
530 
resumeAnimations(Document * document)531 void AnimationController::resumeAnimations(Document* document)
532 {
533     m_data->resumeAnimations(document);
534 }
535 
beginAnimationUpdate()536 void AnimationController::beginAnimationUpdate()
537 {
538     m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
539 }
540 
endAnimationUpdate()541 void AnimationController::endAnimationUpdate()
542 {
543     m_data->endAnimationUpdate();
544 }
545 
supportsAcceleratedAnimationOfProperty(CSSPropertyID property)546 bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property)
547 {
548 #if USE(ACCELERATED_COMPOSITING)
549     return AnimationBase::animationOfPropertyIsAccelerated(property);
550 #else
551     UNUSED_PARAM(property);
552     return false;
553 #endif
554 }
555 
556 } // namespace WebCore
557