Animations and Transitions should not start when globally suspended
[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::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
137 void 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     
443 void 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
501 void 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
517 double 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
539 double 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
569 double 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
611 void 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
635 void 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   
644 void 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
668 double AnimationBase::beginAnimationUpdateTime() const
669 {
670     if (!m_compAnim)
671         return 0;
672
673     return m_compAnim->animationController()->beginAnimationUpdateTime();
674 }
675
676 double 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
688 void AnimationBase::setElapsedTime(double time)
689 {
690     // FIXME: implement this method
691     UNUSED_PARAM(time);
692 }
693
694 void AnimationBase::play()
695 {
696     // FIXME: implement this method
697 }
698
699 void AnimationBase::pause()
700 {
701     // FIXME: implement this method
702 }
703
704 } // namespace WebCore