Home | History | Annotate | Download | only in animation
      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