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 "WebKitAnimationList.h" 41#include "WebKitTransitionEvent.h" 42#include <wtf/CurrentTime.h> 43#include <wtf/UnusedParam.h> 44 45namespace WebCore { 46 47// FIXME: Why isn't this set to 60fps or something? 48static const double cAnimationTimerDelay = 0.025; 49static const double cBeginAnimationUpdateTimeNotSet = -1; 50 51AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) 52 : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) 53 , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired) 54 , m_frame(frame) 55 , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) 56 , m_animationsWaitingForStyle() 57 , m_animationsWaitingForStartTimeResponse() 58 , m_waitingForAsyncStartNotification(false) 59{ 60} 61 62AnimationControllerPrivate::~AnimationControllerPrivate() 63{ 64} 65 66PassRefPtr<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 76bool 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->suspended(); 85} 86 87void 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->suspended() && 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 135void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>*) 136{ 137 fireEventsAndUpdateStyle(); 138} 139 140void AnimationControllerPrivate::fireEventsAndUpdateStyle() 141{ 142 // Protect the frame from getting destroyed in the event handler 143 RefPtr<Frame> protector = m_frame; 144 145 bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_nodeChangesToDispatch.isEmpty(); 146 147 // fire all the events 148 Vector<EventToDispatch> eventsToDispatch = m_eventsToDispatch; 149 m_eventsToDispatch.clear(); 150 Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = eventsToDispatch.end(); 151 for (Vector<EventToDispatch>::const_iterator it = eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) { 152 if (it->eventType == eventNames().webkitTransitionEndEvent) 153 it->element->dispatchEvent(WebKitTransitionEvent::create(it->eventType, it->name, it->elapsedTime)); 154 else 155 it->element->dispatchEvent(WebKitAnimationEvent::create(it->eventType, it->name, it->elapsedTime)); 156 } 157 158 // call setChanged on all the elements 159 Vector<RefPtr<Node> >::const_iterator nodeChangesToDispatchEnd = m_nodeChangesToDispatch.end(); 160 for (Vector<RefPtr<Node> >::const_iterator it = m_nodeChangesToDispatch.begin(); it != nodeChangesToDispatchEnd; ++it) 161 (*it)->setNeedsStyleRecalc(SyntheticStyleChange); 162 163 m_nodeChangesToDispatch.clear(); 164 165 if (updateStyle && m_frame) 166 m_frame->document()->updateStyleIfNeeded(); 167} 168 169void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() 170{ 171 if (!m_updateStyleIfNeededDispatcher.isActive()) 172 m_updateStyleIfNeededDispatcher.startOneShot(0); 173} 174 175void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) 176{ 177 m_eventsToDispatch.grow(m_eventsToDispatch.size()+1); 178 EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1]; 179 event.element = element; 180 event.eventType = eventType; 181 event.name = name; 182 event.elapsedTime = elapsedTime; 183 184 startUpdateStyleIfNeededDispatcher(); 185} 186 187void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node) 188{ 189 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 190 if (!node) 191 return; 192 193 m_nodeChangesToDispatch.append(node); 194 startUpdateStyleIfNeededDispatcher(); 195} 196 197void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>*) 198{ 199 // Make sure animationUpdateTime is updated, so that it is current even if no 200 // styleChange has happened (e.g. accelerated animations) 201 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 202 203 // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate 204 // updateStyleIfNeeded. It will then call back to us with new information. 205 updateAnimationTimer(true); 206 207 // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before 208 // the 'end' event fires. 209 fireEventsAndUpdateStyle(); 210} 211 212bool AnimationControllerPrivate::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 213{ 214 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 215 if (!animation) 216 return false; 217 218 return animation->isAnimatingProperty(property, false, isRunningNow); 219} 220 221bool AnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 222{ 223 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 224 if (!animation) 225 return false; 226 227 return animation->isAnimatingProperty(property, true, isRunningNow); 228} 229 230void AnimationControllerPrivate::suspendAnimations() 231{ 232 suspendAnimationsForDocument(m_frame->document()); 233 234 // Traverse subframes 235 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) 236 child->animation()->suspendAnimations(); 237} 238 239void AnimationControllerPrivate::resumeAnimations() 240{ 241 resumeAnimationsForDocument(m_frame->document()); 242 243 // Traverse subframes 244 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) 245 child->animation()->resumeAnimations(); 246} 247 248void AnimationControllerPrivate::suspendAnimationsForDocument(Document* document) 249{ 250 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 251 252 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 253 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 254 RenderObject* renderer = it->first; 255 if (renderer->document() == document) { 256 CompositeAnimation* compAnim = it->second.get(); 257 compAnim->suspendAnimations(); 258 } 259 } 260 261 updateAnimationTimer(); 262} 263 264void AnimationControllerPrivate::resumeAnimationsForDocument(Document* document) 265{ 266 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 267 268 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 269 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 270 RenderObject* renderer = it->first; 271 if (renderer->document() == document) { 272 CompositeAnimation* compAnim = it->second.get(); 273 compAnim->resumeAnimations(); 274 } 275 } 276 277 updateAnimationTimer(); 278} 279 280bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t) 281{ 282 if (!renderer) 283 return false; 284 285 RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer); 286 if (!compAnim) 287 return false; 288 289 if (compAnim->pauseAnimationAtTime(name, t)) { 290 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); 291 startUpdateStyleIfNeededDispatcher(); 292 return true; 293 } 294 295 return false; 296} 297 298bool AnimationControllerPrivate::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) 299{ 300 if (!renderer) 301 return false; 302 303 RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer); 304 if (!compAnim) 305 return false; 306 307 if (compAnim->pauseTransitionAtTime(cssPropertyID(property), t)) { 308 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); 309 startUpdateStyleIfNeededDispatcher(); 310 return true; 311 } 312 313 return false; 314} 315 316double AnimationControllerPrivate::beginAnimationUpdateTime() 317{ 318 if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) 319 m_beginAnimationUpdateTime = currentTime(); 320 return m_beginAnimationUpdateTime; 321} 322 323void AnimationControllerPrivate::endAnimationUpdate() 324{ 325 styleAvailable(); 326 if (!m_waitingForAsyncStartNotification) 327 startTimeResponse(beginAnimationUpdateTime()); 328} 329 330void AnimationControllerPrivate::receivedStartTimeResponse(double time) 331{ 332 m_waitingForAsyncStartNotification = false; 333 startTimeResponse(time); 334} 335 336PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderObject* renderer) 337{ 338 if (!renderer) 339 return 0; 340 341 RefPtr<CompositeAnimation> rendererAnimations = m_compositeAnimations.get(renderer); 342 if (!rendererAnimations) 343 return renderer->style(); 344 345 // Make sure animationUpdateTime is updated, so that it is current even if no 346 // styleChange has happened (e.g. accelerated animations). 347 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 348 RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle(); 349 if (!animatingStyle) 350 animatingStyle = renderer->style(); 351 352 return animatingStyle.release(); 353} 354 355unsigned AnimationControllerPrivate::numberOfActiveAnimations() const 356{ 357 unsigned count = 0; 358 359 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 360 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 361 CompositeAnimation* compAnim = it->second.get(); 362 count += compAnim->numberOfActiveAnimations(); 363 } 364 365 return count; 366} 367 368void AnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) 369{ 370 // Make sure this animation is not in the start time waiters 371 m_animationsWaitingForStartTimeResponse.remove(animation); 372 373 m_animationsWaitingForStyle.add(animation); 374} 375 376void AnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) 377{ 378 m_animationsWaitingForStyle.remove(animationToRemove); 379} 380 381void AnimationControllerPrivate::styleAvailable() 382{ 383 // Go through list of waiters and send them on their way 384 WaitingAnimationsSet::const_iterator it = m_animationsWaitingForStyle.begin(); 385 WaitingAnimationsSet::const_iterator end = m_animationsWaitingForStyle.end(); 386 for (; it != end; ++it) 387 (*it)->styleAvailable(); 388 389 m_animationsWaitingForStyle.clear(); 390} 391 392void AnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) 393{ 394 // If willGetResponse is true, it means this animation is actually waiting for a response 395 // (which will come in as a call to notifyAnimationStarted()). 396 // In that case we don't need to add it to this list. We just set a waitingForAResponse flag 397 // which says we are waiting for the response. If willGetResponse is false, this animation 398 // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for 399 // another animation to which it will sync. 400 // 401 // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is 402 // true. If so, we just return and will do our work when the first notifyXXXStarted() call 403 // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will 404 // do our work right away. In both cases we call the onAnimationStartResponse() method 405 // on each animation. In the first case we send in the time we got from notifyXXXStarted(). 406 // In the second case, we just pass in the beginAnimationUpdateTime(). 407 // 408 // This will synchronize all software and accelerated animations started in the same 409 // updateStyleIfNeeded cycle. 410 // 411 412 if (willGetResponse) 413 m_waitingForAsyncStartNotification = true; 414 415 m_animationsWaitingForStartTimeResponse.add(animation); 416} 417 418void AnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) 419{ 420 m_animationsWaitingForStartTimeResponse.remove(animationToRemove); 421 422 if (m_animationsWaitingForStartTimeResponse.isEmpty()) 423 m_waitingForAsyncStartNotification = false; 424} 425 426void AnimationControllerPrivate::startTimeResponse(double time) 427{ 428 // Go through list of waiters and send them on their way 429 430 WaitingAnimationsSet::const_iterator it = m_animationsWaitingForStartTimeResponse.begin(); 431 WaitingAnimationsSet::const_iterator end = m_animationsWaitingForStartTimeResponse.end(); 432 for (; it != end; ++it) 433 (*it)->onAnimationStartResponse(time); 434 435 m_animationsWaitingForStartTimeResponse.clear(); 436 m_waitingForAsyncStartNotification = false; 437} 438 439void AnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) 440{ 441 removeFromAnimationsWaitingForStyle(animation); 442 removeFromAnimationsWaitingForStartTimeResponse(animation); 443} 444 445PassRefPtr<WebKitAnimationList> AnimationControllerPrivate::animationsForRenderer(RenderObject* renderer) const 446{ 447 RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer); 448 449 if (!animation) 450 return 0; 451 452 return animation->animations(); 453} 454 455AnimationController::AnimationController(Frame* frame) 456 : m_data(new AnimationControllerPrivate(frame)) 457{ 458} 459 460AnimationController::~AnimationController() 461{ 462 delete m_data; 463} 464 465void AnimationController::cancelAnimations(RenderObject* renderer) 466{ 467 if (!m_data->hasAnimations()) 468 return; 469 470 if (m_data->clear(renderer)) { 471 Node* node = renderer->node(); 472 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 473 node->setNeedsStyleRecalc(SyntheticStyleChange); 474 } 475} 476 477PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle) 478{ 479 // Don't do anything if we're in the cache 480 if (!renderer->document() || renderer->document()->inPageCache()) 481 return newStyle; 482 483 RenderStyle* oldStyle = renderer->style(); 484 485 if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions())) 486 return newStyle; 487 488 // Don't run transitions when printing. 489 if (renderer->view()->printing()) 490 return newStyle; 491 492 // Fetch our current set of implicit animations from a hashtable. We then compare them 493 // against the animations in the style and make sure we're in sync. If destination values 494 // have changed, we reset the animation. We then do a blend to get new values and we return 495 // a new style. 496 ASSERT(renderer->node()); // FIXME: We do not animate generated content yet. 497 498 RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer); 499 RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle); 500 501 m_data->updateAnimationTimer(); 502 503 if (blendedStyle != newStyle) { 504 // If the animations/transitions change opacity or transform, we need to update 505 // the style to impose the stacking rules. Note that this is also 506 // done in CSSStyleSelector::adjustRenderStyle(). 507 if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform())) 508 blendedStyle->setZIndex(0); 509 } 510 return blendedStyle.release(); 511} 512 513PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer) 514{ 515 return m_data->getAnimatedStyleForRenderer(renderer); 516} 517 518void AnimationController::notifyAnimationStarted(RenderObject*, double startTime) 519{ 520 m_data->receivedStartTimeResponse(startTime); 521} 522 523bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t) 524{ 525 return m_data->pauseAnimationAtTime(renderer, name, t); 526} 527 528unsigned AnimationController::numberOfActiveAnimations() const 529{ 530 return m_data->numberOfActiveAnimations(); 531} 532 533bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) 534{ 535 return m_data->pauseTransitionAtTime(renderer, property, t); 536} 537 538bool AnimationController::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 539{ 540 return m_data->isRunningAnimationOnRenderer(renderer, property, isRunningNow); 541} 542 543bool AnimationController::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 544{ 545 return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow); 546} 547 548void AnimationController::suspendAnimations() 549{ 550 m_data->suspendAnimations(); 551} 552 553void AnimationController::resumeAnimations() 554{ 555 m_data->resumeAnimations(); 556} 557 558void AnimationController::suspendAnimationsForDocument(Document* document) 559{ 560 m_data->suspendAnimationsForDocument(document); 561} 562 563void AnimationController::resumeAnimationsForDocument(Document* document) 564{ 565 m_data->resumeAnimationsForDocument(document); 566} 567 568void AnimationController::beginAnimationUpdate() 569{ 570 m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 571} 572 573void AnimationController::endAnimationUpdate() 574{ 575 m_data->endAnimationUpdate(); 576} 577 578bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) 579{ 580#if USE(ACCELERATED_COMPOSITING) 581 return AnimationBase::animationOfPropertyIsAccelerated(property); 582#else 583 UNUSED_PARAM(property); 584 return false; 585#endif 586} 587 588PassRefPtr<WebKitAnimationList> AnimationController::animationsForRenderer(RenderObject* renderer) const 589{ 590 return m_data->animationsForRenderer(renderer); 591} 592 593} // namespace WebCore 594