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