diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/page/animation/AnimationBase.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/page/animation/AnimationBase.cpp')
-rw-r--r-- | Source/WebCore/page/animation/AnimationBase.cpp | 618 |
1 files changed, 388 insertions, 230 deletions
diff --git a/Source/WebCore/page/animation/AnimationBase.cpp b/Source/WebCore/page/animation/AnimationBase.cpp index dd3315ebb..44bfe82a3 100644 --- a/Source/WebCore/page/animation/AnimationBase.cpp +++ b/Source/WebCore/page/animation/AnimationBase.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -29,16 +29,18 @@ #include "config.h" #include "AnimationBase.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPrimitiveValue.h" #include "CSSPropertyAnimation.h" #include "CompositeAnimation.h" #include "Document.h" -#include "EventNames.h" #include "FloatConversion.h" +#include "GeometryUtilities.h" #include "Logging.h" #include "RenderBox.h" #include "RenderStyle.h" +#include "RenderView.h" +#include "SpringSolver.h" #include "UnitBezier.h" #include <algorithm> #include <wtf/CurrentTime.h> @@ -68,21 +70,16 @@ static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t return floor(numSteps * t) / numSteps; } -AnimationBase::AnimationBase(const Animation& transition, RenderElement* renderer, CompositeAnimation* compAnim) - : m_animState(AnimationStateNew) - , m_isAccelerated(false) - , m_transformFunctionListValid(false) -#if ENABLE(CSS_FILTERS) - , m_filterFunctionListsMatch(false) -#endif - , m_startTime(0) - , m_pauseTime(-1) - , m_requestedStartTime(0) - , m_totalDuration(-1) - , m_nextIterationDuration(-1) - , m_object(renderer) - , m_animation(const_cast<Animation*>(&transition)) - , m_compAnim(compAnim) +static inline double solveSpringFunction(double mass, double stiffness, double damping, double initialVelocity, double t, double duration) +{ + SpringSolver solver(mass, stiffness, damping, initialVelocity); + return solver.solve(t * duration); +} + +AnimationBase::AnimationBase(const Animation& animation, RenderElement* renderer, CompositeAnimation* compositeAnimation) + : m_object(renderer) + , m_compositeAnimation(compositeAnimation) + , m_animation(const_cast<Animation&>(animation)) { // Compute the total duration if (m_animation->iterationCount() > 0) @@ -91,9 +88,11 @@ AnimationBase::AnimationBase(const Animation& transition, RenderElement* rendere void AnimationBase::setNeedsStyleRecalc(Element* element) { - ASSERT(!element || !element->document().inPageCache()); - if (element) - element->setNeedsStyleRecalc(SyntheticStyleChange); + if (!element || element->document().renderTreeBeingDestroyed()) + return; + + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); } double AnimationBase::duration() const @@ -106,137 +105,164 @@ bool AnimationBase::playStatePlaying() const return m_animation->playState() == AnimPlayStatePlaying; } -bool AnimationBase::animationsMatch(const Animation* anim) const +bool AnimationBase::animationsMatch(const Animation& animation) const { - return m_animation->animationsMatch(anim); + return m_animation->animationsMatch(animation); } #if !LOG_DISABLED -static const char* nameForState(AnimationBase::AnimState state) +static const char* nameForState(AnimationBase::AnimationState state) { switch (state) { - case AnimationBase::AnimationStateNew: return "New"; - case AnimationBase::AnimationStateStartWaitTimer: return "StartWaitTimer"; - case AnimationBase::AnimationStateStartWaitStyleAvailable: return "StartWaitStyleAvailable"; - case AnimationBase::AnimationStateStartWaitResponse: return "StartWaitResponse"; - case AnimationBase::AnimationStateLooping: return "Looping"; - case AnimationBase::AnimationStateEnding: return "Ending"; - case AnimationBase::AnimationStatePausedNew: return "PausedNew"; - case AnimationBase::AnimationStatePausedWaitTimer: return "PausedWaitTimer"; - case AnimationBase::AnimationStatePausedWaitStyleAvailable: return "PausedWaitStyleAvailable"; - case AnimationBase::AnimationStatePausedWaitResponse: return "PausedWaitResponse"; - case AnimationBase::AnimationStatePausedRun: return "PausedRun"; - case AnimationBase::AnimationStateDone: return "Done"; - case AnimationBase::AnimationStateFillingForwards: return "FillingForwards"; + case AnimationBase::AnimationState::New: return "New"; + case AnimationBase::AnimationState::StartWaitTimer: return "StartWaitTimer"; + case AnimationBase::AnimationState::StartWaitStyleAvailable: return "StartWaitStyleAvailable"; + case AnimationBase::AnimationState::StartWaitResponse: return "StartWaitResponse"; + case AnimationBase::AnimationState::Looping: return "Looping"; + case AnimationBase::AnimationState::Ending: return "Ending"; + case AnimationBase::AnimationState::PausedNew: return "PausedNew"; + case AnimationBase::AnimationState::PausedWaitTimer: return "PausedWaitTimer"; + case AnimationBase::AnimationState::PausedWaitStyleAvailable: return "PausedWaitStyleAvailable"; + case AnimationBase::AnimationState::PausedWaitResponse: return "PausedWaitResponse"; + case AnimationBase::AnimationState::PausedRun: return "PausedRun"; + case AnimationBase::AnimationState::Done: return "Done"; + case AnimationBase::AnimationState::FillingForwards: return "FillingForwards"; + } + return ""; +} + +static const char* nameForStateInput(AnimationBase::AnimationStateInput input) +{ + switch (input) { + case AnimationBase::AnimationStateInput::MakeNew: return "MakeNew"; + case AnimationBase::AnimationStateInput::StartAnimation: return "StartAnimation"; + case AnimationBase::AnimationStateInput::RestartAnimation: return "RestartAnimation"; + case AnimationBase::AnimationStateInput::StartTimerFired: return "StartTimerFired"; + case AnimationBase::AnimationStateInput::StyleAvailable: return "StyleAvailable"; + case AnimationBase::AnimationStateInput::StartTimeSet: return "StartTimeSet"; + case AnimationBase::AnimationStateInput::LoopTimerFired: return "LoopTimerFired"; + case AnimationBase::AnimationStateInput::EndTimerFired: return "EndTimerFired"; + case AnimationBase::AnimationStateInput::PauseOverride: return "PauseOverride"; + case AnimationBase::AnimationStateInput::ResumeOverride: return "ResumeOverride"; + case AnimationBase::AnimationStateInput::PlayStateRunning: return "PlayStateRunning"; + case AnimationBase::AnimationStateInput::PlayStatePaused: return "PlayStatePaused"; + case AnimationBase::AnimationStateInput::EndAnimation: return "EndAnimation"; } return ""; } #endif -void AnimationBase::updateStateMachine(AnimStateInput input, double param) +void AnimationBase::updateStateMachine(AnimationStateInput input, double param) { - if (!m_compAnim) + if (!m_compositeAnimation) return; - // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state. - if (input == AnimationStateInputMakeNew) { - if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); - LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); - m_animState = AnimationStateNew; - m_startTime = 0; - m_pauseTime = -1; + // If we get AnimationStateInput::RestartAnimation then we force a new animation, regardless of state. + if (input == AnimationStateInput::MakeNew) { + if (m_animationState == AnimationState::StartWaitStyleAvailable) + m_compositeAnimation->animationController().removeFromAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + m_startTime = std::nullopt; + m_pauseTime = std::nullopt; m_requestedStartTime = 0; - m_nextIterationDuration = -1; + m_nextIterationDuration = std::nullopt; endAnimation(); return; } - if (input == AnimationStateInputRestartAnimation) { - if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); - LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); - m_animState = AnimationStateNew; - m_startTime = 0; - m_pauseTime = -1; + if (input == AnimationStateInput::RestartAnimation) { + if (m_animationState == AnimationState::StartWaitStyleAvailable) + m_compositeAnimation->animationController().removeFromAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + m_startTime = std::nullopt; + m_pauseTime = std::nullopt; m_requestedStartTime = 0; - m_nextIterationDuration = -1; + m_nextIterationDuration = std::nullopt; endAnimation(); if (!paused()) - updateStateMachine(AnimationStateInputStartAnimation, -1); + updateStateMachine(AnimationStateInput::StartAnimation, -1); return; } - if (input == AnimationStateInputEndAnimation) { - if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); - LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState)); - m_animState = AnimationStateDone; + if (input == AnimationStateInput::EndAnimation) { + if (m_animationState == AnimationState::StartWaitStyleAvailable) + m_compositeAnimation->animationController().removeFromAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animationState)); + m_animationState = AnimationState::Done; endAnimation(); return; } - if (input == AnimationStateInputPauseOverride) { - if (m_animState == AnimationStateStartWaitResponse) { - // If we are in AnimationStateStartWaitResponse, the animation will get canceled before + if (input == AnimationStateInput::PauseOverride) { + if (m_animationState == AnimationState::StartWaitResponse) { + // If we are in AnimationState::StartWaitResponse, the animation will get canceled before // we get a response, so move to the next state. endAnimation(); - updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime()); } return; } - if (input == AnimationStateInputResumeOverride) { - if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) { + if (input == AnimationStateInput::ResumeOverride) { + if (m_animationState == AnimationState::Looping || m_animationState == AnimationState::Ending) { // Start the animation - startAnimation(beginAnimationUpdateTime() - m_startTime); + startAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); } return; } // Execute state machine - switch (m_animState) { - case AnimationStateNew: - ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused); - if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) { + switch (m_animationState) { + case AnimationState::New: + ASSERT(input == AnimationStateInput::StartAnimation || input == AnimationStateInput::PlayStateRunning || input == AnimationStateInput::PlayStatePaused); + + if (input == AnimationStateInput::StartAnimation || input == AnimationStateInput::PlayStateRunning) { m_requestedStartTime = beginAnimationUpdateTime(); - LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitTimer; + LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitTimer; } else { // We are pausing before we even started. - LOG(Animations, "%p AnimationState %s -> AnimationStatePausedNew", this, nameForState(m_animState)); - m_animState = AnimationStatePausedNew; + LOG(Animations, "%p AnimationState %s -> AnimationState::PausedNew", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedNew; + m_pauseTime = std::nullopt; } + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) + m_compositeAnimation->animationController().addToAnimationsDependentOnScroll(this); +#endif break; - case AnimationStateStartWaitTimer: - ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused); + case AnimationState::StartWaitTimer: + ASSERT(input == AnimationStateInput::StartTimerFired || input == AnimationStateInput::PlayStatePaused); - if (input == AnimationStateInputStartTimerFired) { + if (input == AnimationStateInput::StartTimerFired) { ASSERT(param >= 0); // Start timer has fired, tell the animation to start and wait for it to respond with start time - LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitStyleAvailable; - m_compAnim->animationController()->addToAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::StartWaitStyleAvailable; + m_compositeAnimation->animationController().addToAnimationsWaitingForStyle(this); // Trigger a render so we can start the animation if (m_object && m_object->element()) - m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element()); + m_compositeAnimation->animationController().addElementChangeToDispatch(*m_object->element()); } else { ASSERT(!paused()); // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait m_pauseTime = beginAnimationUpdateTime(); - LOG(Animations, "%p AnimationState %s -> PausedWaitTimer", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitTimer; + LOG(Animations, "%p AnimationState %s -> PausedWaitTimer", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitTimer; } break; - case AnimationStateStartWaitStyleAvailable: - ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused); + case AnimationState::StartWaitStyleAvailable: + ASSERT(input == AnimationStateInput::StyleAvailable || input == AnimationStateInput::PlayStatePaused); - if (input == AnimationStateInputStyleAvailable) { + if (input == AnimationStateInput::StyleAvailable) { // Start timer has fired, tell the animation to start and wait for it to respond with start time - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::StartWaitResponse; overrideAnimations(); @@ -244,10 +270,10 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) if (overridden()) { // We won't try to start accelerated animations if we are overridden and // just move on to the next state. - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitResponse; m_isAccelerated = false; - updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime()); } else { double timeOffset = 0; // If the value for 'animation-delay' is negative then the animation appears to have started in the past. @@ -255,27 +281,29 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) timeOffset = -m_animation->delay(); bool started = startAnimation(timeOffset); - m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); + m_compositeAnimation->animationController().addToAnimationsWaitingForStartTimeResponse(this, started); m_isAccelerated = started; } } else { // We're waiting for the style to be available and we got a pause. Pause and wait m_pauseTime = beginAnimationUpdateTime(); - LOG(Animations, "%p AnimationState %s -> PausedWaitStyleAvailable", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitStyleAvailable; + LOG(Animations, "%p AnimationState %s -> PausedWaitStyleAvailable", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitStyleAvailable; } break; - case AnimationStateStartWaitResponse: - ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused); + case AnimationState::StartWaitResponse: + ASSERT(input == AnimationStateInput::StartTimeSet || input == AnimationStateInput::PlayStatePaused); + + if (input == AnimationStateInput::StartTimeSet) { + ASSERT(param > -0.001); // Sometimes Core Animation gives us a beginTime slightly into the future. + LOG(Animations, "%p AnimationState %s -> StartTimeSet (time is %f)", this, nameForState(m_animationState), param); - if (input == AnimationStateInputStartTimeSet) { - ASSERT(param >= 0); // We have a start time, set it, unless the startTime is already set - if (m_startTime <= 0) { + if (!m_startTime) { m_startTime = param; // If the value for 'animation-delay' is negative then the animation appears to have started in the past. if (m_animation->delay() < 0) - m_startTime += m_animation->delay(); + m_startTime = m_startTime.value() + m_animation->delay(); } // Now that we know the start time, fire the start event. @@ -286,21 +314,23 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // Dispatch updateStyleIfNeeded so we can start the animation if (m_object && m_object->element()) - m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element()); + m_compositeAnimation->animationController().addElementChangeToDispatch(*m_object->element()); } else { // We are pausing while waiting for a start response. Cancel the animation and wait. When // we unpause, we will act as though the start timer just fired m_pauseTime = beginAnimationUpdateTime(); - pauseAnimation(beginAnimationUpdateTime() - m_startTime); - LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitResponse; + pauseAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitResponse; } break; - case AnimationStateLooping: - ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused); + case AnimationState::Looping: + ASSERT(input == AnimationStateInput::LoopTimerFired || input == AnimationStateInput::PlayStatePaused); - if (input == AnimationStateInputLoopTimerFired) { + if (input == AnimationStateInput::LoopTimerFired) { ASSERT(param >= 0); + LOG(Animations, "%p AnimationState %s -> LoopTimerFired (time is %f)", this, nameForState(m_animationState), param); + // Loop timer fired, loop again or end. onAnimationIteration(param); @@ -309,178 +339,198 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) } else { // We are pausing while running. Cancel the animation and wait m_pauseTime = beginAnimationUpdateTime(); - pauseAnimation(beginAnimationUpdateTime() - m_startTime); - LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); - m_animState = AnimationStatePausedRun; + pauseAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedRun; } break; - case AnimationStateEnding: + case AnimationState::Ending: #if !LOG_DISABLED - if (input != AnimationStateInputEndTimerFired && input != AnimationStateInputPlayStatePaused) - LOG_ERROR("State is AnimationStateEnding, but input is not AnimationStateInputEndTimerFired or AnimationStateInputPlayStatePaused. It is %d.", input); + if (input != AnimationStateInput::EndTimerFired && input != AnimationStateInput::PlayStatePaused) + LOG_ERROR("State is AnimationState::Ending, but input is not AnimationStateInput::EndTimerFired or AnimationStateInput::PlayStatePaused. It is %s.", nameForStateInput(input)); #endif - if (input == AnimationStateInputEndTimerFired) { - + if (input == AnimationStateInput::EndTimerFired) { ASSERT(param >= 0); // End timer fired, finish up onAnimationEnd(param); - LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState)); - m_animState = AnimationStateDone; + LOG(Animations, "%p AnimationState %s -> Done (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::Done; if (m_object) { if (m_animation->fillsForwards()) { - LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animState)); - m_animState = AnimationStateFillingForwards; + LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animationState)); + m_animationState = AnimationState::FillingForwards; } else resumeOverriddenAnimations(); // Fire off another style change so we can set the final value if (m_object->element()) - m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element()); + m_compositeAnimation->animationController().addElementChangeToDispatch(*m_object->element()); } } else { // We are pausing while running. Cancel the animation and wait m_pauseTime = beginAnimationUpdateTime(); - pauseAnimation(beginAnimationUpdateTime() - m_startTime); - LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); - m_animState = AnimationStatePausedRun; + pauseAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedRun; } // |this| may be deleted here break; - case AnimationStatePausedWaitTimer: - ASSERT(input == AnimationStateInputPlayStateRunning); + case AnimationState::PausedWaitTimer: + ASSERT(input == AnimationStateInput::PlayStateRunning); ASSERT(paused()); // Update the times - m_startTime += beginAnimationUpdateTime() - m_pauseTime; - m_pauseTime = -1; + m_startTime = m_startTime.value() + beginAnimationUpdateTime() - m_pauseTime.value_or(0); + m_pauseTime = std::nullopt; // we were waiting for the start timer to fire, go back and wait again - LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); - m_animState = AnimationStateNew; - updateStateMachine(AnimationStateInputStartAnimation, 0); + LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + updateStateMachine(AnimationStateInput::StartAnimation, 0); break; - case AnimationStatePausedNew: - case AnimationStatePausedWaitResponse: - case AnimationStatePausedWaitStyleAvailable: - case AnimationStatePausedRun: + case AnimationState::PausedNew: + case AnimationState::PausedWaitResponse: + case AnimationState::PausedWaitStyleAvailable: + case AnimationState::PausedRun: // We treat these two cases the same. The only difference is that, when we are in - // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. - // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice + // AnimationState::PausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. + // When the AnimationStateInput::StartTimeSet comes in and we were in AnimationState::PausedRun, we will notice // that we have already set the startTime and will ignore it. - ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable || input == AnimationStateInputStartAnimation); + ASSERT(input == AnimationStateInput::PlayStatePaused || input == AnimationStateInput::PlayStateRunning || input == AnimationStateInput::StartTimeSet || input == AnimationStateInput::StyleAvailable || input == AnimationStateInput::StartAnimation); ASSERT(paused()); - if (input == AnimationStateInputPlayStateRunning) { - if (m_animState == AnimationStatePausedNew) { + if (input == AnimationStateInput::PlayStateRunning) { + if (m_animationState == AnimationState::PausedNew) { // We were paused before we even started, and now we're supposed // to start, so jump back to the New state and reset. - LOG(Animations, "%p AnimationState %s -> AnimationStateNew", this, nameForState(m_animState)); - m_animState = AnimationStateNew; + LOG(Animations, "%p AnimationState %s -> AnimationState::New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + m_pauseTime = std::nullopt; updateStateMachine(input, param); break; } // Update the times - if (m_animState == AnimationStatePausedRun) - m_startTime += beginAnimationUpdateTime() - m_pauseTime; + if (m_animationState == AnimationState::PausedRun) + m_startTime = m_startTime.value() + beginAnimationUpdateTime() - m_pauseTime.value_or(0); else m_startTime = 0; - m_pauseTime = -1; - if (m_animState == AnimationStatePausedWaitStyleAvailable) { - LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitStyleAvailable; + m_pauseTime = std::nullopt; + + if (m_animationState == AnimationState::PausedWaitStyleAvailable) { + LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitStyleAvailable; } else { // We were either running or waiting for a begin time response from the animation. // Either way we need to restart the animation (possibly with an offset if we // had already been running) and wait for it to start. - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitResponse; // Start the animation if (overridden()) { // We won't try to start accelerated animations if we are overridden and // just move on to the next state. - updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime()); m_isAccelerated = true; } else { - bool started = startAnimation(beginAnimationUpdateTime() - m_startTime); - m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); + bool started = startAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + m_compositeAnimation->animationController().addToAnimationsWaitingForStartTimeResponse(this, started); m_isAccelerated = started; } } break; } - if (input == AnimationStateInputStartTimeSet) { - ASSERT(m_animState == AnimationStatePausedWaitResponse); + if (input == AnimationStateInput::StartTimeSet) { + ASSERT(m_animationState == AnimationState::PausedWaitResponse); // We are paused but we got the callback that notifies us that an accelerated animation started. // We ignore the start time and just move into the paused-run state. - LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); - m_animState = AnimationStatePausedRun; - ASSERT(m_startTime == 0); + LOG(Animations, "%p AnimationState %s -> PausedRun (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::PausedRun; + ASSERT(!m_startTime); m_startTime = param; - m_pauseTime += m_startTime; + m_pauseTime = m_pauseTime.value_or(0) + param; break; } - ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable); + ASSERT(m_animationState == AnimationState::PausedNew || m_animationState == AnimationState::PausedWaitStyleAvailable); // We are paused but we got the callback that notifies us that style has been updated. - // We move to the AnimationStatePausedWaitResponse state - LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitResponse; + // We move to the AnimationState::PausedWaitResponse state + LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitResponse; overrideAnimations(); break; - case AnimationStateFillingForwards: - case AnimationStateDone: + case AnimationState::FillingForwards: + case AnimationState::Done: // We're done. Stay in this state until we are deleted break; } } - + void AnimationBase::fireAnimationEventsIfNeeded() { - if (!m_compAnim) + if (!m_compositeAnimation) return; // If we are waiting for the delay time to expire and it has, go to the next state - if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding) + if (m_animationState != AnimationState::StartWaitTimer && m_animationState != AnimationState::Looping && m_animationState != AnimationState::Ending) return; // We have to make sure to keep a ref to the this pointer, because it could get destroyed // during an animation callback that might get called. Since the owner is a CompositeAnimation // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase // can still access the resources of its CompositeAnimation as needed. - Ref<AnimationBase> protect(*this); - Ref<CompositeAnimation> protectCompositeAnimation(*m_compAnim); + Ref<AnimationBase> protectedThis(*this); + Ref<CompositeAnimation> protectCompositeAnimation(*m_compositeAnimation); // Check for start timeout - if (m_animState == AnimationStateStartWaitTimer) { + if (m_animationState == AnimationState::StartWaitTimer) { +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) { + if (m_object) { + float offset = m_compositeAnimation->animationController().scrollPosition(); + auto& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger()); + if (offset > scrollTrigger.startValue().value()) + updateStateMachine(AnimationStateInput::StartTimerFired, 0); + } + + return; + } +#endif if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay()) - updateStateMachine(AnimationStateInputStartTimerFired, 0); + updateStateMachine(AnimationStateInput::StartTimerFired, 0); return; } - - double elapsedDuration = beginAnimationUpdateTime() - m_startTime; + + double elapsedDuration = beginAnimationUpdateTime() - m_startTime.value_or(0); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + // If we are a triggered animation that depends on scroll, our elapsed + // time is determined by the scroll position. + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) + elapsedDuration = getElapsedTime(); +#endif + // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate(). // Also check in getTimeToNextEvent(). elapsedDuration = std::max(elapsedDuration, 0.0); // Check for end timeout - if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) { - // We may still be in AnimationStateLooping if we've managed to skip a + if (m_totalDuration && elapsedDuration >= m_totalDuration.value()) { + // We may still be in AnimationState::Looping if we've managed to skip a // whole iteration, in which case we should jump to the end state. - LOG(Animations, "%p AnimationState %s -> Ending", this, nameForState(m_animState)); - m_animState = AnimationStateEnding; + LOG(Animations, "%p AnimationState %s -> Ending", this, nameForState(m_animationState)); + m_animationState = AnimationState::Ending; // Fire an end event - updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration); + updateStateMachine(AnimationStateInput::EndTimerFired, m_totalDuration.value()); } else { // Check for iteration timeout - if (m_nextIterationDuration < 0) { + if (!m_nextIterationDuration) { // Hasn't been set yet, set it double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); m_nextIterationDuration = elapsedDuration + durationLeft; @@ -488,40 +538,51 @@ void AnimationBase::fireAnimationEventsIfNeeded() if (elapsedDuration >= m_nextIterationDuration) { // Set to the next iteration - double previous = m_nextIterationDuration; + double previous = m_nextIterationDuration.value(); double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); m_nextIterationDuration = elapsedDuration + durationLeft; // Send the event - updateStateMachine(AnimationStateInputLoopTimerFired, previous); + updateStateMachine(AnimationStateInput::LoopTimerFired, previous); } } } void AnimationBase::updatePlayState(EAnimPlayState playState) { - if (!m_compAnim) + if (!m_compositeAnimation) return; // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended. // The state machine can be in one of two states: running, paused. // Set the state machine to the desired state. - bool pause = playState == AnimPlayStatePaused || m_compAnim->isSuspended(); + bool pause = playState == AnimPlayStatePaused || m_compositeAnimation->isSuspended(); if (pause == paused() && !isNew()) return; - updateStateMachine(pause ? AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1); + updateStateMachine(pause ? AnimationStateInput::PlayStatePaused : AnimationStateInput::PlayStateRunning, -1); } double AnimationBase::timeToNextService() { // Returns the time at which next service is required. -1 means no service is required. 0 means // service is required now, and > 0 means service is required that many seconds in the future. - if (paused() || isNew() || m_animState == AnimationStateFillingForwards) + if (paused() || isNew() || postActive() || fillingForwards()) return -1; - if (m_animState == AnimationStateStartWaitTimer) { + if (m_animationState == AnimationState::StartWaitTimer) { +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger()->isScrollAnimationTrigger()) { + if (m_object) { + float currentScrollPosition = m_object->view().frameView().scrollPositionForFixedPosition().y().toFloat(); + auto& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger()); + if (currentScrollPosition >= scrollTrigger.startValue().value() && (!scrollTrigger.hasEndValue() || currentScrollPosition <= scrollTrigger.endValue().value())) + return 0; + } + return -1; + } +#endif double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime); return std::max(timeFromNow, 0.0); } @@ -566,39 +627,45 @@ double AnimationBase::fractionalTime(double scale, double elapsedTime, double of return fractionalTime; } -double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const +double AnimationBase::progress(double scale, double offset, const TimingFunction* timingFunction) const { if (preActive()) return 0; + if (postActive()) + return 1; + double elapsedTime = getElapsedTime(); - double dur = m_animation->duration(); + double duration = m_animation->duration(); if (m_animation->iterationCount() > 0) - dur *= m_animation->iterationCount(); + duration *= m_animation->iterationCount(); - if (postActive() || !m_animation->duration()) - return 1.0; + if (fillingForwards()) + elapsedTime = duration; - if (m_animation->iterationCount() > 0 && elapsedTime >= dur) { - const int integralIterationCount = static_cast<int>(m_animation->iterationCount()); - const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount; - return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0; - } + double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); - const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); + if (m_animation->iterationCount() > 0 && elapsedTime >= duration) { + if (WTF::isIntegral(fractionalTime)) + return fractionalTime; + } - if (!tf) - tf = m_animation->timingFunction().get(); + if (!timingFunction) + timingFunction = m_animation->timingFunction(); - switch (tf->type()) { + switch (timingFunction->type()) { case TimingFunction::CubicBezierFunction: { - const CubicBezierTimingFunction* function = static_cast<const CubicBezierTimingFunction*>(tf); - return solveCubicBezierFunction(function->x1(), function->y1(), function->x2(), function->y2(), fractionalTime, m_animation->duration()); + auto& function = *static_cast<const CubicBezierTimingFunction*>(timingFunction); + return solveCubicBezierFunction(function.x1(), function.y1(), function.x2(), function.y2(), fractionalTime, m_animation->duration()); } case TimingFunction::StepsFunction: { - const StepsTimingFunction* stepsTimingFunction = static_cast<const StepsTimingFunction*>(tf); - return solveStepsFunction(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart(), fractionalTime); + auto& function = *static_cast<const StepsTimingFunction*>(timingFunction); + return solveStepsFunction(function.numberOfSteps(), function.stepAtStart(), fractionalTime); + } + case TimingFunction::SpringFunction: { + auto& function = *static_cast<const SpringTimingFunction*>(timingFunction); + return solveSpringFunction(function.mass(), function.stiffness(), function.damping(), function.initialVelocity(), fractionalTime, m_animation->duration()); } case TimingFunction::LinearFunction: return fractionalTime; @@ -611,16 +678,16 @@ double AnimationBase::progress(double scale, double offset, const TimingFunction void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const { // Decide when the end or loop event needs to fire - const double elapsedDuration = std::max(beginAnimationUpdateTime() - m_startTime, 0.0); + const double elapsedDuration = std::max(beginAnimationUpdateTime() - m_startTime.value_or(0), 0.0); double durationLeft = 0; - double nextIterationTime = m_totalDuration; + double nextIterationTime = m_totalDuration.value_or(0); - if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) { + if (!m_totalDuration || elapsedDuration < m_totalDuration.value()) { durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0; nextIterationTime = elapsedDuration + durationLeft; } - if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) { + if (!m_totalDuration || nextIterationTime < m_totalDuration.value()) { // We are not at the end yet ASSERT(nextIterationTime > 0); isLooping = true; @@ -637,52 +704,70 @@ void AnimationBase::goIntoEndingOrLoopingState() double t; bool isLooping; getTimeToNextEvent(t, isLooping); - LOG(Animations, "%p AnimationState %s -> %s", this, nameForState(m_animState), isLooping ? "Looping" : "Ending"); - m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding; + LOG(Animations, "%p AnimationState %s -> %s", this, nameForState(m_animationState), isLooping ? "Looping" : "Ending"); + m_animationState = isLooping ? AnimationState::Looping : AnimationState::Ending; } void AnimationBase::freezeAtTime(double t) { - if (!m_compAnim) + if (!m_compositeAnimation) return; if (!m_startTime) { // If we haven't started yet, make it as if we started. - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitResponse; onAnimationStartResponse(monotonicallyIncreasingTime()); } - ASSERT(m_startTime); // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time. + ASSERT(m_startTime); // If m_startTime is zero, we haven't started yet, so we'll get a bad pause time. if (t <= m_animation->delay()) - m_pauseTime = m_startTime; + m_pauseTime = m_startTime.value_or(0); else - m_pauseTime = m_startTime + t - m_animation->delay(); + m_pauseTime = m_startTime.value_or(0) + t - m_animation->delay(); -#if USE(ACCELERATED_COMPOSITING) if (m_object && m_object->isComposited()) - toRenderBoxModelObject(m_object)->suspendAnimations(m_pauseTime); -#endif + downcast<RenderBoxModelObject>(*m_object).suspendAnimations(m_pauseTime.value()); } double AnimationBase::beginAnimationUpdateTime() const { - if (!m_compAnim) + if (!m_compositeAnimation) return 0; - return m_compAnim->animationController()->beginAnimationUpdateTime(); + return m_compositeAnimation->animationController().beginAnimationUpdateTime(); } double AnimationBase::getElapsedTime() const { - if (paused()) - return m_pauseTime - m_startTime; - if (m_startTime <= 0) +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) { + auto& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger()); + if (scrollTrigger.hasEndValue() && m_object) { + float offset = m_compositeAnimation->animationController().scrollPosition(); + float startValue = scrollTrigger.startValue().value(); + if (offset < startValue) + return 0; + float endValue = scrollTrigger.endValue().value(); + if (offset > endValue) + return m_animation->duration(); + return m_animation->duration() * (offset - startValue) / (endValue - startValue); + } + } +#endif + + if (paused()) { + double delayOffset = (!m_startTime && m_animation->delay() < 0) ? m_animation->delay() : 0; + return m_pauseTime.value_or(0) - m_startTime.value_or(0) - delayOffset; + } + + if (!m_startTime) return 0; - if (postActive()) - return 1; - return beginAnimationUpdateTime() - m_startTime; + if (postActive() || fillingForwards()) + return m_totalDuration.value_or(0); + + return beginAnimationUpdateTime() - m_startTime.value_or(0); } void AnimationBase::setElapsedTime(double time) @@ -701,4 +786,77 @@ void AnimationBase::pause() // FIXME: implement this method } +static bool containsRotation(const Vector<RefPtr<TransformOperation>>& operations) +{ + for (const auto& operation : operations) { + if (operation->type() == TransformOperation::ROTATE) + return true; + } + return false; +} + +bool AnimationBase::computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const +{ + FloatRect floatBounds = bounds; + FloatPoint transformOrigin; + + bool applyTransformOrigin = containsRotation(style.transform().operations()) || style.transform().affectedByTransformOrigin(); + if (applyTransformOrigin) { + float offsetX = style.transformOriginX().isPercent() ? rendererBox.x() : 0; + float offsetY = style.transformOriginY().isPercent() ? rendererBox.y() : 0; + + transformOrigin.setX(floatValueForLength(style.transformOriginX(), rendererBox.width()) + offsetX); + transformOrigin.setY(floatValueForLength(style.transformOriginY(), rendererBox.height()) + offsetY); + // Ignore transformOriginZ because we'll bail if we encounter any 3D transforms. + + floatBounds.moveBy(-transformOrigin); + } + + for (const auto& operation : style.transform().operations()) { + if (operation->type() == TransformOperation::ROTATE) { + // For now, just treat this as a full rotation. This could take angle into account to reduce inflation. + floatBounds = boundsOfRotatingRect(floatBounds); + } else { + TransformationMatrix transform; + operation->apply(transform, rendererBox.size()); + if (!transform.isAffine()) + return false; + + if (operation->type() == TransformOperation::MATRIX || operation->type() == TransformOperation::MATRIX_3D) { + TransformationMatrix::Decomposed2Type toDecomp; + transform.decompose2(toDecomp); + // Any rotation prevents us from using a simple start/end rect union. + if (toDecomp.angle) + return false; + } + + floatBounds = transform.mapRect(floatBounds); + } + } + + if (applyTransformOrigin) + floatBounds.moveBy(transformOrigin); + + bounds = LayoutRect(floatBounds); + return true; +} + +bool AnimationBase::computeTransformedExtentViaMatrix(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const +{ + TransformationMatrix transform; + style.applyTransform(transform, rendererBox, RenderStyle::IncludeTransformOrigin); + if (!transform.isAffine()) + return false; + + TransformationMatrix::Decomposed2Type fromDecomp; + transform.decompose2(fromDecomp); + // Any rotation prevents us from using a simple start/end rect union. + if (fromDecomp.angle) + return false; + + bounds = LayoutRect(transform.mapRect(bounds)); + return true; + +} + } // namespace WebCore |