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