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