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 "core/page/animation/AnimationBase.h"
     31 
     32 #include "core/page/animation/AnimationControllerPrivate.h"
     33 #include "core/page/animation/CompositeAnimation.h"
     34 #include "core/platform/animation/AnimationUtilities.h"
     35 #include "core/platform/animation/TimingFunction.h"
     36 #include "core/rendering/RenderBox.h"
     37 #include <algorithm>
     38 
     39 using namespace std;
     40 
     41 namespace WebCore {
     42 
     43 AnimationBase::AnimationBase(const CSSAnimationData* transition, RenderObject* renderer, CompositeAnimation* compAnim)
     44     : m_animState(AnimationStateNew)
     45     , m_isAccelerated(false)
     46     , m_transformFunctionListValid(false)
     47     , m_filterFunctionListsMatch(false)
     48     , m_startTime(0)
     49     , m_pauseTime(-1)
     50     , m_requestedStartTime(0)
     51     , m_totalDuration(-1)
     52     , m_nextIterationDuration(-1)
     53     , m_object(renderer)
     54     , m_animation(const_cast<CSSAnimationData*>(transition))
     55     , m_compAnim(compAnim)
     56 {
     57     // Compute the total duration
     58     if (m_animation->iterationCount() > 0)
     59         m_totalDuration = m_animation->duration() * m_animation->iterationCount();
     60 }
     61 
     62 void AnimationBase::setNeedsStyleRecalc(Node* node)
     63 {
     64     if (node)
     65         node->setNeedsStyleRecalc(LocalStyleChange);
     66 }
     67 
     68 double AnimationBase::duration() const
     69 {
     70     return m_animation->duration();
     71 }
     72 
     73 bool AnimationBase::playStatePlaying() const
     74 {
     75     return m_animation->playState() == AnimPlayStatePlaying;
     76 }
     77 
     78 bool AnimationBase::animationsMatch(const CSSAnimationData* anim) const
     79 {
     80     return m_animation->animationsMatch(anim);
     81 }
     82 
     83 void AnimationBase::updateStateMachine(AnimStateInput input, double param)
     84 {
     85     if (!m_compAnim)
     86         return;
     87 
     88     // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state.
     89     if (input == AnimationStateInputMakeNew) {
     90         if (m_animState == AnimationStateStartWaitStyleAvailable)
     91             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
     92         m_animState = AnimationStateNew;
     93         m_startTime = 0;
     94         m_pauseTime = -1;
     95         m_requestedStartTime = 0;
     96         m_nextIterationDuration = -1;
     97         endAnimation();
     98         return;
     99     }
    100 
    101     if (input == AnimationStateInputRestartAnimation) {
    102         if (m_animState == AnimationStateStartWaitStyleAvailable)
    103             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
    104         m_animState = AnimationStateNew;
    105         m_startTime = 0;
    106         m_pauseTime = -1;
    107         m_requestedStartTime = 0;
    108         m_nextIterationDuration = -1;
    109         endAnimation();
    110 
    111         if (!paused())
    112             updateStateMachine(AnimationStateInputStartAnimation, -1);
    113         return;
    114     }
    115 
    116     if (input == AnimationStateInputEndAnimation) {
    117         if (m_animState == AnimationStateStartWaitStyleAvailable)
    118             m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
    119         m_animState = AnimationStateDone;
    120         endAnimation();
    121         return;
    122     }
    123 
    124     if (input == AnimationStateInputPauseOverride) {
    125         if (m_animState == AnimationStateStartWaitResponse) {
    126             // If we are in AnimationStateStartWaitResponse, the animation will get canceled before
    127             // we get a response, so move to the next state.
    128             endAnimation();
    129             updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
    130         }
    131         return;
    132     }
    133 
    134     if (input == AnimationStateInputResumeOverride) {
    135         if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) {
    136             // Start the animation
    137             startAnimation(beginAnimationUpdateTime() - m_startTime);
    138         }
    139         return;
    140     }
    141 
    142     // Execute state machine
    143     switch (m_animState) {
    144         case AnimationStateNew:
    145             ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused);
    146             if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) {
    147                 m_requestedStartTime = beginAnimationUpdateTime();
    148                 m_animState = AnimationStateStartWaitTimer;
    149             }
    150             break;
    151         case AnimationStateStartWaitTimer:
    152             ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused);
    153 
    154             if (input == AnimationStateInputStartTimerFired) {
    155                 ASSERT(param >= 0);
    156                 // Start timer has fired, tell the animation to start and wait for it to respond with start time
    157                 m_animState = AnimationStateStartWaitStyleAvailable;
    158                 m_compAnim->animationController()->addToAnimationsWaitingForStyle(this);
    159 
    160                 // Trigger a render so we can start the animation
    161                 if (m_object)
    162                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
    163             } else {
    164                 ASSERT(!paused());
    165                 // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
    166                 m_pauseTime = beginAnimationUpdateTime();
    167                 m_animState = AnimationStatePausedWaitTimer;
    168             }
    169             break;
    170         case AnimationStateStartWaitStyleAvailable:
    171             ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused);
    172 
    173             if (input == AnimationStateInputStyleAvailable) {
    174                 // Start timer has fired, tell the animation to start and wait for it to respond with start time
    175                 m_animState = AnimationStateStartWaitResponse;
    176 
    177                 overrideAnimations();
    178 
    179                 // Start the animation
    180                 if (overridden()) {
    181                     // We won't try to start accelerated animations if we are overridden and
    182                     // just move on to the next state.
    183                     m_animState = AnimationStateStartWaitResponse;
    184                     m_isAccelerated = false;
    185                     updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
    186                 } else {
    187                     double timeOffset = 0;
    188                     // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
    189                     if (m_animation->delay() < 0)
    190                         timeOffset = -m_animation->delay();
    191                     bool started = startAnimation(timeOffset);
    192 
    193                     m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
    194                     m_isAccelerated = started;
    195                 }
    196             } else {
    197                 // We're waiting for the style to be available and we got a pause. Pause and wait
    198                 m_pauseTime = beginAnimationUpdateTime();
    199                 m_animState = AnimationStatePausedWaitStyleAvailable;
    200             }
    201             break;
    202         case AnimationStateStartWaitResponse:
    203             ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused);
    204 
    205             if (input == AnimationStateInputStartTimeSet) {
    206                 ASSERT(param >= 0);
    207                 // We have a start time, set it, unless the startTime is already set
    208                 if (m_startTime <= 0) {
    209                     m_startTime = param;
    210                     // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
    211                     if (m_animation->delay() < 0)
    212                         m_startTime += m_animation->delay();
    213                 }
    214 
    215                 // Now that we know the start time, fire the start event.
    216                 onAnimationStart(0); // The elapsedTime is 0.
    217 
    218                 // Decide whether to go into looping or ending state
    219                 goIntoEndingOrLoopingState();
    220 
    221                 // Dispatch updateStyleIfNeeded so we can start the animation
    222                 if (m_object)
    223                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
    224             } else {
    225                 // We are pausing while waiting for a start response. Cancel the animation and wait. When
    226                 // we unpause, we will act as though the start timer just fired
    227                 m_pauseTime = beginAnimationUpdateTime();
    228                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
    229                 m_animState = AnimationStatePausedWaitResponse;
    230             }
    231             break;
    232         case AnimationStateLooping:
    233             ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused);
    234 
    235             if (input == AnimationStateInputLoopTimerFired) {
    236                 ASSERT(param >= 0);
    237                 // Loop timer fired, loop again or end.
    238                 onAnimationIteration(param);
    239 
    240                 // Decide whether to go into looping or ending state
    241                 goIntoEndingOrLoopingState();
    242             } else {
    243                 // We are pausing while running. Cancel the animation and wait
    244                 m_pauseTime = beginAnimationUpdateTime();
    245                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
    246                 m_animState = AnimationStatePausedRun;
    247             }
    248             break;
    249         case AnimationStateEnding:
    250 #if !LOG_DISABLED
    251             if (input != AnimationStateInputEndTimerFired && input != AnimationStateInputPlayStatePaused)
    252                 LOG_ERROR("State is AnimationStateEnding, but input is not AnimationStateInputEndTimerFired or AnimationStateInputPlayStatePaused. It is %d.", input);
    253 #endif
    254             if (input == AnimationStateInputEndTimerFired) {
    255 
    256                 ASSERT(param >= 0);
    257                 // End timer fired, finish up
    258                 onAnimationEnd(param);
    259 
    260                 m_animState = AnimationStateDone;
    261 
    262                 if (m_object) {
    263                     if (m_animation->fillsForwards())
    264                         m_animState = AnimationStateFillingForwards;
    265                     else
    266                         resumeOverriddenAnimations();
    267 
    268                     // Fire off another style change so we can set the final value
    269                     m_compAnim->animationController()->addNodeChangeToDispatch(m_object->node());
    270                 }
    271             } else {
    272                 // We are pausing while running. Cancel the animation and wait
    273                 m_pauseTime = beginAnimationUpdateTime();
    274                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
    275                 m_animState = AnimationStatePausedRun;
    276             }
    277             // |this| may be deleted here
    278             break;
    279         case AnimationStatePausedWaitTimer:
    280             ASSERT(input == AnimationStateInputPlayStateRunning);
    281             ASSERT(paused());
    282             // Update the times
    283             m_startTime += beginAnimationUpdateTime() - m_pauseTime;
    284             m_pauseTime = -1;
    285 
    286             // we were waiting for the start timer to fire, go back and wait again
    287             m_animState = AnimationStateNew;
    288             updateStateMachine(AnimationStateInputStartAnimation, 0);
    289             break;
    290         case AnimationStatePausedWaitResponse:
    291         case AnimationStatePausedWaitStyleAvailable:
    292         case AnimationStatePausedRun:
    293             // We treat these two cases the same. The only difference is that, when we are in
    294             // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
    295             // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
    296             // that we have already set the startTime and will ignore it.
    297             ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable);
    298             ASSERT(paused());
    299 
    300             if (input == AnimationStateInputPlayStateRunning) {
    301                 // Update the times
    302                 if (m_animState == AnimationStatePausedRun)
    303                     m_startTime += beginAnimationUpdateTime() - m_pauseTime;
    304                 else
    305                     m_startTime = 0;
    306                 m_pauseTime = -1;
    307 
    308                 if (m_animState == AnimationStatePausedWaitStyleAvailable)
    309                     m_animState = AnimationStateStartWaitStyleAvailable;
    310                 else {
    311                     // We were either running or waiting for a begin time response from the animation.
    312                     // Either way we need to restart the animation (possibly with an offset if we
    313                     // had already been running) and wait for it to start.
    314                     m_animState = AnimationStateStartWaitResponse;
    315 
    316                     // Start the animation
    317                     if (overridden()) {
    318                         // We won't try to start accelerated animations if we are overridden and
    319                         // just move on to the next state.
    320                         updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
    321                         m_isAccelerated = true;
    322                     } else {
    323                         bool started = startAnimation(beginAnimationUpdateTime() - m_startTime);
    324                         m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
    325                         m_isAccelerated = started;
    326                     }
    327                 }
    328                 break;
    329             }
    330 
    331             if (input == AnimationStateInputStartTimeSet) {
    332                 ASSERT(m_animState == AnimationStatePausedWaitResponse);
    333 
    334                 // We are paused but we got the callback that notifies us that an accelerated animation started.
    335                 // We ignore the start time and just move into the paused-run state.
    336                 m_animState = AnimationStatePausedRun;
    337                 ASSERT(m_startTime == 0);
    338                 m_startTime = param;
    339                 m_pauseTime += m_startTime;
    340                 break;
    341             }
    342 
    343             ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable);
    344             // We are paused but we got the callback that notifies us that style has been updated.
    345             // We move to the AnimationStatePausedWaitResponse state
    346             m_animState = AnimationStatePausedWaitResponse;
    347             overrideAnimations();
    348             break;
    349         case AnimationStateFillingForwards:
    350         case AnimationStateDone:
    351             // We're done. Stay in this state until we are deleted
    352             break;
    353     }
    354 }
    355 
    356 void AnimationBase::fireAnimationEventsIfNeeded()
    357 {
    358     if (!m_compAnim)
    359         return;
    360 
    361     // If we are waiting for the delay time to expire and it has, go to the next state
    362     if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding)
    363         return;
    364 
    365     // We have to make sure to keep a ref to the this pointer, because it could get destroyed
    366     // during an animation callback that might get called. Since the owner is a CompositeAnimation
    367     // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase
    368     // can still access the resources of its CompositeAnimation as needed.
    369     RefPtr<AnimationBase> protector(this);
    370     RefPtr<CompositeAnimation> compProtector(m_compAnim);
    371 
    372     // Check for start timeout
    373     if (m_animState == AnimationStateStartWaitTimer) {
    374         if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay())
    375             updateStateMachine(AnimationStateInputStartTimerFired, 0);
    376         return;
    377     }
    378 
    379     double elapsedDuration = getElapsedTime();
    380 
    381     // Check for end timeout
    382     if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) {
    383         // We may still be in AnimationStateLooping if we've managed to skip a
    384         // whole iteration, in which case we should jump to the end state.
    385         m_animState = AnimationStateEnding;
    386 
    387         // Fire an end event
    388         updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration);
    389     } else {
    390         // Check for iteration timeout
    391         if (m_nextIterationDuration < 0) {
    392             // Hasn't been set yet, set it
    393             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
    394             m_nextIterationDuration = elapsedDuration + durationLeft;
    395         }
    396 
    397         if (elapsedDuration >= m_nextIterationDuration) {
    398             // Set to the next iteration
    399             double previous = m_nextIterationDuration;
    400             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
    401             m_nextIterationDuration = elapsedDuration + durationLeft;
    402 
    403             // Send the event
    404             updateStateMachine(AnimationStateInputLoopTimerFired, previous);
    405         }
    406     }
    407 }
    408 
    409 void AnimationBase::updatePlayState(EAnimPlayState playState)
    410 {
    411     if (!m_compAnim)
    412         return;
    413 
    414     // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended.
    415     // The state machine can be in one of two states: running, paused.
    416     // Set the state machine to the desired state.
    417     bool pause = playState == AnimPlayStatePaused || m_compAnim->suspended();
    418 
    419     if (pause == paused() && !isNew())
    420         return;
    421 
    422     updateStateMachine(pause ?  AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1);
    423 }
    424 
    425 double AnimationBase::timeToNextService()
    426 {
    427     // Returns the time at which next service is required. -1 means no service is required. 0 means
    428     // service is required now, and > 0 means service is required that many seconds in the future.
    429     if (paused() || isNew() || m_animState == AnimationStateFillingForwards)
    430         return -1;
    431 
    432     if (m_animState == AnimationStateStartWaitTimer) {
    433         double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime);
    434         return max(timeFromNow, 0.0);
    435     }
    436 
    437     fireAnimationEventsIfNeeded();
    438 
    439     // In all other cases, we need service right away.
    440     return 0;
    441 }
    442 
    443 // Compute the fractional time, taking into account direction.
    444 // There is no need to worry about iterations, we assume that we would have
    445 // short circuited above if we were done.
    446 
    447 double AnimationBase::fractionalTime(double scale, double elapsedTime, double offset) const
    448 {
    449     double fractionalTime = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
    450     // FIXME: startTime can be before the current animation "frame" time. This is to sync with the frame time
    451     // concept in AnimationTimeController. So we need to somehow sync the two. Until then, the possible
    452     // error is small and will probably not be noticeable. Until we fix this, remove the assert.
    453     // https://bugs.webkit.org/show_bug.cgi?id=52037
    454     // ASSERT(fractionalTime >= 0);
    455     if (fractionalTime < 0)
    456         fractionalTime = 0;
    457 
    458     int integralTime = static_cast<int>(fractionalTime);
    459     const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
    460     const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
    461     if (m_animation->iterationCount() != CSSAnimationData::IterationCountInfinite && !iterationCountHasFractional)
    462         integralTime = min(integralTime, integralIterationCount - 1);
    463 
    464     fractionalTime -= integralTime;
    465 
    466     if (((m_animation->direction() == CSSAnimationData::AnimationDirectionAlternate) && (integralTime & 1))
    467         || ((m_animation->direction() == CSSAnimationData::AnimationDirectionAlternateReverse) && !(integralTime & 1))
    468         || m_animation->direction() == CSSAnimationData::AnimationDirectionReverse)
    469         fractionalTime = 1 - fractionalTime;
    470 
    471     if (scale != 1 || offset)
    472         fractionalTime = (fractionalTime - offset) * scale;
    473 
    474     return fractionalTime;
    475 }
    476 
    477 double AnimationBase::progress(double scale, double offset, const TimingFunction* timingFunction) const
    478 {
    479     if (preActive())
    480         return 0;
    481 
    482     double dur = m_animation->duration();
    483     if (m_animation->iterationCount() > 0)
    484         dur *= m_animation->iterationCount();
    485 
    486     if (postActive() || !m_animation->duration())
    487         return 1.0;
    488 
    489     double elapsedTime = getElapsedTime();
    490     if (m_animation->iterationCount() > 0 && elapsedTime >= dur) {
    491         const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
    492         const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
    493         return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0;
    494     }
    495 
    496     const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset);
    497 
    498     if (!timingFunction)
    499         timingFunction = m_animation->timingFunction().get();
    500 
    501     return timingFunction->evaluate(fractionalTime, accuracyForDuration(m_animation->duration()));
    502 }
    503 
    504 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
    505 {
    506     if (postActive()) {
    507         time = -1;
    508         isLooping = false;
    509         return;
    510     }
    511 
    512     // Decide when the end or loop event needs to fire
    513     const double elapsedDuration = getElapsedTime();
    514     double durationLeft = 0;
    515     double nextIterationTime = m_totalDuration;
    516 
    517     if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) {
    518         durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0;
    519         nextIterationTime = elapsedDuration + durationLeft;
    520     }
    521 
    522     if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) {
    523         // We are not at the end yet
    524         ASSERT(nextIterationTime > 0);
    525         isLooping = true;
    526     } else {
    527         // We are at the end
    528         isLooping = false;
    529     }
    530 
    531     time = durationLeft;
    532 }
    533 
    534 void AnimationBase::goIntoEndingOrLoopingState()
    535 {
    536     double t;
    537     bool isLooping;
    538     getTimeToNextEvent(t, isLooping);
    539     m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
    540 }
    541 
    542 void AnimationBase::freezeAtTime(double t)
    543 {
    544     if (!m_compAnim)
    545         return;
    546 
    547     if (!m_startTime) {
    548         // If we haven't started yet, make it as if we started.
    549         m_animState = AnimationStateStartWaitResponse;
    550         onAnimationStartResponse(beginAnimationUpdateTime());
    551     }
    552 
    553     ASSERT(m_startTime);        // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
    554     if (t <= m_animation->delay())
    555         m_pauseTime = m_startTime;
    556     else
    557         m_pauseTime = m_startTime + t - m_animation->delay();
    558 
    559     if (m_object && m_object->isComposited())
    560         toRenderBoxModelObject(m_object)->suspendAnimations(m_pauseTime);
    561 }
    562 
    563 double AnimationBase::beginAnimationUpdateTime() const
    564 {
    565     if (!m_compAnim)
    566         return 0;
    567 
    568     return m_compAnim->animationController()->beginAnimationUpdateTime();
    569 }
    570 
    571 double AnimationBase::getElapsedTime() const
    572 {
    573     ASSERT(!postActive());
    574     if (paused())
    575         return m_pauseTime - m_startTime;
    576     if (m_startTime <= 0)
    577         return 0;
    578 
    579     double elapsedTime = beginAnimationUpdateTime() - m_startTime;
    580     // It's possible for the start time to be ahead of the last update time
    581     // if the compositor has just sent notification for the start of an
    582     // accelerated animation.
    583     return max(elapsedTime, 0.0);
    584 }
    585 
    586 } // namespace WebCore
    587