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