CSS Animations with triggers should map scroll position to duration
[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 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 "GeometryUtilities.h"
40 #include "Logging.h"
41 #include "RenderBox.h"
42 #include "RenderStyle.h"
43 #include "RenderView.h"
44 #include "UnitBezier.h"
45 #include <algorithm>
46 #include <wtf/CurrentTime.h>
47 #include <wtf/Ref.h>
48
49 namespace WebCore {
50
51 // The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the
52 // animation, the more precision we need in the timing function result to avoid ugly discontinuities.
53 static inline double solveEpsilon(double duration)
54 {
55     return 1.0 / (200.0 * duration);
56 }
57
58 static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
59 {
60     // Convert from input time to parametric value in curve, then from
61     // that to output time.
62     UnitBezier bezier(p1x, p1y, p2x, p2y);
63     return bezier.solve(t, solveEpsilon(duration));
64 }
65
66 static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
67 {
68     if (stepAtStart)
69         return std::min(1.0, (floor(numSteps * t) + 1) / numSteps);
70     return floor(numSteps * t) / numSteps;
71 }
72
73 AnimationBase::AnimationBase(const Animation& transition, RenderElement* renderer, CompositeAnimation* compositeAnimation)
74     : m_animationState(AnimationState::New)
75     , m_isAccelerated(false)
76     , m_transformFunctionListValid(false)
77     , m_filterFunctionListsMatch(false)
78     , m_startTime(0)
79     , m_pauseTime(-1)
80     , m_requestedStartTime(0)
81     , m_totalDuration(-1)
82     , m_nextIterationDuration(-1)
83     , m_object(renderer)
84     , m_animation(const_cast<Animation*>(&transition))
85     , m_compositeAnimation(compositeAnimation)
86 {
87     // Compute the total duration
88     if (m_animation->iterationCount() > 0)
89         m_totalDuration = m_animation->duration() * m_animation->iterationCount();
90 }
91
92 void AnimationBase::setNeedsStyleRecalc(Element* element)
93 {
94     ASSERT(!element || !element->document().inPageCache());
95     if (element)
96         element->setNeedsStyleRecalc(SyntheticStyleChange);
97 }
98
99 double AnimationBase::duration() const
100 {
101     return m_animation->duration();
102 }
103
104 bool AnimationBase::playStatePlaying() const
105 {
106     return m_animation->playState() == AnimPlayStatePlaying;
107 }
108
109 bool AnimationBase::animationsMatch(const Animation* anim) const
110 {
111     return m_animation->animationsMatch(anim);
112 }
113
114 #if !LOG_DISABLED
115 static const char* nameForState(AnimationBase::AnimationState state)
116 {
117     switch (state) {
118     case AnimationBase::AnimationState::New: return "New";
119     case AnimationBase::AnimationState::StartWaitTimer: return "StartWaitTimer";
120     case AnimationBase::AnimationState::StartWaitStyleAvailable: return "StartWaitStyleAvailable";
121     case AnimationBase::AnimationState::StartWaitResponse: return "StartWaitResponse";
122     case AnimationBase::AnimationState::Looping: return "Looping";
123     case AnimationBase::AnimationState::Ending: return "Ending";
124     case AnimationBase::AnimationState::PausedNew: return "PausedNew";
125     case AnimationBase::AnimationState::PausedWaitTimer: return "PausedWaitTimer";
126     case AnimationBase::AnimationState::PausedWaitStyleAvailable: return "PausedWaitStyleAvailable";
127     case AnimationBase::AnimationState::PausedWaitResponse: return "PausedWaitResponse";
128     case AnimationBase::AnimationState::PausedRun: return "PausedRun";
129     case AnimationBase::AnimationState::Done: return "Done";
130     case AnimationBase::AnimationState::FillingForwards: return "FillingForwards";
131     }
132     return "";
133 }
134 #endif
135
136 void AnimationBase::updateStateMachine(AnimationStateInput input, double param)
137 {
138     if (!m_compositeAnimation)
139         return;
140
141     // If we get AnimationStateInput::RestartAnimation then we force a new animation, regardless of state.
142     if (input == AnimationStateInput::MakeNew) {
143         if (m_animationState == AnimationState::StartWaitStyleAvailable)
144             m_compositeAnimation->animationController()->removeFromAnimationsWaitingForStyle(this);
145         LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState));
146         m_animationState = AnimationState::New;
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 == AnimationStateInput::RestartAnimation) {
156         if (m_animationState == AnimationState::StartWaitStyleAvailable)
157             m_compositeAnimation->animationController()->removeFromAnimationsWaitingForStyle(this);
158         LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState));
159         m_animationState = AnimationState::New;
160         m_startTime = 0;
161         m_pauseTime = -1;
162         m_requestedStartTime = 0;
163         m_nextIterationDuration = -1;
164         endAnimation();
165
166         if (!paused())
167             updateStateMachine(AnimationStateInput::StartAnimation, -1);
168         return;
169     }
170
171     if (input == AnimationStateInput::EndAnimation) {
172         if (m_animationState == AnimationState::StartWaitStyleAvailable)
173             m_compositeAnimation->animationController()->removeFromAnimationsWaitingForStyle(this);
174         LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animationState));
175         m_animationState = AnimationState::Done;
176         endAnimation();
177         return;
178     }
179
180     if (input == AnimationStateInput::PauseOverride) {
181         if (m_animationState == AnimationState::StartWaitResponse) {
182             // If we are in AnimationState::StartWaitResponse, the animation will get canceled before 
183             // we get a response, so move to the next state.
184             endAnimation();
185             updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime());
186         }
187         return;
188     }
189
190     if (input == AnimationStateInput::ResumeOverride) {
191         if (m_animationState == AnimationState::Looping || m_animationState == AnimationState::Ending) {
192             // Start the animation
193             startAnimation(beginAnimationUpdateTime() - m_startTime);
194         }
195         return;
196     }
197
198     // Execute state machine
199     switch (m_animationState) {
200         case AnimationState::New:
201             ASSERT(input == AnimationStateInput::StartAnimation || input == AnimationStateInput::PlayStateRunning || input == AnimationStateInput::PlayStatePaused);
202
203             if (input == AnimationStateInput::StartAnimation || input == AnimationStateInput::PlayStateRunning) {
204                 m_requestedStartTime = beginAnimationUpdateTime();
205                 LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animationState));
206                 m_animationState = AnimationState::StartWaitTimer;
207             } else {
208                 // We are pausing before we even started.
209                 LOG(Animations, "%p AnimationState %s -> AnimationState::PausedNew", this, nameForState(m_animationState));
210                 m_animationState = AnimationState::PausedNew;
211             }
212             break;
213         case AnimationState::StartWaitTimer:
214             ASSERT(input == AnimationStateInput::StartTimerFired || input == AnimationStateInput::PlayStatePaused);
215
216             if (input == AnimationStateInput::StartTimerFired) {
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_animationState));
220                 m_animationState = AnimationState::StartWaitStyleAvailable;
221                 m_compositeAnimation->animationController()->addToAnimationsWaitingForStyle(this);
222
223                 // Trigger a render so we can start the animation
224                 if (m_object && m_object->element())
225                     m_compositeAnimation->animationController()->addElementChangeToDispatch(*m_object->element());
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_animationState));
231                 m_animationState = AnimationState::PausedWaitTimer;
232             }
233             break;
234         case AnimationState::StartWaitStyleAvailable:
235             ASSERT(input == AnimationStateInput::StyleAvailable || input == AnimationStateInput::PlayStatePaused);
236
237             if (input == AnimationStateInput::StyleAvailable) {
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_animationState));
240                 m_animationState = AnimationState::StartWaitResponse;
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_animationState));
249                     m_animationState = AnimationState::StartWaitResponse;
250                     m_isAccelerated = false;
251                     updateStateMachine(AnimationStateInput::StartTimeSet, 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_compositeAnimation->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_animationState));
266                 m_animationState = AnimationState::PausedWaitStyleAvailable;
267             }
268             break;
269         case AnimationState::StartWaitResponse:
270             ASSERT(input == AnimationStateInput::StartTimeSet || input == AnimationStateInput::PlayStatePaused);
271
272             if (input == AnimationStateInput::StartTimeSet) {
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 && m_object->element())
290                     m_compositeAnimation->animationController()->addElementChangeToDispatch(*m_object->element());
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_animationState));
297                 m_animationState = AnimationState::PausedWaitResponse;
298             }
299             break;
300         case AnimationState::Looping:
301             ASSERT(input == AnimationStateInput::LoopTimerFired || input == AnimationStateInput::PlayStatePaused);
302
303             if (input == AnimationStateInput::LoopTimerFired) {
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_animationState));
315                 m_animationState = AnimationState::PausedRun;
316             }
317             break;
318         case AnimationState::Ending:
319 #if !LOG_DISABLED
320             if (input != AnimationStateInput::EndTimerFired && input != AnimationStateInput::PlayStatePaused)
321                 LOG_ERROR("State is AnimationState::Ending, but input is not AnimationStateInput::EndTimerFired or AnimationStateInput::PlayStatePaused. It is %d.", input);
322 #endif
323             if (input == AnimationStateInput::EndTimerFired) {
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_animationState));
330                 m_animationState = AnimationState::Done;
331                 
332                 if (m_object) {
333                     if (m_animation->fillsForwards()) {
334                         LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animationState));
335                         m_animationState = AnimationState::FillingForwards;
336                     } else
337                         resumeOverriddenAnimations();
338
339                     // Fire off another style change so we can set the final value
340                     if (m_object->element())
341                         m_compositeAnimation->animationController()->addElementChangeToDispatch(*m_object->element());
342                 }
343             } else {
344                 // We are pausing while running. Cancel the animation and wait
345                 m_pauseTime = beginAnimationUpdateTime();
346                 pauseAnimation(beginAnimationUpdateTime() - m_startTime);
347                 LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animationState));
348                 m_animationState = AnimationState::PausedRun;
349             }
350             // |this| may be deleted here
351             break;
352         case AnimationState::PausedWaitTimer:
353             ASSERT(input == AnimationStateInput::PlayStateRunning);
354             ASSERT(paused());
355             // Update the times
356             m_startTime += beginAnimationUpdateTime() - m_pauseTime;
357             m_pauseTime = -1;
358
359             // we were waiting for the start timer to fire, go back and wait again
360             LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState));
361             m_animationState = AnimationState::New;
362             updateStateMachine(AnimationStateInput::StartAnimation, 0);
363             break;
364         case AnimationState::PausedNew:
365         case AnimationState::PausedWaitResponse:
366         case AnimationState::PausedWaitStyleAvailable:
367         case AnimationState::PausedRun:
368             // We treat these two cases the same. The only difference is that, when we are in
369             // AnimationState::PausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
370             // When the AnimationStateInput::StartTimeSet comes in and we were in AnimationState::PausedRun, we will notice
371             // that we have already set the startTime and will ignore it.
372             ASSERT(input == AnimationStateInput::PlayStateRunning || input == AnimationStateInput::StartTimeSet || input == AnimationStateInput::StyleAvailable || input == AnimationStateInput::StartAnimation);
373             ASSERT(paused());
374
375             if (input == AnimationStateInput::PlayStateRunning) {
376                 if (m_animationState == AnimationState::PausedNew) {
377                     // We were paused before we even started, and now we're supposed
378                     // to start, so jump back to the New state and reset.
379                     LOG(Animations, "%p AnimationState %s -> AnimationState::New", this, nameForState(m_animationState));
380                     m_animationState = AnimationState::New;
381                     updateStateMachine(input, param);
382                     break;
383                 }
384
385                 // Update the times
386                 if (m_animationState == AnimationState::PausedRun)
387                     m_startTime += beginAnimationUpdateTime() - m_pauseTime;
388                 else
389                     m_startTime = 0;
390                 m_pauseTime = -1;
391
392                 if (m_animationState == AnimationState::PausedWaitStyleAvailable) {
393                     LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animationState));
394                     m_animationState = AnimationState::StartWaitStyleAvailable;
395                 } else {
396                     // We were either running or waiting for a begin time response from the animation.
397                     // Either way we need to restart the animation (possibly with an offset if we
398                     // had already been running) and wait for it to start.
399                     LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState));
400                     m_animationState = AnimationState::StartWaitResponse;
401
402                     // Start the animation
403                     if (overridden()) {
404                         // We won't try to start accelerated animations if we are overridden and
405                         // just move on to the next state.
406                         updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime());
407                         m_isAccelerated = true;
408                     } else {
409                         bool started = startAnimation(beginAnimationUpdateTime() - m_startTime);
410                         m_compositeAnimation->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
411                         m_isAccelerated = started;
412                     }
413                 }
414                 break;
415             }
416             
417             if (input == AnimationStateInput::StartTimeSet) {
418                 ASSERT(m_animationState == AnimationState::PausedWaitResponse);
419                 
420                 // We are paused but we got the callback that notifies us that an accelerated animation started.
421                 // We ignore the start time and just move into the paused-run state.
422                 LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animationState));
423                 m_animationState = AnimationState::PausedRun;
424                 ASSERT(m_startTime == 0);
425                 m_startTime = param;
426                 m_pauseTime += m_startTime;
427                 break;
428             }
429
430             ASSERT(m_animationState == AnimationState::PausedWaitStyleAvailable);
431             // We are paused but we got the callback that notifies us that style has been updated.
432             // We move to the AnimationState::PausedWaitResponse state
433             LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animationState));
434             m_animationState = AnimationState::PausedWaitResponse;
435             overrideAnimations();
436             break;
437         case AnimationState::FillingForwards:
438         case AnimationState::Done:
439             // We're done. Stay in this state until we are deleted
440             break;
441     }
442 }
443     
444 void AnimationBase::fireAnimationEventsIfNeeded()
445 {
446     if (!m_compositeAnimation)
447         return;
448
449     // If we are waiting for the delay time to expire and it has, go to the next state
450     if (m_animationState != AnimationState::StartWaitTimer && m_animationState != AnimationState::Looping && m_animationState != AnimationState::Ending)
451         return;
452
453     // We have to make sure to keep a ref to the this pointer, because it could get destroyed
454     // during an animation callback that might get called. Since the owner is a CompositeAnimation
455     // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase
456     // can still access the resources of its CompositeAnimation as needed.
457     Ref<AnimationBase> protect(*this);
458     Ref<CompositeAnimation> protectCompositeAnimation(*m_compositeAnimation);
459     
460     // Check for start timeout
461     if (m_animationState == AnimationState::StartWaitTimer) {
462 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
463         if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) {
464             if (m_object) {
465                 float offset = m_compositeAnimation->animationController()->scrollPosition();
466                 ScrollAnimationTrigger& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger().get());
467                 if (offset > scrollTrigger.startValue().value())
468                     updateStateMachine(AnimationStateInput::StartTimerFired, 0);
469             }
470
471             return;
472         }
473 #endif
474         if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay())
475             updateStateMachine(AnimationStateInput::StartTimerFired, 0);
476         return;
477     }
478
479     double elapsedDuration = beginAnimationUpdateTime() - m_startTime;
480 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
481     // If we are a triggered animation that depends on scroll, our elapsed
482     // time is determined by the scroll position.
483     if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger())
484         elapsedDuration = getElapsedTime();
485 #endif
486
487     // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that
488     // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate().
489     // Also check in getTimeToNextEvent().
490     elapsedDuration = std::max(elapsedDuration, 0.0);
491     
492     // Check for end timeout
493     if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) {
494         // We may still be in AnimationState::Looping if we've managed to skip a
495         // whole iteration, in which case we should jump to the end state.
496         LOG(Animations, "%p AnimationState %s -> Ending", this, nameForState(m_animationState));
497         m_animationState = AnimationState::Ending;
498
499         // Fire an end event
500         updateStateMachine(AnimationStateInput::EndTimerFired, m_totalDuration);
501     } else {
502         // Check for iteration timeout
503         if (m_nextIterationDuration < 0) {
504             // Hasn't been set yet, set it
505             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
506             m_nextIterationDuration = elapsedDuration + durationLeft;
507         }
508         
509         if (elapsedDuration >= m_nextIterationDuration) {
510             // Set to the next iteration
511             double previous = m_nextIterationDuration;
512             double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration());
513             m_nextIterationDuration = elapsedDuration + durationLeft;
514             
515             // Send the event
516             updateStateMachine(AnimationStateInput::LoopTimerFired, previous);
517         }
518     }
519 }
520
521 void AnimationBase::updatePlayState(EAnimPlayState playState)
522 {
523     if (!m_compositeAnimation)
524         return;
525
526     // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended.
527     // The state machine can be in one of two states: running, paused.
528     // Set the state machine to the desired state.
529     bool pause = playState == AnimPlayStatePaused || m_compositeAnimation->isSuspended();
530
531     if (pause == paused() && !isNew())
532         return;
533
534     updateStateMachine(pause ?  AnimationStateInput::PlayStatePaused : AnimationStateInput::PlayStateRunning, -1);
535 }
536
537 double AnimationBase::timeToNextService()
538 {
539     // Returns the time at which next service is required. -1 means no service is required. 0 means 
540     // service is required now, and > 0 means service is required that many seconds in the future.
541     if (paused() || isNew() || m_animationState == AnimationState::FillingForwards)
542         return -1;
543     
544     if (m_animationState == AnimationState::StartWaitTimer) {
545 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
546         if (m_animation->trigger()->isScrollAnimationTrigger()) {
547             if (m_object) {
548                 float currentScrollOffset = m_object->view().frameView().scrollOffsetForFixedPosition().height().toFloat();
549                 ScrollAnimationTrigger& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger().get());
550                 if (currentScrollOffset >= scrollTrigger.startValue().value() && (!scrollTrigger.hasEndValue() || currentScrollOffset <= scrollTrigger.endValue().value()))
551                     return 0;
552             }
553             return -1;
554         }
555 #endif
556         double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime);
557         return std::max(timeFromNow, 0.0);
558     }
559     
560     fireAnimationEventsIfNeeded();
561         
562     // In all other cases, we need service right away.
563     return 0;
564 }
565
566 // Compute the fractional time, taking into account direction.
567 // There is no need to worry about iterations, we assume that we would have
568 // short circuited above if we were done.
569
570 double AnimationBase::fractionalTime(double scale, double elapsedTime, double offset) const
571 {
572     double fractionalTime = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1;
573     // FIXME: startTime can be before the current animation "frame" time. This is to sync with the frame time
574     // concept in AnimationTimeController. So we need to somehow sync the two. Until then, the possible
575     // error is small and will probably not be noticeable. Until we fix this, remove the assert.
576     // https://bugs.webkit.org/show_bug.cgi?id=52037
577     // ASSERT(fractionalTime >= 0);
578     if (fractionalTime < 0)
579         fractionalTime = 0;
580
581     int integralTime = static_cast<int>(fractionalTime);
582     const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
583     const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
584     if (m_animation->iterationCount() != Animation::IterationCountInfinite && !iterationCountHasFractional)
585         integralTime = std::min(integralTime, integralIterationCount - 1);
586
587     fractionalTime -= integralTime;
588
589     if (((m_animation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1))
590         || ((m_animation->direction() == Animation::AnimationDirectionAlternateReverse) && !(integralTime & 1))
591         || m_animation->direction() == Animation::AnimationDirectionReverse)
592         fractionalTime = 1 - fractionalTime;
593
594     if (scale != 1 || offset)
595         fractionalTime = (fractionalTime - offset) * scale;
596
597     return fractionalTime;
598 }
599
600 double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const
601 {
602     if (preActive())
603         return 0;
604
605     double elapsedTime = getElapsedTime();
606
607     double dur = m_animation->duration();
608     if (m_animation->iterationCount() > 0)
609         dur *= m_animation->iterationCount();
610
611     if (postActive() || !m_animation->duration())
612         return 1.0;
613
614     if (m_animation->iterationCount() > 0 && elapsedTime >= dur) {
615         const int integralIterationCount = static_cast<int>(m_animation->iterationCount());
616         const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount;
617         return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0;
618     }
619
620     const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset);
621
622     if (!tf)
623         tf = m_animation->timingFunction().get();
624
625     switch (tf->type()) {
626     case TimingFunction::CubicBezierFunction: {
627         const CubicBezierTimingFunction* function = static_cast<const CubicBezierTimingFunction*>(tf);
628         return solveCubicBezierFunction(function->x1(), function->y1(), function->x2(), function->y2(), fractionalTime, m_animation->duration());
629     }
630     case TimingFunction::StepsFunction: {
631         const StepsTimingFunction* stepsTimingFunction = static_cast<const StepsTimingFunction*>(tf);
632         return solveStepsFunction(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart(), fractionalTime);
633     }
634     case TimingFunction::LinearFunction:
635         return fractionalTime;
636     }
637
638     ASSERT_NOT_REACHED();
639     return 0;
640 }
641
642 void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const
643 {
644     // Decide when the end or loop event needs to fire
645     const double elapsedDuration = std::max(beginAnimationUpdateTime() - m_startTime, 0.0);
646     double durationLeft = 0;
647     double nextIterationTime = m_totalDuration;
648
649     if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) {
650         durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0;
651         nextIterationTime = elapsedDuration + durationLeft;
652     }
653     
654     if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) {
655         // We are not at the end yet
656         ASSERT(nextIterationTime > 0);
657         isLooping = true;
658     } else {
659         // We are at the end
660         isLooping = false;
661     }
662     
663     time = durationLeft;
664 }
665
666 void AnimationBase::goIntoEndingOrLoopingState()
667 {
668     double t;
669     bool isLooping;
670     getTimeToNextEvent(t, isLooping);
671     LOG(Animations, "%p AnimationState %s -> %s", this, nameForState(m_animationState), isLooping ? "Looping" : "Ending");
672     m_animationState = isLooping ? AnimationState::Looping : AnimationState::Ending;
673 }
674   
675 void AnimationBase::freezeAtTime(double t)
676 {
677     if (!m_compositeAnimation)
678         return;
679
680     if (!m_startTime) {
681         // If we haven't started yet, make it as if we started.
682         LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState));
683         m_animationState = AnimationState::StartWaitResponse;
684         onAnimationStartResponse(monotonicallyIncreasingTime());
685     }
686
687     ASSERT(m_startTime); // If m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
688     if (t <= m_animation->delay())
689         m_pauseTime = m_startTime;
690     else
691         m_pauseTime = m_startTime + t - m_animation->delay();
692
693     if (m_object && m_object->isComposited())
694         downcast<RenderBoxModelObject>(*m_object).suspendAnimations(m_pauseTime);
695 }
696
697 double AnimationBase::beginAnimationUpdateTime() const
698 {
699     if (!m_compositeAnimation)
700         return 0;
701
702     return m_compositeAnimation->animationController()->beginAnimationUpdateTime();
703 }
704
705 double AnimationBase::getElapsedTime() const
706 {
707 #if ENABLE(CSS_ANIMATIONS_LEVEL_2)
708     if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) {
709         ScrollAnimationTrigger& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger().get());
710         if (scrollTrigger.hasEndValue() && m_object) {
711             float offset = m_compositeAnimation->animationController()->scrollPosition();
712             float startValue = scrollTrigger.startValue().value();
713             if (offset < startValue)
714                 return 0;
715             float endValue = scrollTrigger.endValue().value();
716             if (offset > endValue)
717                 return m_animation->duration();
718             return m_animation->duration() * (offset - startValue) / (endValue - startValue);
719         }
720     }
721 #endif
722
723     if (paused())
724         return m_pauseTime - m_startTime;
725     if (m_startTime <= 0)
726         return 0;
727     if (postActive())
728         return 1;
729
730     return beginAnimationUpdateTime() - m_startTime;
731 }
732
733 void AnimationBase::setElapsedTime(double time)
734 {
735     // FIXME: implement this method
736     UNUSED_PARAM(time);
737 }
738
739 void AnimationBase::play()
740 {
741     // FIXME: implement this method
742 }
743
744 void AnimationBase::pause()
745 {
746     // FIXME: implement this method
747 }
748
749 static bool containsRotation(const Vector<RefPtr<TransformOperation>>& operations)
750 {
751     for (const auto& operation : operations) {
752         if (operation->type() == TransformOperation::ROTATE)
753             return true;
754     }
755     return false;
756 }
757
758 bool AnimationBase::computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const
759 {
760     FloatRect floatBounds = bounds;
761     FloatPoint transformOrigin;
762     
763     bool applyTransformOrigin = containsRotation(style.transform().operations()) || style.transform().affectedByTransformOrigin();
764     if (applyTransformOrigin) {
765         float offsetX = style.transformOriginX().isPercentNotCalculated() ? rendererBox.x() : 0;
766         float offsetY = style.transformOriginY().isPercentNotCalculated() ? rendererBox.y() : 0;
767
768         transformOrigin.setX(floatValueForLength(style.transformOriginX(), rendererBox.width()) + offsetX);
769         transformOrigin.setY(floatValueForLength(style.transformOriginY(), rendererBox.height()) + offsetY);
770         // Ignore transformOriginZ because we'll bail if we encounter any 3D transforms.
771         
772         floatBounds.moveBy(-transformOrigin);
773     }
774
775     for (const auto& operation : style.transform().operations()) {
776         if (operation->type() == TransformOperation::ROTATE) {
777             // For now, just treat this as a full rotation. This could take angle into account to reduce inflation.
778             floatBounds = boundsOfRotatingRect(floatBounds);
779         } else {
780             TransformationMatrix transform;
781             operation->apply(transform, rendererBox.size());
782             if (!transform.isAffine())
783                 return false;
784
785             if (operation->type() == TransformOperation::MATRIX || operation->type() == TransformOperation::MATRIX_3D) {
786                 TransformationMatrix::Decomposed2Type toDecomp;
787                 transform.decompose2(toDecomp);
788                 // Any rotation prevents us from using a simple start/end rect union.
789                 if (toDecomp.angle)
790                     return false;
791             }
792
793             floatBounds = transform.mapRect(floatBounds);
794         }
795     }
796
797     if (applyTransformOrigin)
798         floatBounds.moveBy(transformOrigin);
799
800     bounds = LayoutRect(floatBounds);
801     return true;
802 }
803
804 bool AnimationBase::computeTransformedExtentViaMatrix(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const
805 {
806     TransformationMatrix transform;
807     style.applyTransform(transform, rendererBox, RenderStyle::IncludeTransformOrigin);
808     if (!transform.isAffine())
809         return false;
810
811     TransformationMatrix::Decomposed2Type fromDecomp;
812     transform.decompose2(fromDecomp);
813     // Any rotation prevents us from using a simple start/end rect union.
814     if (fromDecomp.angle)
815         return false;
816
817     bounds = LayoutRect(transform.mapRect(bounds));
818     return true;
819
820 }
821
822 } // namespace WebCore