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 "CSSPropertyAnimation.h" 36#include "CompositeAnimation.h" 37#include "EventNames.h" 38#include "Frame.h" 39#include "FrameView.h" 40#include "Logging.h" 41#include "PseudoElement.h" 42#include "RenderView.h" 43#include "TransitionEvent.h" 44#include "WebKitAnimationEvent.h" 45#include "WebKitTransitionEvent.h" 46#include <wtf/CurrentTime.h> 47 48namespace WebCore { 49 50static const double cAnimationTimerDelay = 0.025; 51static const double cBeginAnimationUpdateTimeNotSet = -1; 52 53AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) 54 : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) 55 , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired) 56 , m_frame(frame) 57 , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) 58 , m_animationsWaitingForStyle() 59 , m_animationsWaitingForStartTimeResponse() 60 , m_waitingForAsyncStartNotification(false) 61 , m_isSuspended(false) 62{ 63} 64 65AnimationControllerPrivate::~AnimationControllerPrivate() 66{ 67} 68 69CompositeAnimation* AnimationControllerPrivate::ensureCompositeAnimation(RenderObject* renderer) 70{ 71 RenderObjectAnimationMap::AddResult result = m_compositeAnimations.add(renderer, 0); 72 if (result.isNewEntry) 73 result.iterator->value = CompositeAnimation::create(this); 74 return result.iterator->value.get(); 75} 76 77bool AnimationControllerPrivate::clear(RenderObject* renderer) 78{ 79 // Return false if we didn't do anything OR we are suspended (so we don't try to 80 // do a setNeedsStyleRecalc() when suspended). 81 RefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer); 82 if (!animation) 83 return false; 84 animation->clearRenderer(); 85 return animation->isSuspended(); 86} 87 88double AnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/) 89{ 90 double timeToNextService = -1; 91 bool calledSetChanged = false; 92 93 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 94 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 95 CompositeAnimation* compAnim = it->value.get(); 96 if (!compAnim->isSuspended() && compAnim->hasAnimations()) { 97 double t = compAnim->timeToNextService(); 98 if (t != -1 && (t < timeToNextService || timeToNextService == -1)) 99 timeToNextService = t; 100 if (!timeToNextService) { 101 if (callSetChanged == CallSetChanged) { 102 Node* node = it->key->node(); 103 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 104 node->setNeedsStyleRecalc(SyntheticStyleChange); 105 calledSetChanged = true; 106 } 107 else 108 break; 109 } 110 } 111 } 112 113 if (calledSetChanged) 114 m_frame->document()->updateStyleIfNeeded(); 115 116 return timeToNextService; 117} 118 119void AnimationControllerPrivate::updateAnimationTimerForRenderer(RenderObject* renderer) 120{ 121 double timeToNextService = 0; 122 123 const CompositeAnimation* compositeAnimation = m_compositeAnimations.get(renderer); 124 if (!compositeAnimation->isSuspended() && compositeAnimation->hasAnimations()) 125 timeToNextService = compositeAnimation->timeToNextService(); 126 127 if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService)) 128 return; 129 130 m_animationTimer.startOneShot(timeToNextService); 131} 132 133void AnimationControllerPrivate::updateAnimationTimer(SetChanged callSetChanged/* = DoNotCallSetChanged*/) 134{ 135 double timeToNextService = updateAnimations(callSetChanged); 136 137 LOG(Animations, "updateAnimationTimer: timeToNextService is %.2f", timeToNextService); 138 139 // If we want service immediately, we start a repeating timer to reduce the overhead of starting 140 if (!timeToNextService) { 141 if (!m_animationTimer.isActive() || m_animationTimer.repeatInterval() == 0) 142 m_animationTimer.startRepeating(cAnimationTimerDelay); 143 return; 144 } 145 146 // If we don't need service, we want to make sure the timer is no longer running 147 if (timeToNextService < 0) { 148 if (m_animationTimer.isActive()) 149 m_animationTimer.stop(); 150 return; 151 } 152 153 // Otherwise, we want to start a one-shot timer so we get here again 154 m_animationTimer.startOneShot(timeToNextService); 155} 156 157void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>*) 158{ 159 fireEventsAndUpdateStyle(); 160} 161 162void AnimationControllerPrivate::fireEventsAndUpdateStyle() 163{ 164 // Protect the frame from getting destroyed in the event handler 165 RefPtr<Frame> protector = m_frame; 166 167 bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_nodeChangesToDispatch.isEmpty(); 168 169 // fire all the events 170 Vector<EventToDispatch> eventsToDispatch = m_eventsToDispatch; 171 m_eventsToDispatch.clear(); 172 Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = eventsToDispatch.end(); 173 for (Vector<EventToDispatch>::const_iterator it = eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) { 174 Element* element = it->element.get(); 175 if (it->eventType == eventNames().transitionendEvent) 176 element->dispatchEvent(TransitionEvent::create(it->eventType, it->name, it->elapsedTime, PseudoElement::pseudoElementNameForEvents(element->pseudoId()))); 177 else 178 element->dispatchEvent(WebKitAnimationEvent::create(it->eventType, it->name, it->elapsedTime)); 179 } 180 181 // call setChanged on all the elements 182 Vector<RefPtr<Node> >::const_iterator nodeChangesToDispatchEnd = m_nodeChangesToDispatch.end(); 183 for (Vector<RefPtr<Node> >::const_iterator it = m_nodeChangesToDispatch.begin(); it != nodeChangesToDispatchEnd; ++it) 184 (*it)->setNeedsStyleRecalc(SyntheticStyleChange); 185 186 m_nodeChangesToDispatch.clear(); 187 188 if (updateStyle && m_frame) 189 m_frame->document()->updateStyleIfNeeded(); 190} 191 192void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() 193{ 194 if (!m_updateStyleIfNeededDispatcher.isActive()) 195 m_updateStyleIfNeededDispatcher.startOneShot(0); 196} 197 198void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) 199{ 200 m_eventsToDispatch.grow(m_eventsToDispatch.size()+1); 201 EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1]; 202 event.element = element; 203 event.eventType = eventType; 204 event.name = name; 205 event.elapsedTime = elapsedTime; 206 207 startUpdateStyleIfNeededDispatcher(); 208} 209 210void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node) 211{ 212 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 213 if (!node) 214 return; 215 216 m_nodeChangesToDispatch.append(node); 217 startUpdateStyleIfNeededDispatcher(); 218} 219 220#if ENABLE(REQUEST_ANIMATION_FRAME) 221void AnimationControllerPrivate::animationFrameCallbackFired() 222{ 223 double timeToNextService = updateAnimations(CallSetChanged); 224 225 if (timeToNextService >= 0) 226 m_frame->document()->view()->scheduleAnimation(); 227} 228#endif 229 230void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>*) 231{ 232 // Make sure animationUpdateTime is updated, so that it is current even if no 233 // styleChange has happened (e.g. accelerated animations) 234 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 235 236 // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate 237 // updateStyleIfNeeded. It will then call back to us with new information. 238 updateAnimationTimer(CallSetChanged); 239 240 // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before 241 // the 'end' event fires. 242 fireEventsAndUpdateStyle(); 243} 244 245bool AnimationControllerPrivate::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 246{ 247 const CompositeAnimation* animation = m_compositeAnimations.get(renderer); 248 if (!animation) 249 return false; 250 251 return animation->isAnimatingProperty(property, false, isRunningNow); 252} 253 254bool AnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 255{ 256 const CompositeAnimation* animation = m_compositeAnimations.get(renderer); 257 if (!animation) 258 return false; 259 260 return animation->isAnimatingProperty(property, true, isRunningNow); 261} 262 263void AnimationControllerPrivate::suspendAnimations() 264{ 265 if (isSuspended()) 266 return; 267 268 suspendAnimationsForDocument(m_frame->document()); 269 270 // Traverse subframes 271 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) 272 child->animation()->suspendAnimations(); 273 274 m_isSuspended = true; 275} 276 277void AnimationControllerPrivate::resumeAnimations() 278{ 279 if (!isSuspended()) 280 return; 281 282 resumeAnimationsForDocument(m_frame->document()); 283 284 // Traverse subframes 285 for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) 286 child->animation()->resumeAnimations(); 287 288 m_isSuspended = false; 289} 290 291void AnimationControllerPrivate::suspendAnimationsForDocument(Document* document) 292{ 293 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 294 295 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 296 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 297 RenderObject* renderer = it->key; 298 if (renderer->document() == document) { 299 CompositeAnimation* compAnim = it->value.get(); 300 compAnim->suspendAnimations(); 301 } 302 } 303 304 updateAnimationTimer(); 305} 306 307void AnimationControllerPrivate::resumeAnimationsForDocument(Document* document) 308{ 309 setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 310 311 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 312 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 313 RenderObject* renderer = it->key; 314 if (renderer->document() == document) { 315 CompositeAnimation* compAnim = it->value.get(); 316 compAnim->resumeAnimations(); 317 } 318 } 319 320 updateAnimationTimer(); 321} 322 323void AnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document) 324{ 325 if (!isSuspended()) 326 resumeAnimationsForDocument(document); 327} 328 329bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, const AtomicString& name, double t) 330{ 331 if (!renderer) 332 return false; 333 334 CompositeAnimation* compositeAnimation = ensureCompositeAnimation(renderer); 335 if (compositeAnimation->pauseAnimationAtTime(name, t)) { 336 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); 337 startUpdateStyleIfNeededDispatcher(); 338 return true; 339 } 340 341 return false; 342} 343 344bool AnimationControllerPrivate::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) 345{ 346 if (!renderer) 347 return false; 348 349 CompositeAnimation* compositeAnimation = ensureCompositeAnimation(renderer); 350 if (compositeAnimation->pauseTransitionAtTime(cssPropertyID(property), t)) { 351 renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); 352 startUpdateStyleIfNeededDispatcher(); 353 return true; 354 } 355 356 return false; 357} 358 359double AnimationControllerPrivate::beginAnimationUpdateTime() 360{ 361 if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) 362 m_beginAnimationUpdateTime = currentTime(); 363 return m_beginAnimationUpdateTime; 364} 365 366void AnimationControllerPrivate::endAnimationUpdate() 367{ 368 styleAvailable(); 369 if (!m_waitingForAsyncStartNotification) 370 startTimeResponse(beginAnimationUpdateTime()); 371} 372 373void AnimationControllerPrivate::receivedStartTimeResponse(double time) 374{ 375 m_waitingForAsyncStartNotification = false; 376 startTimeResponse(time); 377} 378 379PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderObject* renderer) 380{ 381 if (!renderer) 382 return 0; 383 384 const CompositeAnimation* rendererAnimations = m_compositeAnimations.get(renderer); 385 if (!rendererAnimations) 386 return renderer->style(); 387 388 RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle(); 389 if (!animatingStyle) 390 animatingStyle = renderer->style(); 391 392 return animatingStyle.release(); 393} 394 395unsigned AnimationControllerPrivate::numberOfActiveAnimations(Document* document) const 396{ 397 unsigned count = 0; 398 399 RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); 400 for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { 401 RenderObject* renderer = it->key; 402 CompositeAnimation* compAnim = it->value.get(); 403 if (renderer->document() == document) 404 count += compAnim->numberOfActiveAnimations(); 405 } 406 407 return count; 408} 409 410void AnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) 411{ 412 // Make sure this animation is not in the start time waiters 413 m_animationsWaitingForStartTimeResponse.remove(animation); 414 415 m_animationsWaitingForStyle.add(animation); 416} 417 418void AnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) 419{ 420 m_animationsWaitingForStyle.remove(animationToRemove); 421} 422 423void AnimationControllerPrivate::styleAvailable() 424{ 425 // Go through list of waiters and send them on their way 426 WaitingAnimationsSet::const_iterator it = m_animationsWaitingForStyle.begin(); 427 WaitingAnimationsSet::const_iterator end = m_animationsWaitingForStyle.end(); 428 for (; it != end; ++it) 429 (*it)->styleAvailable(); 430 431 m_animationsWaitingForStyle.clear(); 432} 433 434void AnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) 435{ 436 // If willGetResponse is true, it means this animation is actually waiting for a response 437 // (which will come in as a call to notifyAnimationStarted()). 438 // In that case we don't need to add it to this list. We just set a waitingForAResponse flag 439 // which says we are waiting for the response. If willGetResponse is false, this animation 440 // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for 441 // another animation to which it will sync. 442 // 443 // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is 444 // true. If so, we just return and will do our work when the first notifyXXXStarted() call 445 // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will 446 // do our work right away. In both cases we call the onAnimationStartResponse() method 447 // on each animation. In the first case we send in the time we got from notifyXXXStarted(). 448 // In the second case, we just pass in the beginAnimationUpdateTime(). 449 // 450 // This will synchronize all software and accelerated animations started in the same 451 // updateStyleIfNeeded cycle. 452 // 453 454 if (willGetResponse) 455 m_waitingForAsyncStartNotification = true; 456 457 m_animationsWaitingForStartTimeResponse.add(animation); 458} 459 460void AnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) 461{ 462 m_animationsWaitingForStartTimeResponse.remove(animationToRemove); 463 464 if (m_animationsWaitingForStartTimeResponse.isEmpty()) 465 m_waitingForAsyncStartNotification = false; 466} 467 468void AnimationControllerPrivate::startTimeResponse(double time) 469{ 470 // Go through list of waiters and send them on their way 471 472 WaitingAnimationsSet::const_iterator it = m_animationsWaitingForStartTimeResponse.begin(); 473 WaitingAnimationsSet::const_iterator end = m_animationsWaitingForStartTimeResponse.end(); 474 for (; it != end; ++it) 475 (*it)->onAnimationStartResponse(time); 476 477 m_animationsWaitingForStartTimeResponse.clear(); 478 m_waitingForAsyncStartNotification = false; 479} 480 481void AnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) 482{ 483 removeFromAnimationsWaitingForStyle(animation); 484 removeFromAnimationsWaitingForStartTimeResponse(animation); 485} 486 487AnimationController::AnimationController(Frame* frame) 488 : m_data(adoptPtr(new AnimationControllerPrivate(frame))) 489 , m_beginAnimationUpdateCount(0) 490{ 491} 492 493AnimationController::~AnimationController() 494{ 495} 496 497void AnimationController::cancelAnimations(RenderObject* renderer) 498{ 499 if (!m_data->hasAnimations()) 500 return; 501 502 if (m_data->clear(renderer)) { 503 Node* node = renderer->node(); 504 ASSERT(!node || (node->document() && !node->document()->inPageCache())); 505 if (node) 506 node->setNeedsStyleRecalc(SyntheticStyleChange); 507 } 508} 509 510PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle) 511{ 512 // Don't do anything if we're in the cache 513 if (!renderer->document() || renderer->document()->inPageCache()) 514 return newStyle; 515 516 RenderStyle* oldStyle = renderer->style(); 517 518 if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions())) 519 return newStyle; 520 521 // Don't run transitions when printing. 522 if (renderer->view()->printing()) 523 return newStyle; 524 525 // Fetch our current set of implicit animations from a hashtable. We then compare them 526 // against the animations in the style and make sure we're in sync. If destination values 527 // have changed, we reset the animation. We then do a blend to get new values and we return 528 // a new style. 529 530 // We don't support anonymous pseudo elements like :first-line or :first-letter. 531 ASSERT(renderer->node()); 532 533 CompositeAnimation* rendererAnimations = m_data->ensureCompositeAnimation(renderer); 534 RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle); 535 536 if (renderer->parent() || newStyle->animations() || (oldStyle && oldStyle->animations())) { 537 m_data->updateAnimationTimerForRenderer(renderer); 538#if ENABLE(REQUEST_ANIMATION_FRAME) 539 if (FrameView* view = renderer->document()->view()) 540 view->scheduleAnimation(); 541#endif 542 } 543 544 if (blendedStyle != newStyle) { 545 // If the animations/transitions change opacity or transform, we need to update 546 // the style to impose the stacking rules. Note that this is also 547 // done in StyleResolver::adjustRenderStyle(). 548 if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform())) 549 blendedStyle->setZIndex(0); 550 } 551 return blendedStyle.release(); 552} 553 554PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer) 555{ 556 return m_data->getAnimatedStyleForRenderer(renderer); 557} 558 559void AnimationController::notifyAnimationStarted(RenderObject*, double startTime) 560{ 561 m_data->receivedStartTimeResponse(startTime); 562} 563 564bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const AtomicString& name, double t) 565{ 566 return m_data->pauseAnimationAtTime(renderer, name, t); 567} 568 569unsigned AnimationController::numberOfActiveAnimations(Document* document) const 570{ 571 return m_data->numberOfActiveAnimations(document); 572} 573 574bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t) 575{ 576 return m_data->pauseTransitionAtTime(renderer, property, t); 577} 578 579bool AnimationController::isRunningAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 580{ 581 return m_data->isRunningAnimationOnRenderer(renderer, property, isRunningNow); 582} 583 584bool AnimationController::isRunningAcceleratedAnimationOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const 585{ 586 return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow); 587} 588 589bool AnimationController::isSuspended() const 590{ 591 return m_data->isSuspended(); 592} 593 594void AnimationController::suspendAnimations() 595{ 596 LOG(Animations, "controller is suspending animations"); 597 m_data->suspendAnimations(); 598} 599 600void AnimationController::resumeAnimations() 601{ 602 LOG(Animations, "controller is resuming animations"); 603 m_data->resumeAnimations(); 604} 605 606#if ENABLE(REQUEST_ANIMATION_FRAME) 607void AnimationController::serviceAnimations() 608{ 609 m_data->animationFrameCallbackFired(); 610} 611#endif 612 613void AnimationController::suspendAnimationsForDocument(Document* document) 614{ 615 LOG(Animations, "suspending animations for document %p", document); 616 m_data->suspendAnimationsForDocument(document); 617} 618 619void AnimationController::resumeAnimationsForDocument(Document* document) 620{ 621 LOG(Animations, "resuming animations for document %p", document); 622 m_data->resumeAnimationsForDocument(document); 623} 624 625void AnimationController::startAnimationsIfNotSuspended(Document* document) 626{ 627 LOG(Animations, "animations may start for document %p", document); 628 m_data->startAnimationsIfNotSuspended(document); 629} 630 631void AnimationController::beginAnimationUpdate() 632{ 633 if (!m_beginAnimationUpdateCount) 634 m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); 635 ++m_beginAnimationUpdateCount; 636} 637 638void AnimationController::endAnimationUpdate() 639{ 640 ASSERT(m_beginAnimationUpdateCount > 0); 641 --m_beginAnimationUpdateCount; 642 if (!m_beginAnimationUpdateCount) 643 m_data->endAnimationUpdate(); 644} 645 646bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) 647{ 648#if USE(ACCELERATED_COMPOSITING) 649 return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); 650#else 651 UNUSED_PARAM(property); 652 return false; 653#endif 654} 655 656} // namespace WebCore 657