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 45 namespace WebCore { 46 47 // FIXME: Why isn't this set to 60fps or something? 48 static const double cAnimationTimerDelay = 0.025; 49 static const double cBeginAnimationUpdateTimeNotSet = -1; 50 51 AnimationControllerPrivate::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 62 AnimationControllerPrivate::~AnimationControllerPrivate() 63 { 64 } 65 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 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->suspended(); 85 } 86 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->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 135 void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>*) 136 { 137 fireEventsAndUpdateStyle(); 138 } 139 140 void 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 169 void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() 170 { 171 if (!m_updateStyleIfNeededDispatcher.isActive()) 172 m_updateStyleIfNeededDispatcher.startOneShot(0); 173 } 174 175 void 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 187 void 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 197 void 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 212 bool 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 221 bool 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 230 void 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 239 void 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 248 void 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 264 void 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 280 bool 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 298 bool 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 316 double AnimationControllerPrivate::beginAnimationUpdateTime() 317 { 318 if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) 319 m_beginAnimationUpdateTime = currentTime(); 320 return m_beginAnimationUpdateTime; 321 } 322 323 void AnimationControllerPrivate::endAnimationUpdate() 324 { 325 styleAvailable(); 326 if (!m_waitingForAsyncStartNotification) 327 startTimeResponse(beginAnimationUpdateTime()); 328 } 329 330 void AnimationControllerPrivate::receivedStartTimeResponse(double time) 331 { 332 m_waitingForAsyncStartNotification = false; 333 startTimeResponse(time); 334 } 335 336 PassRefPtr<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 355 unsigned 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 368 void 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 376 void AnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) 377 { 378 m_animationsWaitingForStyle.remove(animationToRemove); 379 } 380 381 void 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 392 void 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 418 void AnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) 419 { 420 m_animationsWaitingForStartTimeResponse.remove(animationToRemove); 421 422 if (m_animationsWaitingForStartTimeResponse.isEmpty()) 423 m_waitingForAsyncStartNotification = false; 424 } 425 426 void 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 439 void AnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) 440 { 441 removeFromAnimationsWaitingForStyle(animation); 442 removeFromAnimationsWaitingForStartTimeResponse(animation); 443 } 444 445 PassRefPtr<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 455 AnimationController::AnimationController(Frame* frame) 456 : m_data(new AnimationControllerPrivate(frame)) 457 { 458 } 459 460 AnimationController::~AnimationController() 461 { 462 delete m_data; 463 } 464 465 void 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 477 PassRefPtr<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 513 PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer) 514 { 515 return m_data->getAnimatedStyleForRenderer(renderer); 516 } 517 518 void AnimationController::notifyAnimationStarted(RenderObject*, double startTime) 519 { 520 m_data->receivedStartTimeResponse(startTime); 521 } 522 523 bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t) 524 { 525 return m_data->pauseAnimationAtTime(renderer, name, t); 526 } 527 528 unsigned AnimationController::numberOfActiveAnimations() const 529 { 530 return m_data->numberOfActiveAnimations(); 531 } 532 533 bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) 534 { 535 return m_data->pauseTransitionAtTime(renderer, property, t); 536 } 537 538 bool AnimationController::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 539 { 540 return m_data->isRunningAnimationOnRenderer(renderer, property, isRunningNow); 541 } 542 543 bool AnimationController::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 544 { 545 return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow); 546 } 547 548 void AnimationController::suspendAnimations() 549 { 550 m_data->suspendAnimations(); 551 } 552 553 void AnimationController::resumeAnimations() 554 { 555 m_data->resumeAnimations(); 556 } 557 558 void AnimationController::suspendAnimationsForDocument(Document* document) 559 { 560 m_data->suspendAnimationsForDocument(document); 561 } 562 563 void AnimationController::resumeAnimationsForDocument(Document* document) 564 { 565 m_data->resumeAnimationsForDocument(document); 566 } 567 568 void AnimationController::beginAnimationUpdate() 569 { 570 m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 571 } 572 573 void AnimationController::endAnimationUpdate() 574 { 575 m_data->endAnimationUpdate(); 576 } 577 578 bool 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 588 PassRefPtr<WebKitAnimationList> AnimationController::animationsForRenderer(RenderObject* renderer) const 589 { 590 return m_data->animationsForRenderer(renderer); 591 } 592 593 } // namespace WebCore 594