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