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