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