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 "WebKitTransitionEvent.h"
     41 #include <wtf/CurrentTime.h>
     42 #include <wtf/UnusedParam.h>
     43 
     44 namespace WebCore {
     45 
     46 static const double cAnimationTimerDelay = 0.025;
     47 static const double cBeginAnimationUpdateTimeNotSet = -1;
     48 
     49 AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame)
     50     : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired)
     51     , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired)
     52     , m_frame(frame)
     53     , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet)
     54     , m_styleAvailableWaiters(0)
     55     , m_lastStyleAvailableWaiter(0)
     56     , m_responseWaiters(0)
     57     , m_lastResponseWaiter(0)
     58     , m_waitingForResponse(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->isSuspended();
     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->isSuspended() && 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     // fire all the events
    138     Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = m_eventsToDispatch.end();
    139     for (Vector<EventToDispatch>::const_iterator it = m_eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) {
    140         if (it->eventType == eventNames().webkitTransitionEndEvent)
    141             it->element->dispatchEvent(WebKitTransitionEvent::create(it->eventType, it->name, it->elapsedTime));
    142         else
    143             it->element->dispatchEvent(WebKitAnimationEvent::create(it->eventType, it->name, it->elapsedTime));
    144     }
    145 
    146     m_eventsToDispatch.clear();
    147 
    148     // call setChanged on all the elements
    149     Vector<RefPtr<Node> >::const_iterator nodeChangesToDispatchEnd = m_nodeChangesToDispatch.end();
    150     for (Vector<RefPtr<Node> >::const_iterator it = m_nodeChangesToDispatch.begin(); it != nodeChangesToDispatchEnd; ++it)
    151         (*it)->setNeedsStyleRecalc(SyntheticStyleChange);
    152 
    153     m_nodeChangesToDispatch.clear();
    154 
    155     if (m_frame)
    156         m_frame->document()->updateStyleIfNeeded();
    157 }
    158 
    159 void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher()
    160 {
    161     if (!m_updateStyleIfNeededDispatcher.isActive())
    162         m_updateStyleIfNeededDispatcher.startOneShot(0);
    163 }
    164 
    165 void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime)
    166 {
    167     m_eventsToDispatch.grow(m_eventsToDispatch.size()+1);
    168     EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1];
    169     event.element = element;
    170     event.eventType = eventType;
    171     event.name = name;
    172     event.elapsedTime = elapsedTime;
    173 
    174     startUpdateStyleIfNeededDispatcher();
    175 }
    176 
    177 void AnimationControllerPrivate::addNodeChangeToDispatch(PassRefPtr<Node> node)
    178 {
    179     ASSERT(!node || (node->document() && !node->document()->inPageCache()));
    180     if (!node)
    181         return;
    182 
    183     m_nodeChangesToDispatch.append(node);
    184     startUpdateStyleIfNeededDispatcher();
    185 }
    186 
    187 void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>*)
    188 {
    189     // Make sure animationUpdateTime is updated, so that it is current even if no
    190     // styleChange has happened (e.g. accelerated animations)
    191     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
    192 
    193     // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate
    194     // updateStyleIfNeeded.  It will then call back to us with new information.
    195     updateAnimationTimer(true);
    196 }
    197 
    198 bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const
    199 {
    200     RefPtr<CompositeAnimation> animation = m_compositeAnimations.get(renderer);
    201     if (!animation)
    202         return false;
    203 
    204     return animation->isAnimatingProperty(property, isRunningNow);
    205 }
    206 
    207 void AnimationControllerPrivate::suspendAnimations(Document* document)
    208 {
    209     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
    210 
    211     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
    212     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
    213         RenderObject* renderer = it->first;
    214         if (renderer->document() == document) {
    215             CompositeAnimation* compAnim = it->second.get();
    216             compAnim->suspendAnimations();
    217         }
    218     }
    219 
    220     updateAnimationTimer();
    221 }
    222 
    223 void AnimationControllerPrivate::resumeAnimations(Document* document)
    224 {
    225     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
    226 
    227     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
    228     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
    229         RenderObject* renderer = it->first;
    230         if (renderer->document() == document) {
    231             CompositeAnimation* compAnim = it->second.get();
    232             compAnim->resumeAnimations();
    233         }
    234     }
    235 
    236     updateAnimationTimer();
    237 }
    238 
    239 bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t)
    240 {
    241     if (!renderer)
    242         return false;
    243 
    244     RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer);
    245     if (!compAnim)
    246         return false;
    247 
    248     if (compAnim->pauseAnimationAtTime(name, t)) {
    249         renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
    250         startUpdateStyleIfNeededDispatcher();
    251         return true;
    252     }
    253 
    254     return false;
    255 }
    256 
    257 bool AnimationControllerPrivate::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t)
    258 {
    259     if (!renderer)
    260         return false;
    261 
    262     RefPtr<CompositeAnimation> compAnim = accessCompositeAnimation(renderer);
    263     if (!compAnim)
    264         return false;
    265 
    266     if (compAnim->pauseTransitionAtTime(cssPropertyID(property), t)) {
    267         renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange);
    268         startUpdateStyleIfNeededDispatcher();
    269         return true;
    270     }
    271 
    272     return false;
    273 }
    274 
    275 double AnimationControllerPrivate::beginAnimationUpdateTime()
    276 {
    277     if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet)
    278         m_beginAnimationUpdateTime = currentTime();
    279     return m_beginAnimationUpdateTime;
    280 }
    281 
    282 void AnimationControllerPrivate::endAnimationUpdate()
    283 {
    284     styleAvailable();
    285     if (!m_waitingForResponse)
    286         startTimeResponse(beginAnimationUpdateTime());
    287 }
    288 
    289 void AnimationControllerPrivate::receivedStartTimeResponse(double time)
    290 {
    291     m_waitingForResponse = false;
    292     startTimeResponse(time);
    293 }
    294 
    295 PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderObject* renderer)
    296 {
    297     if (!renderer)
    298         return 0;
    299 
    300     RefPtr<CompositeAnimation> rendererAnimations = m_compositeAnimations.get(renderer);
    301     if (!rendererAnimations)
    302         return renderer->style();
    303 
    304     // Make sure animationUpdateTime is updated, so that it is current even if no
    305     // styleChange has happened (e.g. accelerated animations).
    306     setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
    307     RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle();
    308     if (!animatingStyle)
    309         animatingStyle = renderer->style();
    310 
    311     return animatingStyle.release();
    312 }
    313 
    314 unsigned AnimationControllerPrivate::numberOfActiveAnimations() const
    315 {
    316     unsigned count = 0;
    317 
    318     RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
    319     for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
    320         CompositeAnimation* compAnim = it->second.get();
    321         count += compAnim->numberOfActiveAnimations();
    322     }
    323 
    324     return count;
    325 }
    326 
    327 void AnimationControllerPrivate::addToStyleAvailableWaitList(AnimationBase* animation)
    328 {
    329     ASSERT(!animation->next());
    330 
    331     if (m_styleAvailableWaiters)
    332         m_lastStyleAvailableWaiter->setNext(animation);
    333     else
    334         m_styleAvailableWaiters = animation;
    335 
    336     m_lastStyleAvailableWaiter = animation;
    337     animation->setNext(0);
    338 }
    339 
    340 void AnimationControllerPrivate::removeFromStyleAvailableWaitList(AnimationBase* animationToRemove)
    341 {
    342     AnimationBase* prevAnimation = 0;
    343     for (AnimationBase* animation = m_styleAvailableWaiters; animation; animation = animation->next()) {
    344         if (animation == animationToRemove) {
    345             if (prevAnimation)
    346                 prevAnimation->setNext(animation->next());
    347             else
    348                 m_styleAvailableWaiters = animation->next();
    349 
    350             if (m_lastStyleAvailableWaiter == animation)
    351                 m_lastStyleAvailableWaiter = prevAnimation;
    352 
    353             animationToRemove->setNext(0);
    354         }
    355     }
    356 }
    357 
    358 void AnimationControllerPrivate::styleAvailable()
    359 {
    360     // Go through list of waiters and send them on their way
    361     for (AnimationBase* animation = m_styleAvailableWaiters; animation; ) {
    362         AnimationBase* nextAnimation = animation->next();
    363         animation->setNext(0);
    364         animation->styleAvailable();
    365         animation = nextAnimation;
    366     }
    367 
    368     m_styleAvailableWaiters = 0;
    369     m_lastStyleAvailableWaiter = 0;
    370 }
    371 
    372 void AnimationControllerPrivate::addToStartTimeResponseWaitList(AnimationBase* animation, bool willGetResponse)
    373 {
    374     // If willGetResponse is true, it means this animation is actually waiting for a response
    375     // (which will come in as a call to notifyAnimationStarted()).
    376     // In that case we don't need to add it to this list. We just set a waitingForAResponse flag
    377     // which says we are waiting for the response. If willGetResponse is false, this animation
    378     // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for
    379     // another animation to which it will sync.
    380     //
    381     // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is
    382     // true. If so, we just return and will do our work when the first notifyXXXStarted() call
    383     // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will
    384     // do our work right away. In both cases we call the onAnimationStartResponse() method
    385     // on each animation. In the first case we send in the time we got from notifyXXXStarted().
    386     // In the second case, we just pass in the beginAnimationUpdateTime().
    387     //
    388     // This will synchronize all software and accelerated animations started in the same
    389     // updateStyleIfNeeded cycle.
    390     //
    391     ASSERT(!animation->next());
    392 
    393     if (willGetResponse)
    394         m_waitingForResponse = true;
    395 
    396     if (m_responseWaiters)
    397         m_lastResponseWaiter->setNext(animation);
    398     else
    399         m_responseWaiters = animation;
    400 
    401     m_lastResponseWaiter = animation;
    402     animation->setNext(0);
    403 }
    404 
    405 void AnimationControllerPrivate::removeFromStartTimeResponseWaitList(AnimationBase* animationToRemove)
    406 {
    407     AnimationBase* prevAnimation = 0;
    408     for (AnimationBase* animation = m_responseWaiters; animation; animation = animation->next()) {
    409         if (animation == animationToRemove) {
    410             if (prevAnimation)
    411                 prevAnimation->setNext(animation->next());
    412             else
    413                 m_responseWaiters = animation->next();
    414 
    415             if (m_lastResponseWaiter == animation)
    416                 m_lastResponseWaiter = prevAnimation;
    417 
    418             animationToRemove->setNext(0);
    419         }
    420         prevAnimation = animation;
    421     }
    422 }
    423 
    424 void AnimationControllerPrivate::startTimeResponse(double time)
    425 {
    426     // Go through list of waiters and send them on their way
    427     for (AnimationBase* animation = m_responseWaiters; animation; ) {
    428         AnimationBase* nextAnimation = animation->next();
    429         animation->setNext(0);
    430         animation->onAnimationStartResponse(time);
    431         animation = nextAnimation;
    432     }
    433 
    434     m_responseWaiters = 0;
    435     m_lastResponseWaiter = 0;
    436 }
    437 
    438 AnimationController::AnimationController(Frame* frame)
    439     : m_data(new AnimationControllerPrivate(frame))
    440 {
    441 }
    442 
    443 AnimationController::~AnimationController()
    444 {
    445     delete m_data;
    446 }
    447 
    448 void AnimationController::cancelAnimations(RenderObject* renderer)
    449 {
    450     if (!m_data->hasAnimations())
    451         return;
    452 
    453     if (m_data->clear(renderer)) {
    454         Node* node = renderer->node();
    455         ASSERT(!node || (node->document() && !node->document()->inPageCache()));
    456         node->setNeedsStyleRecalc(SyntheticStyleChange);
    457     }
    458 }
    459 
    460 PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle)
    461 {
    462     // Don't do anything if we're in the cache
    463     if (!renderer->document() || renderer->document()->inPageCache())
    464         return newStyle;
    465 
    466     RenderStyle* oldStyle = renderer->style();
    467 
    468     if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions()))
    469         return newStyle;
    470 
    471     // Don't run transitions when printing.
    472     if (renderer->view()->printing())
    473         return newStyle;
    474 
    475     // Fetch our current set of implicit animations from a hashtable.  We then compare them
    476     // against the animations in the style and make sure we're in sync.  If destination values
    477     // have changed, we reset the animation.  We then do a blend to get new values and we return
    478     // a new style.
    479     ASSERT(renderer->node()); // FIXME: We do not animate generated content yet.
    480 
    481     RefPtr<CompositeAnimation> rendererAnimations = m_data->accessCompositeAnimation(renderer);
    482     RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle);
    483 
    484     m_data->updateAnimationTimer();
    485 
    486     if (blendedStyle != newStyle) {
    487         // If the animations/transitions change opacity or transform, we need to update
    488         // the style to impose the stacking rules. Note that this is also
    489         // done in CSSStyleSelector::adjustRenderStyle().
    490         if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform()))
    491             blendedStyle->setZIndex(0);
    492     }
    493     return blendedStyle.release();
    494 }
    495 
    496 PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderObject* renderer)
    497 {
    498     return m_data->getAnimatedStyleForRenderer(renderer);
    499 }
    500 
    501 void AnimationController::notifyAnimationStarted(RenderObject*, double startTime)
    502 {
    503     m_data->receivedStartTimeResponse(startTime);
    504 }
    505 
    506 bool AnimationController::pauseAnimationAtTime(RenderObject* renderer, const String& name, double t)
    507 {
    508     return m_data->pauseAnimationAtTime(renderer, name, t);
    509 }
    510 
    511 unsigned AnimationController::numberOfActiveAnimations() const
    512 {
    513     return m_data->numberOfActiveAnimations();
    514 }
    515 
    516 bool AnimationController::pauseTransitionAtTime(RenderObject* renderer, const String& property, double t)
    517 {
    518     return m_data->pauseTransitionAtTime(renderer, property, t);
    519 }
    520 
    521 bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, CSSPropertyID property, bool isRunningNow) const
    522 {
    523     return m_data->isAnimatingPropertyOnRenderer(renderer, property, isRunningNow);
    524 }
    525 
    526 void AnimationController::suspendAnimations(Document* document)
    527 {
    528     m_data->suspendAnimations(document);
    529 }
    530 
    531 void AnimationController::resumeAnimations(Document* document)
    532 {
    533     m_data->resumeAnimations(document);
    534 }
    535 
    536 void AnimationController::beginAnimationUpdate()
    537 {
    538     m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
    539 }
    540 
    541 void AnimationController::endAnimationUpdate()
    542 {
    543     m_data->endAnimationUpdate();
    544 }
    545 
    546 bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property)
    547 {
    548 #if USE(ACCELERATED_COMPOSITING)
    549     return AnimationBase::animationOfPropertyIsAccelerated(property);
    550 #else
    551     UNUSED_PARAM(property);
    552     return false;
    553 #endif
    554 }
    555 
    556 } // namespace WebCore
    557