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