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 | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/page/animation')
14 files changed, 2536 insertions, 1851 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 diff --git a/Source/WebCore/page/animation/AnimationBase.h b/Source/WebCore/page/animation/AnimationBase.h index 51673374d..f3387d2ef 100644 --- a/Source/WebCore/page/animation/AnimationBase.h +++ b/Source/WebCore/page/animation/AnimationBase.h @@ -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. * @@ -26,23 +26,19 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AnimationBase_h -#define AnimationBase_h +#pragma once #include "Animation.h" #include "CSSPropertyNames.h" #include "RenderStyleConstants.h" -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/RefCounted.h> #include <wtf/text/AtomicString.h> namespace WebCore { -class AnimationBase; -class AnimationController; class CompositeAnimation; class Element; +class FloatRect; +class LayoutRect; class RenderElement; class RenderStyle; class TimingFunction; @@ -50,7 +46,7 @@ class TimingFunction; class AnimationBase : public RefCounted<AnimationBase> { friend class CompositeAnimation; friend class CSSPropertyAnimation; - + WTF_MAKE_FAST_ALLOCATED; public: AnimationBase(const Animation& transition, RenderElement*, CompositeAnimation*); virtual ~AnimationBase() { } @@ -58,9 +54,9 @@ public: RenderElement* renderer() const { return m_object; } void clear() { - endAnimation(); - m_object = 0; - m_compAnim = 0; + endAnimation(); + m_object = nullptr; + m_compositeAnimation = nullptr; } double duration() const; @@ -68,106 +64,129 @@ public: // Animations and Transitions go through the states below. When entering the STARTED state // the animation is started. This may or may not require deferred response from the animator. // If so, we stay in this state until that response is received (and it returns the start time). - // Otherwise, we use the current time as the start time and go immediately to AnimationStateLooping - // or AnimationStateEnding. - enum AnimState { - AnimationStateNew, // animation just created, animation not running yet - AnimationStateStartWaitTimer, // start timer running, waiting for fire - AnimationStateStartWaitStyleAvailable, // waiting for style setup so we can start animations - AnimationStateStartWaitResponse, // animation started, waiting for response - AnimationStateLooping, // response received, animation running, loop timer running, waiting for fire - AnimationStateEnding, // received, animation running, end timer running, waiting for fire - AnimationStatePausedNew, // in pause mode when animation was created - AnimationStatePausedWaitTimer, // in pause mode when animation started - AnimationStatePausedWaitStyleAvailable, // in pause mode when waiting for style setup - AnimationStatePausedWaitResponse, // animation paused when in STARTING state - AnimationStatePausedRun, // animation paused when in LOOPING or ENDING state - AnimationStateDone, // end timer fired, animation finished and removed - AnimationStateFillingForwards // animation has ended and is retaining its final value + // Otherwise, we use the current time as the start time and go immediately to AnimationState::Looping + // or AnimationState::Ending. + enum class AnimationState { + New, // animation just created, animation not running yet + StartWaitTimer, // start timer running, waiting for fire + StartWaitStyleAvailable, // waiting for style setup so we can start animations + StartWaitResponse, // animation started, waiting for response + Looping, // response received, animation running, loop timer running, waiting for fire + Ending, // received, animation running, end timer running, waiting for fire + PausedNew, // in pause mode when animation was created + PausedWaitTimer, // in pause mode when animation started + PausedWaitStyleAvailable, // in pause mode when waiting for style setup + PausedWaitResponse, // animation paused when in STARTING state + PausedRun, // animation paused when in LOOPING or ENDING state + Done, // end timer fired, animation finished and removed + FillingForwards // animation has ended and is retaining its final value }; - enum AnimStateInput { - AnimationStateInputMakeNew, // reset back to new from any state - AnimationStateInputStartAnimation, // animation requests a start - AnimationStateInputRestartAnimation, // force a restart from any state - AnimationStateInputStartTimerFired, // start timer fired - AnimationStateInputStyleAvailable, // style is setup, ready to start animating - AnimationStateInputStartTimeSet, // m_startTime was set - AnimationStateInputLoopTimerFired, // loop timer fired - AnimationStateInputEndTimerFired, // end timer fired - AnimationStateInputPauseOverride, // pause an animation due to override - AnimationStateInputResumeOverride, // resume an overridden animation - AnimationStateInputPlayStateRunning, // play state paused -> running - AnimationStateInputPlayStatePaused, // play state running -> paused - AnimationStateInputEndAnimation // force an end from any state + enum class AnimationStateInput { + MakeNew, // reset back to new from any state + StartAnimation, // animation requests a start + RestartAnimation, // force a restart from any state + StartTimerFired, // start timer fired + StyleAvailable, // style is setup, ready to start animating + StartTimeSet, // m_startTime was set + LoopTimerFired, // loop timer fired + EndTimerFired, // end timer fired + PauseOverride, // pause an animation due to override + ResumeOverride, // resume an overridden animation + PlayStateRunning, // play state paused -> running + PlayStatePaused, // play state running -> paused + EndAnimation // force an end from any state }; - // Called when animation is in AnimationStateNew to start animation - void updateStateMachine(AnimStateInput, double param); + // Called when animation is in AnimationState::New to start animation + void updateStateMachine(AnimationStateInput, double param); // Animation has actually started, at passed time void onAnimationStartResponse(double startTime) { - updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, startTime); + updateStateMachine(AnimationStateInput::StartTimeSet, startTime); } // Called to change to or from paused state void updatePlayState(EAnimPlayState); bool playStatePlaying() const; - bool waitingToStart() const { return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStatePausedNew; } + bool waitingToStart() const { return m_animationState == AnimationState::New || m_animationState == AnimationState::StartWaitTimer || m_animationState == AnimationState::PausedNew; } bool preActive() const { - return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStateStartWaitStyleAvailable || m_animState == AnimationStateStartWaitResponse; + return m_animationState == AnimationState::New || m_animationState == AnimationState::StartWaitTimer || m_animationState == AnimationState::StartWaitStyleAvailable || m_animationState == AnimationState::StartWaitResponse; } - bool postActive() const { return m_animState == AnimationStateDone; } + bool postActive() const { return m_animationState == AnimationState::Done; } + bool fillingForwards() const { return m_animationState == AnimationState::FillingForwards; } bool active() const { return !postActive() && !preActive(); } bool running() const { return !isNew() && !postActive(); } - bool paused() const { return m_pauseTime >= 0 || m_animState == AnimationStatePausedNew; } - bool isNew() const { return m_animState == AnimationStateNew || m_animState == AnimationStatePausedNew; } - bool waitingForStartTime() const { return m_animState == AnimationStateStartWaitResponse; } - bool waitingForStyleAvailable() const { return m_animState == AnimationStateStartWaitStyleAvailable; } + bool paused() const { return m_pauseTime || m_animationState == AnimationState::PausedNew; } + bool inPausedState() const { return m_animationState >= AnimationState::PausedNew && m_animationState <= AnimationState::PausedRun; } + bool isNew() const { return m_animationState == AnimationState::New || m_animationState == AnimationState::PausedNew; } + bool waitingForStartTime() const { return m_animationState == AnimationState::StartWaitResponse; } + bool waitingForStyleAvailable() const { return m_animationState == AnimationState::StartWaitStyleAvailable; } + + bool isAccelerated() const { return m_isAccelerated; } virtual double timeToNextService(); - double progress(double scale, double offset, const TimingFunction*) const; + double progress(double scale = 1, double offset = 0, const TimingFunction* = nullptr) const; - virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* /*currentStyle*/, RenderStyle* /*targetStyle*/, RefPtr<RenderStyle>& /*animatedStyle*/) = 0; - virtual void getAnimatedStyle(RefPtr<RenderStyle>& /*animatedStyle*/) = 0; + // Returns true if the animation state changed. + virtual bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* /*currentStyle*/, const RenderStyle* /*targetStyle*/, std::unique_ptr<RenderStyle>& /*animatedStyle*/, bool& didBlendStyle) = 0; + virtual void getAnimatedStyle(std::unique_ptr<RenderStyle>& /*animatedStyle*/) = 0; + + virtual bool computeExtentOfTransformAnimation(LayoutRect&) const = 0; virtual bool shouldFireEvents() const { return false; } void fireAnimationEventsIfNeeded(); - bool animationsMatch(const Animation*) const; + bool animationsMatch(const Animation&) const; - void setAnimation(const Animation& animation) { m_animation = const_cast<Animation*>(&animation); } + const Animation& animation() const { return m_animation; } + void setAnimation(const Animation& animation) { m_animation = const_cast<Animation&>(animation); } // Return true if this animation is overridden. This will only be the case for // ImplicitAnimations and is used to determine whether or not we should force // set the start time. If an animation is overridden, it will probably not get - // back the AnimationStateInputStartTimeSet input. + // back the AnimationStateInput::StartTimeSet input. virtual bool overridden() const { return false; } // Does this animation/transition involve the given property? virtual bool affectsProperty(CSSPropertyID /*property*/) const { return false; } - bool isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, bool isRunningNow) const + enum RunningStates { + Delaying = 1 << 0, + Paused = 1 << 1, + Running = 1 << 2, + }; + typedef unsigned RunningState; + bool isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, RunningState runningState) const { if (acceleratedOnly && !m_isAccelerated) return false; - - if (isRunningNow) - return (!waitingToStart() && !postActive()) && affectsProperty(property); - return !postActive() && affectsProperty(property); + if (!affectsProperty(property)) + return false; + + if ((runningState & Delaying) && preActive()) + return true; + + if ((runningState & Paused) && inPausedState()) + return true; + + if ((runningState & Running) && !inPausedState() && (m_animationState >= AnimationState::StartWaitStyleAvailable && m_animationState < AnimationState::Done)) + return true; + + return false; } - // FIXME: rename this using the "lists match" terminology. - bool isTransformFunctionListValid() const { return m_transformFunctionListValid; } -#if ENABLE(CSS_FILTERS) + bool transformFunctionListsMatch() const { return m_transformFunctionListsMatch; } bool filterFunctionListsMatch() const { return m_filterFunctionListsMatch; } +#if ENABLE(FILTERS_LEVEL_2) + bool backdropFilterFunctionListsMatch() const { return m_backdropFilterFunctionListsMatch; } #endif // Freeze the animation; used by DumpRenderTree. @@ -186,16 +205,14 @@ public: void styleAvailable() { ASSERT(waitingForStyleAvailable()); - updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); + updateStateMachine(AnimationStateInput::StyleAvailable, -1); } - const Animation& animation() const { return *m_animation; } - protected: virtual void overrideAnimations() { } virtual void resumeOverriddenAnimations() { } - CompositeAnimation* compositeAnimation() { return m_compAnim; } + CompositeAnimation* compositeAnimation() { return m_compositeAnimation; } // These are called when the corresponding timer fires so subclasses can do any extra work virtual void onAnimationStart(double /*elapsedTime*/) { } @@ -211,7 +228,7 @@ protected: void goIntoEndingOrLoopingState(); - bool isAccelerated() const { return m_isAccelerated; } + AnimationState state() const { return m_animationState; } static void setNeedsStyleRecalc(Element*); @@ -219,26 +236,27 @@ protected: double fractionalTime(double scale, double elapsedTime, double offset) const; - AnimState m_animState; - - bool m_isAccelerated; - bool m_transformFunctionListValid; -#if ENABLE(CSS_FILTERS) - bool m_filterFunctionListsMatch; -#endif - double m_startTime; - double m_pauseTime; - double m_requestedStartTime; - - double m_totalDuration; - double m_nextIterationDuration; + // These return true if we can easily compute a bounding box by applying the style's transform to the bounds rect. + bool computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle&, LayoutRect& bounds) const; + bool computeTransformedExtentViaMatrix(const FloatRect& rendererBox, const RenderStyle&, LayoutRect& bounds) const; RenderElement* m_object; - - RefPtr<Animation> m_animation; - CompositeAnimation* m_compAnim; + CompositeAnimation* m_compositeAnimation; // Ideally this would be a reference, but it has to be cleared if an animation is destroyed inside an event callback. + Ref<Animation> m_animation; + + std::optional<double> m_startTime; + std::optional<double> m_pauseTime; + double m_requestedStartTime { 0 }; + std::optional<double> m_totalDuration; + std::optional<double> m_nextIterationDuration; + + AnimationState m_animationState { AnimationState::New }; + bool m_isAccelerated { false }; + bool m_transformFunctionListsMatch { false }; + bool m_filterFunctionListsMatch { false }; +#if ENABLE(FILTERS_LEVEL_2) + bool m_backdropFilterFunctionListsMatch { false }; +#endif }; } // namespace WebCore - -#endif // AnimationBase_h diff --git a/Source/WebCore/page/animation/AnimationController.cpp b/Source/WebCore/page/animation/AnimationController.cpp deleted file mode 100644 index 5b8c90b8a..000000000 --- a/Source/WebCore/page/animation/AnimationController.cpp +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 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 - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "AnimationController.h" - -#include "AnimationBase.h" -#include "AnimationControllerPrivate.h" -#include "CSSParser.h" -#include "CSSPropertyAnimation.h" -#include "CompositeAnimation.h" -#include "EventNames.h" -#include "Frame.h" -#include "FrameView.h" -#include "Logging.h" -#include "PseudoElement.h" -#include "RenderView.h" -#include "TransitionEvent.h" -#include "WebKitAnimationEvent.h" -#include "WebKitTransitionEvent.h" -#include <wtf/CurrentTime.h> - -namespace WebCore { - -static const double cAnimationTimerDelay = 0.025; -static const double cBeginAnimationUpdateTimeNotSet = -1; - -AnimationControllerPrivate::AnimationControllerPrivate(Frame& frame) - : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) - , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired) - , m_frame(frame) - , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) - , m_animationsWaitingForStyle() - , m_animationsWaitingForStartTimeResponse() - , m_waitingForAsyncStartNotification(false) - , m_isSuspended(false) - , m_allowsNewAnimationsWhileSuspended(false) -{ -} - -AnimationControllerPrivate::~AnimationControllerPrivate() -{ -} - -CompositeAnimation& AnimationControllerPrivate::ensureCompositeAnimation(RenderElement* renderer) -{ - auto result = m_compositeAnimations.add(renderer, nullptr); - if (result.isNewEntry) - result.iterator->value = CompositeAnimation::create(this); - return *result.iterator->value; -} - -bool AnimationControllerPrivate::clear(RenderElement* renderer) -{ - // Return false if we didn't do anything OR we are suspended (so we don't try to - // do a setNeedsStyleRecalc() when suspended). - RefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer); - if (!animation) - return false; - animation->clearRenderer(); - return animation->isSuspended(); -} - -double AnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/) -{ - double timeToNextService = -1; - bool calledSetChanged = false; - - auto end = m_compositeAnimations.end(); - for (auto it = m_compositeAnimations.begin(); it != end; ++it) { - CompositeAnimation& animation = *it->value; - if (!animation.isSuspended() && animation.hasAnimations()) { - double t = animation.timeToNextService(); - if (t != -1 && (t < timeToNextService || timeToNextService == -1)) - timeToNextService = t; - if (!timeToNextService) { - if (callSetChanged != CallSetChanged) - break; - Element* element = it->key->element(); - ASSERT(element); - ASSERT(!element->document().inPageCache()); - element->setNeedsStyleRecalc(SyntheticStyleChange); - calledSetChanged = true; - } - } - } - - if (calledSetChanged) - m_frame.document()->updateStyleIfNeeded(); - - return timeToNextService; -} - -void AnimationControllerPrivate::updateAnimationTimerForRenderer(RenderElement* renderer) -{ - double timeToNextService = 0; - - const CompositeAnimation* compositeAnimation = m_compositeAnimations.get(renderer); - if (!compositeAnimation->isSuspended() && compositeAnimation->hasAnimations()) - timeToNextService = compositeAnimation->timeToNextService(); - - if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService)) - return; - - m_animationTimer.startOneShot(timeToNextService); -} - -void AnimationControllerPrivate::updateAnimationTimer(SetChanged callSetChanged/* = DoNotCallSetChanged*/) -{ - double timeToNextService = updateAnimations(callSetChanged); - - LOG(Animations, "updateAnimationTimer: timeToNextService is %.2f", timeToNextService); - - // If we want service immediately, we start a repeating timer to reduce the overhead of starting - if (!timeToNextService) { - if (!m_animationTimer.isActive() || m_animationTimer.repeatInterval() == 0) - m_animationTimer.startRepeating(cAnimationTimerDelay); - return; - } - - // If we don't need service, we want to make sure the timer is no longer running - if (timeToNextService < 0) { - if (m_animationTimer.isActive()) - m_animationTimer.stop(); - return; - } - - // Otherwise, we want to start a one-shot timer so we get here again - m_animationTimer.startOneShot(timeToNextService); -} - -void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>&) -{ - fireEventsAndUpdateStyle(); -} - -void AnimationControllerPrivate::fireEventsAndUpdateStyle() -{ - // Protect the frame from getting destroyed in the event handler - Ref<Frame> protector(m_frame); - - bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_elementChangesToDispatch.isEmpty(); - - // fire all the events - Vector<EventToDispatch> eventsToDispatch = std::move(m_eventsToDispatch); - Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = eventsToDispatch.end(); - for (Vector<EventToDispatch>::const_iterator it = eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) { - Element* element = it->element.get(); - if (it->eventType == eventNames().transitionendEvent) - element->dispatchEvent(TransitionEvent::create(it->eventType, it->name, it->elapsedTime, PseudoElement::pseudoElementNameForEvents(element->pseudoId()))); - else - element->dispatchEvent(WebKitAnimationEvent::create(it->eventType, it->name, it->elapsedTime)); - } - - for (unsigned i = 0, size = m_elementChangesToDispatch.size(); i < size; ++i) - m_elementChangesToDispatch[i]->setNeedsStyleRecalc(SyntheticStyleChange); - - m_elementChangesToDispatch.clear(); - - if (updateStyle) - m_frame.document()->updateStyleIfNeeded(); -} - -void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() -{ - if (!m_updateStyleIfNeededDispatcher.isActive()) - m_updateStyleIfNeededDispatcher.startOneShot(0); -} - -void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) -{ - m_eventsToDispatch.grow(m_eventsToDispatch.size()+1); - EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1]; - event.element = element; - event.eventType = eventType; - event.name = name; - event.elapsedTime = elapsedTime; - - startUpdateStyleIfNeededDispatcher(); -} - -void AnimationControllerPrivate::addElementChangeToDispatch(PassRef<Element> element) -{ - m_elementChangesToDispatch.append(std::move(element)); - ASSERT(!m_elementChangesToDispatch.last()->document().inPageCache()); - startUpdateStyleIfNeededDispatcher(); -} - -#if ENABLE(REQUEST_ANIMATION_FRAME) -void AnimationControllerPrivate::animationFrameCallbackFired() -{ - double timeToNextService = updateAnimations(CallSetChanged); - - if (timeToNextService >= 0) - m_frame.document()->view()->scheduleAnimation(); -} -#endif - -void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>&) -{ - // Make sure animationUpdateTime is updated, so that it is current even if no - // styleChange has happened (e.g. accelerated animations) - setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - - // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate - // updateStyleIfNeeded. It will then call back to us with new information. - updateAnimationTimer(CallSetChanged); - - // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before - // the 'end' event fires. - fireEventsAndUpdateStyle(); -} - -bool AnimationControllerPrivate::isRunningAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - const CompositeAnimation* animation = m_compositeAnimations.get(renderer); - return animation && animation->isAnimatingProperty(property, false, isRunningNow); -} - -bool AnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - const CompositeAnimation* animation = m_compositeAnimations.get(renderer); - return animation && animation->isAnimatingProperty(property, true, isRunningNow); -} - -void AnimationControllerPrivate::suspendAnimations() -{ - if (isSuspended()) - return; - - suspendAnimationsForDocument(m_frame.document()); - - // Traverse subframes - for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) - child->animation().suspendAnimations(); - - m_isSuspended = true; -} - -void AnimationControllerPrivate::resumeAnimations() -{ - if (!isSuspended()) - return; - - resumeAnimationsForDocument(m_frame.document()); - - // Traverse subframes - for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) - child->animation().resumeAnimations(); - - m_isSuspended = false; -} - -void AnimationControllerPrivate::suspendAnimationsForDocument(Document* document) -{ - setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - - for (auto it = m_compositeAnimations.begin(), end = m_compositeAnimations.end(); it != end; ++it) { - if (&it->key->document() == document) - it->value->suspendAnimations(); - } - - updateAnimationTimer(); -} - -void AnimationControllerPrivate::resumeAnimationsForDocument(Document* document) -{ - setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - - for (auto it = m_compositeAnimations.begin(), end = m_compositeAnimations.end(); it != end; ++it) { - if (&it->key->document() == document) - it->value->resumeAnimations(); - } - - updateAnimationTimer(); -} - -void AnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document) -{ - if (!isSuspended() || allowsNewAnimationsWhileSuspended()) - resumeAnimationsForDocument(document); -} - -void AnimationControllerPrivate::setAllowsNewAnimationsWhileSuspended(bool allowed) -{ - m_allowsNewAnimationsWhileSuspended = allowed; -} - -bool AnimationControllerPrivate::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) -{ - if (!renderer) - return false; - - CompositeAnimation& compositeAnimation = ensureCompositeAnimation(renderer); - if (compositeAnimation.pauseAnimationAtTime(name, t)) { - renderer->element()->setNeedsStyleRecalc(SyntheticStyleChange); - startUpdateStyleIfNeededDispatcher(); - return true; - } - - return false; -} - -bool AnimationControllerPrivate::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) -{ - if (!renderer) - return false; - - CompositeAnimation& compositeAnimation = ensureCompositeAnimation(renderer); - if (compositeAnimation.pauseTransitionAtTime(cssPropertyID(property), t)) { - renderer->element()->setNeedsStyleRecalc(SyntheticStyleChange); - startUpdateStyleIfNeededDispatcher(); - return true; - } - - return false; -} - -double AnimationControllerPrivate::beginAnimationUpdateTime() -{ - if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) - m_beginAnimationUpdateTime = monotonicallyIncreasingTime(); - return m_beginAnimationUpdateTime; -} - -void AnimationControllerPrivate::endAnimationUpdate() -{ - styleAvailable(); - if (!m_waitingForAsyncStartNotification) - startTimeResponse(beginAnimationUpdateTime()); -} - -void AnimationControllerPrivate::receivedStartTimeResponse(double time) -{ - m_waitingForAsyncStartNotification = false; - startTimeResponse(time); -} - -PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderElement* renderer) -{ - if (!renderer) - return 0; - - const CompositeAnimation* rendererAnimations = m_compositeAnimations.get(renderer); - if (!rendererAnimations) - return &renderer->style(); - - RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle(); - if (!animatingStyle) - animatingStyle = &renderer->style(); - - return animatingStyle.release(); -} - -unsigned AnimationControllerPrivate::numberOfActiveAnimations(Document* document) const -{ - unsigned count = 0; - - for (auto it = m_compositeAnimations.begin(), end = m_compositeAnimations.end(); it != end; ++it) { - if (&it->key->document() == document) - count += it->value->numberOfActiveAnimations(); - } - - return count; -} - -void AnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) -{ - // Make sure this animation is not in the start time waiters - m_animationsWaitingForStartTimeResponse.remove(animation); - - m_animationsWaitingForStyle.add(animation); -} - -void AnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) -{ - m_animationsWaitingForStyle.remove(animationToRemove); -} - -void AnimationControllerPrivate::styleAvailable() -{ - // Go through list of waiters and send them on their way - for (const auto& waitingAnimation : m_animationsWaitingForStyle) - waitingAnimation->styleAvailable(); - - m_animationsWaitingForStyle.clear(); -} - -void AnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) -{ - // If willGetResponse is true, it means this animation is actually waiting for a response - // (which will come in as a call to notifyAnimationStarted()). - // In that case we don't need to add it to this list. We just set a waitingForAResponse flag - // which says we are waiting for the response. If willGetResponse is false, this animation - // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for - // another animation to which it will sync. - // - // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is - // true. If so, we just return and will do our work when the first notifyXXXStarted() call - // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will - // do our work right away. In both cases we call the onAnimationStartResponse() method - // on each animation. In the first case we send in the time we got from notifyXXXStarted(). - // In the second case, we just pass in the beginAnimationUpdateTime(). - // - // This will synchronize all software and accelerated animations started in the same - // updateStyleIfNeeded cycle. - // - - if (willGetResponse) - m_waitingForAsyncStartNotification = true; - - m_animationsWaitingForStartTimeResponse.add(animation); -} - -void AnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) -{ - m_animationsWaitingForStartTimeResponse.remove(animationToRemove); - - if (m_animationsWaitingForStartTimeResponse.isEmpty()) - m_waitingForAsyncStartNotification = false; -} - -void AnimationControllerPrivate::startTimeResponse(double time) -{ - // Go through list of waiters and send them on their way - - for (const auto& animation : m_animationsWaitingForStartTimeResponse) - animation->onAnimationStartResponse(time); - - m_animationsWaitingForStartTimeResponse.clear(); - m_waitingForAsyncStartNotification = false; -} - -void AnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) -{ - removeFromAnimationsWaitingForStyle(animation); - removeFromAnimationsWaitingForStartTimeResponse(animation); -} - -AnimationController::AnimationController(Frame& frame) - : m_data(std::make_unique<AnimationControllerPrivate>(frame)) - , m_beginAnimationUpdateCount(0) -{ -} - -AnimationController::~AnimationController() -{ -} - -void AnimationController::cancelAnimations(RenderElement* renderer) -{ - if (!m_data->hasAnimations()) - return; - - if (m_data->clear(renderer)) { - Element* element = renderer->element(); - ASSERT(!element || !element->document().inPageCache()); - if (element) - element->setNeedsStyleRecalc(SyntheticStyleChange); - } -} - -PassRef<RenderStyle> AnimationController::updateAnimations(RenderElement& renderer, PassRef<RenderStyle> newStyle) -{ - // Don't do anything if we're in the cache - if (renderer.document().inPageCache()) - return newStyle; - - RenderStyle* oldStyle = renderer.hasInitializedStyle() ? &renderer.style() : nullptr; - - if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.get().animations() && !newStyle.get().transitions())) - return newStyle; - - // Don't run transitions when printing. - if (renderer.view().printing()) - return newStyle; - - // Fetch our current set of implicit animations from a hashtable. We then compare them - // against the animations in the style and make sure we're in sync. If destination values - // have changed, we reset the animation. We then do a blend to get new values and we return - // a new style. - - // We don't support anonymous pseudo elements like :first-line or :first-letter. - ASSERT(renderer.element()); - - Ref<RenderStyle> newStyleBeforeAnimation(std::move(newStyle)); - - CompositeAnimation& rendererAnimations = m_data->ensureCompositeAnimation(&renderer); - auto blendedStyle = rendererAnimations.animate(renderer, oldStyle, newStyleBeforeAnimation.get()); - - if (renderer.parent() || newStyleBeforeAnimation->animations() || (oldStyle && oldStyle->animations())) { - m_data->updateAnimationTimerForRenderer(&renderer); -#if ENABLE(REQUEST_ANIMATION_FRAME) - renderer.view().frameView().scheduleAnimation(); -#endif - } - - if (&blendedStyle.get() != &newStyleBeforeAnimation.get()) { - // If the animations/transitions change opacity or transform, we need to update - // the style to impose the stacking rules. Note that this is also - // done in StyleResolver::adjustRenderStyle(). - if (blendedStyle.get().hasAutoZIndex() && (blendedStyle.get().opacity() < 1.0f || blendedStyle.get().hasTransform())) - blendedStyle.get().setZIndex(0); - } - return blendedStyle; -} - -PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderElement* renderer) -{ - return m_data->getAnimatedStyleForRenderer(renderer); -} - -void AnimationController::notifyAnimationStarted(RenderElement*, double startTime) -{ - m_data->receivedStartTimeResponse(startTime); -} - -bool AnimationController::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) -{ - return m_data->pauseAnimationAtTime(renderer, name, t); -} - -unsigned AnimationController::numberOfActiveAnimations(Document* document) const -{ - return m_data->numberOfActiveAnimations(document); -} - -bool AnimationController::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) -{ - return m_data->pauseTransitionAtTime(renderer, property, t); -} - -bool AnimationController::isRunningAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - return m_data->isRunningAnimationOnRenderer(renderer, property, isRunningNow); -} - -bool AnimationController::isRunningAcceleratedAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow); -} - -bool AnimationController::isSuspended() const -{ - return m_data->isSuspended(); -} - -void AnimationController::suspendAnimations() -{ - LOG(Animations, "controller is suspending animations"); - m_data->suspendAnimations(); -} - -void AnimationController::resumeAnimations() -{ - LOG(Animations, "controller is resuming animations"); - m_data->resumeAnimations(); -} - -bool AnimationController::allowsNewAnimationsWhileSuspended() const -{ - return m_data->allowsNewAnimationsWhileSuspended(); -} - -void AnimationController::setAllowsNewAnimationsWhileSuspended(bool allowed) -{ - m_data->setAllowsNewAnimationsWhileSuspended(allowed); -} - -#if ENABLE(REQUEST_ANIMATION_FRAME) -void AnimationController::serviceAnimations() -{ - m_data->animationFrameCallbackFired(); -} -#endif - -void AnimationController::suspendAnimationsForDocument(Document* document) -{ - LOG(Animations, "suspending animations for document %p", document); - m_data->suspendAnimationsForDocument(document); -} - -void AnimationController::resumeAnimationsForDocument(Document* document) -{ - LOG(Animations, "resuming animations for document %p", document); - m_data->resumeAnimationsForDocument(document); -} - -void AnimationController::startAnimationsIfNotSuspended(Document* document) -{ - LOG(Animations, "animations may start for document %p", document); - m_data->startAnimationsIfNotSuspended(document); -} - -void AnimationController::beginAnimationUpdate() -{ - if (!m_beginAnimationUpdateCount) - m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - ++m_beginAnimationUpdateCount; -} - -void AnimationController::endAnimationUpdate() -{ - ASSERT(m_beginAnimationUpdateCount > 0); - --m_beginAnimationUpdateCount; - if (!m_beginAnimationUpdateCount) - m_data->endAnimationUpdate(); -} - -bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) -{ -#if USE(ACCELERATED_COMPOSITING) - return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); -#else - UNUSED_PARAM(property); - return false; -#endif -} - -} // namespace WebCore diff --git a/Source/WebCore/page/animation/CSSAnimationController.cpp b/Source/WebCore/page/animation/CSSAnimationController.cpp new file mode 100644 index 000000000..b71ed2e43 --- /dev/null +++ b/Source/WebCore/page/animation/CSSAnimationController.cpp @@ -0,0 +1,805 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CSSAnimationController.h" + +#include "AnimationBase.h" +#include "AnimationEvent.h" +#include "CSSAnimationControllerPrivate.h" +#include "CSSPropertyAnimation.h" +#include "CSSPropertyParser.h" +#include "CompositeAnimation.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "Logging.h" +#include "PseudoElement.h" +#include "RenderView.h" +#include "TransitionEvent.h" +#include "WebKitAnimationEvent.h" +#include "WebKitTransitionEvent.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +// Allow a little more than 60fps to make sure we can at least hit that frame rate. +static const double cAnimationTimerDelay = 0.015; +static const double cBeginAnimationUpdateTimeNotSet = -1; + +class AnimationPrivateUpdateBlock { +public: + AnimationPrivateUpdateBlock(CSSAnimationControllerPrivate& animationController) + : m_animationController(animationController) + { + m_animationController.beginAnimationUpdate(); + } + + ~AnimationPrivateUpdateBlock() + { + m_animationController.endAnimationUpdate(); + } + + CSSAnimationControllerPrivate& m_animationController; +}; + +CSSAnimationControllerPrivate::CSSAnimationControllerPrivate(Frame& frame) + : m_animationTimer(*this, &CSSAnimationControllerPrivate::animationTimerFired) + , m_updateStyleIfNeededDispatcher(*this, &CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired) + , m_frame(frame) + , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) + , m_beginAnimationUpdateCount(0) + , m_waitingForAsyncStartNotification(false) + , m_isSuspended(false) + , m_allowsNewAnimationsWhileSuspended(false) +{ +} + +CSSAnimationControllerPrivate::~CSSAnimationControllerPrivate() +{ +} + +CompositeAnimation& CSSAnimationControllerPrivate::ensureCompositeAnimation(RenderElement& renderer) +{ + auto result = m_compositeAnimations.add(&renderer, nullptr); + if (result.isNewEntry) { + result.iterator->value = CompositeAnimation::create(*this); + renderer.setIsCSSAnimating(true); + } + + if (animationsAreSuspendedForDocument(&renderer.document())) + result.iterator->value->suspendAnimations(); + + return *result.iterator->value; +} + +bool CSSAnimationControllerPrivate::clear(RenderElement& renderer) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p clear: %p", this, &renderer); + + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + + Element* element = renderer.element(); + + m_eventsToDispatch.removeAllMatching([element] (const EventToDispatch& info) { + return info.element.ptr() == element; + }); + + m_elementChangesToDispatch.removeAllMatching([element](auto& currentElement) { + return currentElement.ptr() == element; + }); + + // Return false if we didn't do anything OR we are suspended (so we don't try to + // do a invalidateStyleForSubtree() when suspended). + RefPtr<CompositeAnimation> animation = m_compositeAnimations.take(&renderer); + ASSERT(animation); + renderer.setIsCSSAnimating(false); + animation->clearRenderer(); + return animation->isSuspended(); +} + +double CSSAnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/) +{ + AnimationPrivateUpdateBlock updateBlock(*this); + double timeToNextService = -1; + bool calledSetChanged = false; + + for (auto& compositeAnimation : m_compositeAnimations) { + CompositeAnimation& animation = *compositeAnimation.value; + if (!animation.isSuspended() && animation.hasAnimations()) { + double t = animation.timeToNextService(); + if (t != -1 && (t < timeToNextService || timeToNextService == -1)) + timeToNextService = t; + if (!timeToNextService) { + if (callSetChanged != CallSetChanged) + break; + Element* element = compositeAnimation.key->element(); + ASSERT(element); + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); + calledSetChanged = true; + } + } + } + + if (calledSetChanged) + m_frame.document()->updateStyleIfNeeded(); + + return timeToNextService; +} + +void CSSAnimationControllerPrivate::updateAnimationTimerForRenderer(RenderElement& renderer) +{ + double timeToNextService = 0; + + const CompositeAnimation* compositeAnimation = m_compositeAnimations.get(&renderer); + if (!compositeAnimation->isSuspended() && compositeAnimation->hasAnimations()) + timeToNextService = compositeAnimation->timeToNextService(); + + if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService)) + return; + + m_animationTimer.startOneShot(timeToNextService); +} + +void CSSAnimationControllerPrivate::updateAnimationTimer(SetChanged callSetChanged/* = DoNotCallSetChanged*/) +{ + double timeToNextService = updateAnimations(callSetChanged); + + LOG(Animations, "updateAnimationTimer: timeToNextService is %.2f", timeToNextService); + + // If we want service immediately, we start a repeating timer to reduce the overhead of starting + if (!timeToNextService) { + if (!m_animationTimer.isActive() || !m_animationTimer.repeatInterval()) + m_animationTimer.startRepeating(cAnimationTimerDelay); + return; + } + + // If we don't need service, we want to make sure the timer is no longer running + if (timeToNextService < 0) { + if (m_animationTimer.isActive()) + m_animationTimer.stop(); + return; + } + + // Otherwise, we want to start a one-shot timer so we get here again + m_animationTimer.startOneShot(timeToNextService); +} + +void CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired() +{ + fireEventsAndUpdateStyle(); +} + +void CSSAnimationControllerPrivate::fireEventsAndUpdateStyle() +{ + // Protect the frame from getting destroyed in the event handler + Ref<Frame> protector(m_frame); + + bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_elementChangesToDispatch.isEmpty(); + + // fire all the events + Vector<EventToDispatch> eventsToDispatch = WTFMove(m_eventsToDispatch); + for (auto& event : eventsToDispatch) { + Element& element = event.element; + if (event.eventType == eventNames().transitionendEvent) + element.dispatchEvent(TransitionEvent::create(event.eventType, event.name, event.elapsedTime, PseudoElement::pseudoElementNameForEvents(element.pseudoId()))); + else + element.dispatchEvent(AnimationEvent::create(event.eventType, event.name, event.elapsedTime)); + } + + for (auto& change : m_elementChangesToDispatch) + change->invalidateStyleAndLayerComposition(); + + m_elementChangesToDispatch.clear(); + + if (updateStyle) + m_frame.document()->updateStyleIfNeeded(); +} + +void CSSAnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() +{ + if (!m_updateStyleIfNeededDispatcher.isActive()) + m_updateStyleIfNeededDispatcher.startOneShot(0); +} + +void CSSAnimationControllerPrivate::addEventToDispatch(Element& element, const AtomicString& eventType, const String& name, double elapsedTime) +{ + m_eventsToDispatch.append({ element, eventType, name, elapsedTime }); + startUpdateStyleIfNeededDispatcher(); +} + +void CSSAnimationControllerPrivate::addElementChangeToDispatch(Element& element) +{ + m_elementChangesToDispatch.append(element); + ASSERT(m_elementChangesToDispatch.last()->document().pageCacheState() == Document::NotInPageCache); + startUpdateStyleIfNeededDispatcher(); +} + +void CSSAnimationControllerPrivate::animationFrameCallbackFired() +{ + double timeToNextService = updateAnimations(CallSetChanged); + + if (timeToNextService >= 0) + m_frame.document()->view()->scheduleAnimation(); +} + +void CSSAnimationControllerPrivate::animationTimerFired() +{ + // We need to keep the frame alive, since it owns us. + Ref<Frame> protector(m_frame); + + // The animation timer might fire before the layout timer, in + // which case we might create some animations with incorrect + // values if we don't layout first. + if (m_requiresLayout) { + if (auto* frameView = m_frame.document()->view()) { + if (frameView->needsLayout()) + frameView->forceLayout(); + } + m_requiresLayout = false; + } + + // Make sure animationUpdateTime is updated, so that it is current even if no + // styleChange has happened (e.g. accelerated animations) + AnimationPrivateUpdateBlock updateBlock(*this); + + // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate + // updateStyleIfNeeded. It will then call back to us with new information. + updateAnimationTimer(CallSetChanged); + + // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before + // the 'end' event fires. + fireEventsAndUpdateStyle(); +} + +bool CSSAnimationControllerPrivate::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& animation = *m_compositeAnimations.get(&renderer); + return animation.isAnimatingProperty(property, false, runningState); +} + +bool CSSAnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& animation = *m_compositeAnimations.get(&renderer); + return animation.isAnimatingProperty(property, true, runningState); +} + +void CSSAnimationControllerPrivate::suspendAnimations() +{ + if (isSuspended()) + return; + + suspendAnimationsForDocument(m_frame.document()); + + // Traverse subframes + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->animation().suspendAnimations(); + + m_isSuspended = true; +} + +void CSSAnimationControllerPrivate::resumeAnimations() +{ + if (!isSuspended()) + return; + + resumeAnimationsForDocument(m_frame.document()); + + // Traverse subframes + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->animation().resumeAnimations(); + + m_isSuspended = false; +} + +bool CSSAnimationControllerPrivate::animationsAreSuspendedForDocument(Document* document) +{ + return isSuspended() || m_suspendedDocuments.contains(document); +} + +void CSSAnimationControllerPrivate::detachFromDocument(Document* document) +{ + m_suspendedDocuments.remove(document); +} + +void CSSAnimationControllerPrivate::suspendAnimationsForDocument(Document* document) +{ + if (animationsAreSuspendedForDocument(document)) + return; + + m_suspendedDocuments.add(document); + + AnimationPrivateUpdateBlock updateBlock(*this); + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + animation.value->suspendAnimations(); + } + + updateAnimationTimer(); +} + +void CSSAnimationControllerPrivate::resumeAnimationsForDocument(Document* document) +{ + if (!animationsAreSuspendedForDocument(document)) + return; + + detachFromDocument(document); + + AnimationPrivateUpdateBlock updateBlock(*this); + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + animation.value->resumeAnimations(); + } + + updateAnimationTimer(); +} + +void CSSAnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document) +{ + if (!animationsAreSuspendedForDocument(document) || allowsNewAnimationsWhileSuspended()) + resumeAnimationsForDocument(document); +} + +void CSSAnimationControllerPrivate::setAllowsNewAnimationsWhileSuspended(bool allowed) +{ + m_allowsNewAnimationsWhileSuspended = allowed; +} + +bool CSSAnimationControllerPrivate::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) +{ + if (!renderer) + return false; + + CompositeAnimation& compositeAnimation = ensureCompositeAnimation(*renderer); + if (compositeAnimation.pauseAnimationAtTime(name, t)) { + renderer->element()->invalidateStyleAndLayerComposition(); + startUpdateStyleIfNeededDispatcher(); + return true; + } + + return false; +} + +bool CSSAnimationControllerPrivate::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) +{ + if (!renderer) + return false; + + CompositeAnimation& compositeAnimation = ensureCompositeAnimation(*renderer); + if (compositeAnimation.pauseTransitionAtTime(cssPropertyID(property), t)) { + renderer->element()->invalidateStyleAndLayerComposition(); + startUpdateStyleIfNeededDispatcher(); + return true; + } + + return false; +} + +double CSSAnimationControllerPrivate::beginAnimationUpdateTime() +{ + ASSERT(m_beginAnimationUpdateCount); + if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) + m_beginAnimationUpdateTime = monotonicallyIncreasingTime(); + + return m_beginAnimationUpdateTime; +} + +void CSSAnimationControllerPrivate::beginAnimationUpdate() +{ + if (!m_beginAnimationUpdateCount) + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); + ++m_beginAnimationUpdateCount; +} + +void CSSAnimationControllerPrivate::endAnimationUpdate() +{ + ASSERT(m_beginAnimationUpdateCount > 0); + if (m_beginAnimationUpdateCount == 1) { + styleAvailable(); + if (!m_waitingForAsyncStartNotification) + startTimeResponse(beginAnimationUpdateTime()); + } + --m_beginAnimationUpdateCount; +} + +void CSSAnimationControllerPrivate::receivedStartTimeResponse(double time) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p receivedStartTimeResponse %f", this, time); + + m_waitingForAsyncStartNotification = false; + startTimeResponse(time); +} + +std::unique_ptr<RenderStyle> CSSAnimationControllerPrivate::getAnimatedStyleForRenderer(RenderElement& renderer) +{ + AnimationPrivateUpdateBlock animationUpdateBlock(*this); + + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& rendererAnimations = *m_compositeAnimations.get(&renderer); + std::unique_ptr<RenderStyle> animatingStyle = rendererAnimations.getAnimatedStyle(); + if (!animatingStyle) + animatingStyle = RenderStyle::clonePtr(renderer.style()); + + return animatingStyle; +} + +bool CSSAnimationControllerPrivate::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + + const CompositeAnimation& rendererAnimations = *m_compositeAnimations.get(&renderer); + if (!rendererAnimations.isAnimatingProperty(CSSPropertyTransform, false, AnimationBase::Running | AnimationBase::Paused)) + return true; + + return rendererAnimations.computeExtentOfTransformAnimation(bounds); +} + +unsigned CSSAnimationControllerPrivate::numberOfActiveAnimations(Document* document) const +{ + unsigned count = 0; + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + count += animation.value->numberOfActiveAnimations(); + } + + return count; +} + +void CSSAnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) +{ + // Make sure this animation is not in the start time waiters + m_animationsWaitingForStartTimeResponse.remove(animation); + + m_animationsWaitingForStyle.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) +{ + m_animationsWaitingForStyle.remove(animationToRemove); +} + +void CSSAnimationControllerPrivate::styleAvailable() +{ + // Go through list of waiters and send them on their way + for (const auto& waitingAnimation : m_animationsWaitingForStyle) + waitingAnimation->styleAvailable(); + + m_animationsWaitingForStyle.clear(); +} + +void CSSAnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) +{ + // If willGetResponse is true, it means this animation is actually waiting for a response + // (which will come in as a call to notifyAnimationStarted()). + // In that case we don't need to add it to this list. We just set a waitingForAResponse flag + // which says we are waiting for the response. If willGetResponse is false, this animation + // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for + // another animation to which it will sync. + // + // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is + // true. If so, we just return and will do our work when the first notifyXXXStarted() call + // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will + // do our work right away. In both cases we call the onAnimationStartResponse() method + // on each animation. In the first case we send in the time we got from notifyXXXStarted(). + // In the second case, we just pass in the beginAnimationUpdateTime(). + // + // This will synchronize all software and accelerated animations started in the same + // updateStyleIfNeeded cycle. + // + + if (willGetResponse) + m_waitingForAsyncStartNotification = true; + + m_animationsWaitingForStartTimeResponse.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) +{ + m_animationsWaitingForStartTimeResponse.remove(animationToRemove); + + if (m_animationsWaitingForStartTimeResponse.isEmpty()) + m_waitingForAsyncStartNotification = false; +} + +void CSSAnimationControllerPrivate::startTimeResponse(double time) +{ + // Go through list of waiters and send them on their way + + for (const auto& animation : m_animationsWaitingForStartTimeResponse) + animation->onAnimationStartResponse(time); + + m_animationsWaitingForStartTimeResponse.clear(); + m_waitingForAsyncStartNotification = false; +} + +void CSSAnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p animationWillBeRemoved: %p", this, animation); + + removeFromAnimationsWaitingForStyle(animation); + removeFromAnimationsWaitingForStartTimeResponse(animation); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + removeFromAnimationsDependentOnScroll(animation); +#endif + + bool anyAnimationsWaitingForAsyncStart = false; + for (auto& animation : m_animationsWaitingForStartTimeResponse) { + if (animation->waitingForStartTime() && animation->isAccelerated()) { + anyAnimationsWaitingForAsyncStart = true; + break; + } + } + + if (!anyAnimationsWaitingForAsyncStart) + m_waitingForAsyncStartNotification = false; +} + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) +void CSSAnimationControllerPrivate::addToAnimationsDependentOnScroll(AnimationBase* animation) +{ + m_animationsDependentOnScroll.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsDependentOnScroll(AnimationBase* animation) +{ + m_animationsDependentOnScroll.remove(animation); +} + +void CSSAnimationControllerPrivate::scrollWasUpdated() +{ + auto* view = m_frame.view(); + if (!view || !wantsScrollUpdates()) + return; + + m_scrollPosition = view->scrollPositionForFixedPosition().y().toFloat(); + + // FIXME: This is updating all the animations, rather than just the ones + // that are dependent on scroll. We to go from our AnimationBase to its CompositeAnimation + // so we can execute code similar to updateAnimations. + // https://bugs.webkit.org/show_bug.cgi?id=144170 + updateAnimations(CallSetChanged); +} +#endif + +CSSAnimationController::CSSAnimationController(Frame& frame) + : m_data(std::make_unique<CSSAnimationControllerPrivate>(frame)) +{ +} + +CSSAnimationController::~CSSAnimationController() +{ +} + +void CSSAnimationController::cancelAnimations(RenderElement& renderer) +{ + if (!renderer.isCSSAnimating()) + return; + + if (!m_data->clear(renderer)) + return; + + Element* element = renderer.element(); + if (!element || element->document().renderTreeBeingDestroyed()) + return; + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); +} + +bool CSSAnimationController::updateAnimations(RenderElement& renderer, const RenderStyle& newStyle, std::unique_ptr<RenderStyle>& animatedStyle) +{ + auto* oldStyle = renderer.hasInitializedStyle() ? &renderer.style() : nullptr; + if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.animations() && !newStyle.transitions())) + return false; + + if (renderer.document().pageCacheState() != Document::NotInPageCache) + return false; + + // Don't run transitions when printing. + if (renderer.view().printing()) + return false; + + // Fetch our current set of implicit animations from a hashtable. We then compare them + // against the animations in the style and make sure we're in sync. If destination values + // have changed, we reset the animation. We then do a blend to get new values and we return + // a new style. + + // We don't support anonymous pseudo elements like :first-line or :first-letter. + ASSERT(renderer.element()); + + CompositeAnimation& rendererAnimations = m_data->ensureCompositeAnimation(renderer); + bool animationStateChanged = rendererAnimations.animate(renderer, oldStyle, newStyle, animatedStyle); + + if (renderer.parent() || newStyle.animations() || (oldStyle && oldStyle->animations())) { + auto& frameView = renderer.view().frameView(); + if (rendererAnimations.hasAnimationThatDependsOnLayout()) + m_data->setRequiresLayout(); + m_data->updateAnimationTimerForRenderer(renderer); + frameView.scheduleAnimation(); + } + + return animationStateChanged; +} + +std::unique_ptr<RenderStyle> CSSAnimationController::getAnimatedStyleForRenderer(RenderElement& renderer) +{ + if (!renderer.isCSSAnimating()) + return RenderStyle::clonePtr(renderer.style()); + return m_data->getAnimatedStyleForRenderer(renderer); +} + +bool CSSAnimationController::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const +{ + if (!renderer.isCSSAnimating()) + return true; + + return m_data->computeExtentOfAnimation(renderer, bounds); +} + +void CSSAnimationController::notifyAnimationStarted(RenderElement& renderer, double startTime) +{ + LOG(Animations, "CSSAnimationController %p notifyAnimationStarted on renderer %p, time=%f", this, &renderer, startTime); + UNUSED_PARAM(renderer); + + AnimationUpdateBlock animationUpdateBlock(this); + m_data->receivedStartTimeResponse(startTime); +} + +bool CSSAnimationController::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) +{ + AnimationUpdateBlock animationUpdateBlock(this); + return m_data->pauseAnimationAtTime(renderer, name, t); +} + +unsigned CSSAnimationController::numberOfActiveAnimations(Document* document) const +{ + return m_data->numberOfActiveAnimations(document); +} + +bool CSSAnimationController::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) +{ + AnimationUpdateBlock animationUpdateBlock(this); + return m_data->pauseTransitionAtTime(renderer, property, t); +} + +bool CSSAnimationController::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + return renderer.isCSSAnimating() && m_data->isRunningAnimationOnRenderer(renderer, property, runningState); +} + +bool CSSAnimationController::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + return renderer.isCSSAnimating() && m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, runningState); +} + +bool CSSAnimationController::isSuspended() const +{ + return m_data->isSuspended(); +} + +void CSSAnimationController::suspendAnimations() +{ + LOG(Animations, "controller is suspending animations"); + m_data->suspendAnimations(); +} + +void CSSAnimationController::resumeAnimations() +{ + LOG(Animations, "controller is resuming animations"); + m_data->resumeAnimations(); +} + +bool CSSAnimationController::allowsNewAnimationsWhileSuspended() const +{ + return m_data->allowsNewAnimationsWhileSuspended(); +} + +void CSSAnimationController::setAllowsNewAnimationsWhileSuspended(bool allowed) +{ + m_data->setAllowsNewAnimationsWhileSuspended(allowed); +} + +void CSSAnimationController::serviceAnimations() +{ + m_data->animationFrameCallbackFired(); +} + +bool CSSAnimationController::animationsAreSuspendedForDocument(Document* document) +{ + return m_data->animationsAreSuspendedForDocument(document); +} + +void CSSAnimationController::detachFromDocument(Document* document) +{ + return m_data->detachFromDocument(document); +} + +void CSSAnimationController::suspendAnimationsForDocument(Document* document) +{ + LOG(Animations, "suspending animations for document %p", document); + m_data->suspendAnimationsForDocument(document); +} + +void CSSAnimationController::resumeAnimationsForDocument(Document* document) +{ + LOG(Animations, "resuming animations for document %p", document); + AnimationUpdateBlock animationUpdateBlock(this); + m_data->resumeAnimationsForDocument(document); +} + +void CSSAnimationController::startAnimationsIfNotSuspended(Document* document) +{ + LOG(Animations, "animations may start for document %p", document); + + AnimationUpdateBlock animationUpdateBlock(this); + m_data->startAnimationsIfNotSuspended(document); +} + +void CSSAnimationController::beginAnimationUpdate() +{ + m_data->beginAnimationUpdate(); +} + +void CSSAnimationController::endAnimationUpdate() +{ + m_data->endAnimationUpdate(); +} + +bool CSSAnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) +{ + return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); +} + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) +bool CSSAnimationController::wantsScrollUpdates() const +{ + return m_data->wantsScrollUpdates(); +} + +void CSSAnimationController::scrollWasUpdated() +{ + m_data->scrollWasUpdated(); +} +#endif + +bool CSSAnimationController::hasAnimations() const +{ + return m_data->hasAnimations(); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/animation/AnimationController.h b/Source/WebCore/page/animation/CSSAnimationController.h index c29af5d82..d473cd173 100644 --- a/Source/WebCore/page/animation/AnimationController.h +++ b/Source/WebCore/page/animation/CSSAnimationController.h @@ -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. * @@ -26,69 +26,80 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AnimationController_h -#define AnimationController_h +#pragma once +#include "AnimationBase.h" #include "CSSPropertyNames.h" #include <wtf/Forward.h> -#include <wtf/OwnPtr.h> namespace WebCore { -class AnimationBase; -class AnimationControllerPrivate; +class CSSAnimationControllerPrivate; class Document; class Element; class Frame; +class LayoutRect; class RenderElement; class RenderStyle; -class AnimationController { +class CSSAnimationController { + WTF_MAKE_FAST_ALLOCATED; public: - explicit AnimationController(Frame&); - ~AnimationController(); + explicit CSSAnimationController(Frame&); + ~CSSAnimationController(); - void cancelAnimations(RenderElement*); - PassRef<RenderStyle> updateAnimations(RenderElement&, PassRef<RenderStyle> newStyle); - PassRefPtr<RenderStyle> getAnimatedStyleForRenderer(RenderElement*); + void cancelAnimations(RenderElement&); + bool updateAnimations(RenderElement&, const RenderStyle& newStyle, std::unique_ptr<RenderStyle>& animatedStyle); + std::unique_ptr<RenderStyle> getAnimatedStyleForRenderer(RenderElement&); + + // If possible, compute the visual extent of any transform animation on the given renderer + // using the given rect, returning the result in the rect. Return false if there is some + // transform animation but we were unable to cheaply compute its affect on the extent. + bool computeExtentOfAnimation(RenderElement&, LayoutRect&) const; // This is called when an accelerated animation or transition has actually started to animate. - void notifyAnimationStarted(RenderElement*, double startTime); + void notifyAnimationStarted(RenderElement&, double startTime); - bool pauseAnimationAtTime(RenderElement*, const AtomicString& name, double t); // To be used only for testing - bool pauseTransitionAtTime(RenderElement*, const String& property, double t); // To be used only for testing - unsigned numberOfActiveAnimations(Document*) const; // To be used only for testing + WEBCORE_EXPORT bool pauseAnimationAtTime(RenderElement*, const AtomicString& name, double t); // To be used only for testing + WEBCORE_EXPORT bool pauseTransitionAtTime(RenderElement*, const String& property, double t); // To be used only for testing + WEBCORE_EXPORT unsigned numberOfActiveAnimations(Document*) const; // To be used only for testing - bool isRunningAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow = true) const; - bool isRunningAcceleratedAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow = true) const; + bool isRunningAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; + bool isRunningAcceleratedAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; - bool isSuspended() const; - void suspendAnimations(); - void resumeAnimations(); -#if ENABLE(REQUEST_ANIMATION_FRAME) + WEBCORE_EXPORT bool isSuspended() const; + WEBCORE_EXPORT void suspendAnimations(); + WEBCORE_EXPORT void resumeAnimations(); void serviceAnimations(); -#endif - void suspendAnimationsForDocument(Document*); - void resumeAnimationsForDocument(Document*); + WEBCORE_EXPORT void suspendAnimationsForDocument(Document*); + WEBCORE_EXPORT void resumeAnimationsForDocument(Document*); + WEBCORE_EXPORT bool animationsAreSuspendedForDocument(Document*); + void detachFromDocument(Document*); void startAnimationsIfNotSuspended(Document*); void beginAnimationUpdate(); void endAnimationUpdate(); - bool allowsNewAnimationsWhileSuspended() const; - void setAllowsNewAnimationsWhileSuspended(bool); + WEBCORE_EXPORT bool allowsNewAnimationsWhileSuspended() const; + WEBCORE_EXPORT void setAllowsNewAnimationsWhileSuspended(bool); static bool supportsAcceleratedAnimationOfProperty(CSSPropertyID); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool wantsScrollUpdates() const; + void scrollWasUpdated(); +#endif + + bool hasAnimations() const; + private: - const std::unique_ptr<AnimationControllerPrivate> m_data; - int m_beginAnimationUpdateCount; + const std::unique_ptr<CSSAnimationControllerPrivate> m_data; }; class AnimationUpdateBlock { public: - AnimationUpdateBlock(AnimationController* animationController) + AnimationUpdateBlock(CSSAnimationController* animationController) : m_animationController(animationController) { if (m_animationController) @@ -101,9 +112,7 @@ public: m_animationController->endAnimationUpdate(); } - AnimationController* m_animationController; + CSSAnimationController* m_animationController; }; } // namespace WebCore - -#endif // AnimationController_h diff --git a/Source/WebCore/page/animation/AnimationControllerPrivate.h b/Source/WebCore/page/animation/CSSAnimationControllerPrivate.h index f03092280..c4996c331 100644 --- a/Source/WebCore/page/animation/AnimationControllerPrivate.h +++ b/Source/WebCore/page/animation/CSSAnimationControllerPrivate.h @@ -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. * @@ -26,76 +26,68 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AnimationControllerPrivate_h -#define AnimationControllerPrivate_h +#pragma once -#include "CSSPropertyNames.h" +#include "AnimationBase.h" #include "Timer.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> #include <wtf/Vector.h> -#include <wtf/text/AtomicString.h> -#include <wtf/text/WTFString.h> namespace WebCore { -class AnimationBase; class CompositeAnimation; class Document; -class Element; class Frame; -class RenderElement; -class RenderStyle; -enum SetChanged { - DoNotCallSetChanged = 0, - CallSetChanged = 1 -}; +enum SetChanged { DoNotCallSetChanged, CallSetChanged }; -class AnimationControllerPrivate { - WTF_MAKE_NONCOPYABLE(AnimationControllerPrivate); WTF_MAKE_FAST_ALLOCATED; +class CSSAnimationControllerPrivate { + WTF_MAKE_FAST_ALLOCATED; public: - explicit AnimationControllerPrivate(Frame&); - ~AnimationControllerPrivate(); + explicit CSSAnimationControllerPrivate(Frame&); + ~CSSAnimationControllerPrivate(); // Returns the time until the next animation needs to be serviced, or -1 if there are none. double updateAnimations(SetChanged callSetChanged = DoNotCallSetChanged); void updateAnimationTimer(SetChanged callSetChanged = DoNotCallSetChanged); - CompositeAnimation& ensureCompositeAnimation(RenderElement*); - bool clear(RenderElement*); + CompositeAnimation& ensureCompositeAnimation(RenderElement&); + bool clear(RenderElement&); - void updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>&); + void updateStyleIfNeededDispatcherFired(); void startUpdateStyleIfNeededDispatcher(); - void addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime); - void addElementChangeToDispatch(PassRef<Element>); + void addEventToDispatch(Element&, const AtomicString& eventType, const String& name, double elapsedTime); + void addElementChangeToDispatch(Element&); bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); } bool isSuspended() const { return m_isSuspended; } void suspendAnimations(); void resumeAnimations(); -#if ENABLE(REQUEST_ANIMATION_FRAME) void animationFrameCallbackFired(); -#endif void suspendAnimationsForDocument(Document*); void resumeAnimationsForDocument(Document*); + bool animationsAreSuspendedForDocument(Document*); void startAnimationsIfNotSuspended(Document*); + void detachFromDocument(Document*); - bool isRunningAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow) const; - bool isRunningAcceleratedAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow) const; + bool isRunningAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; + bool isRunningAcceleratedAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; bool pauseAnimationAtTime(RenderElement*, const AtomicString& name, double t); bool pauseTransitionAtTime(RenderElement*, const String& property, double t); unsigned numberOfActiveAnimations(Document*) const; - PassRefPtr<RenderStyle> getAnimatedStyleForRenderer(RenderElement* renderer); + std::unique_ptr<RenderStyle> getAnimatedStyleForRenderer(RenderElement&); + + bool computeExtentOfAnimation(RenderElement&, LayoutRect&) const; double beginAnimationUpdateTime(); void setBeginAnimationUpdateTime(double t) { m_beginAnimationUpdateTime = t; } + + void beginAnimationUpdate(); void endAnimationUpdate(); void receivedStartTimeResponse(double); @@ -107,48 +99,65 @@ public: void animationWillBeRemoved(AnimationBase*); - void updateAnimationTimerForRenderer(RenderElement*); + void updateAnimationTimerForRenderer(RenderElement&); bool allowsNewAnimationsWhileSuspended() const { return m_allowsNewAnimationsWhileSuspended; } void setAllowsNewAnimationsWhileSuspended(bool); + void setRequiresLayout() { m_requiresLayout = true; } + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool wantsScrollUpdates() const { return !m_animationsDependentOnScroll.isEmpty(); } + void addToAnimationsDependentOnScroll(AnimationBase*); + void removeFromAnimationsDependentOnScroll(AnimationBase*); + + void scrollWasUpdated(); + float scrollPosition() const { return m_scrollPosition; } +#endif + private: - void animationTimerFired(Timer<AnimationControllerPrivate>&); + void animationTimerFired(); void styleAvailable(); void fireEventsAndUpdateStyle(); void startTimeResponse(double t); HashMap<RenderElement*, RefPtr<CompositeAnimation>> m_compositeAnimations; - Timer<AnimationControllerPrivate> m_animationTimer; - Timer<AnimationControllerPrivate> m_updateStyleIfNeededDispatcher; + Timer m_animationTimer; + Timer m_updateStyleIfNeededDispatcher; Frame& m_frame; - - class EventToDispatch { - public: - RefPtr<Element> element; + + struct EventToDispatch { + Ref<Element> element; AtomicString eventType; String name; double elapsedTime; }; - Vector<EventToDispatch> m_eventsToDispatch; Vector<Ref<Element>> m_elementChangesToDispatch; - + HashSet<Document*> m_suspendedDocuments; + double m_beginAnimationUpdateTime; - typedef HashSet<RefPtr<AnimationBase>> WaitingAnimationsSet; - WaitingAnimationsSet m_animationsWaitingForStyle; - WaitingAnimationsSet m_animationsWaitingForStartTimeResponse; + using AnimationsSet = HashSet<RefPtr<AnimationBase>>; + AnimationsSet m_animationsWaitingForStyle; + AnimationsSet m_animationsWaitingForStartTimeResponse; + + int m_beginAnimationUpdateCount; + bool m_waitingForAsyncStartNotification; bool m_isSuspended; + bool m_requiresLayout { false }; // Used to flag whether we should revert to previous buggy // behavior of allowing new transitions and animations to // run even when this object is suspended. bool m_allowsNewAnimationsWhileSuspended; + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + AnimationsSet m_animationsDependentOnScroll; + float m_scrollPosition { 0 }; +#endif }; } // namespace WebCore - -#endif // AnimationControllerPrivate_h diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp index 291129e93..98b87c947 100644 --- a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp +++ b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2013, 2016 Apple Inc. All rights reserved. * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,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. * @@ -41,7 +41,9 @@ #include "CachedImage.h" #include "ClipPathOperation.h" #include "FloatConversion.h" +#include "FontTaggedSettings.h" #include "IdentityTransformOperation.h" +#include "Logging.h" #include "Matrix3DTransformOperation.h" #include "MatrixTransformOperation.h" #include "RenderBox.h" @@ -50,9 +52,13 @@ #include "StyleGeneratedImage.h" #include "StylePropertyShorthand.h" #include "StyleResolver.h" +#include "TextStream.h" #include <algorithm> +#include <memory> #include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Noncopyable.h> +#include <wtf/PointerComparison.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -62,11 +68,6 @@ static inline int blendFunc(const AnimationBase*, int from, int to, double progr return blend(from, to, progress); } -static inline unsigned blendFunc(const AnimationBase*, unsigned from, unsigned to, double progress) -{ - return blend(from, to, progress); -} - static inline double blendFunc(const AnimationBase*, double from, double to, double progress) { return blend(from, to, progress); @@ -84,13 +85,12 @@ static inline Color blendFunc(const AnimationBase*, const Color& from, const Col static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress) { - return to.blend(from, narrowPrecisionToFloat(progress)); + return blend(from, to, progress); } static inline LengthSize blendFunc(const AnimationBase* anim, const LengthSize& from, const LengthSize& to, double progress) { - return LengthSize(blendFunc(anim, from.width(), to.width(), progress), - blendFunc(anim, from.height(), to.height(), progress)); + return { blendFunc(anim, from.width, to.width, progress), blendFunc(anim, from.height, to.height, progress) }; } static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, ShadowStyle to, double progress) @@ -104,28 +104,28 @@ static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, return result > 0 ? Normal : Inset; } -static inline PassOwnPtr<ShadowData> blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress) +static inline std::unique_ptr<ShadowData> blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress) { ASSERT(from && to); if (from->style() != to->style()) - return adoptPtr(new ShadowData(*to)); - - return adoptPtr(new ShadowData(blend(from->location(), to->location(), progress), - blend(from->radius(), to->radius(), progress), - blend(from->spread(), to->spread(), progress), - blendFunc(anim, from->style(), to->style(), progress), - from->isWebkitBoxShadow(), - blend(from->color(), to->color(), progress))); + return std::make_unique<ShadowData>(*to); + + return std::make_unique<ShadowData>(blend(from->location(), to->location(), progress), + blend(from->radius(), to->radius(), progress), + blend(from->spread(), to->spread(), progress), + blendFunc(anim, from->style(), to->style(), progress), + from->isWebkitBoxShadow(), + blend(from->color(), to->color(), progress)); } -static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress) +static inline TransformOperations blendFunc(const AnimationBase* animation, const TransformOperations& from, const TransformOperations& to, double progress) { - if (anim->isTransformFunctionListValid()) + if (animation->transformFunctionListsMatch()) return to.blendByMatchingOperations(from, progress); - return to.blendByUsingMatrixInterpolation(from, progress, anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize()); + return to.blendByUsingMatrixInterpolation(from, progress, is<RenderBox>(*animation->renderer()) ? downcast<RenderBox>(*animation->renderer()).borderBoxRect().size() : LayoutSize()); } -static inline PassRefPtr<ClipPathOperation> blendFunc(const AnimationBase*, ClipPathOperation* from, ClipPathOperation* to, double progress) +static inline RefPtr<ClipPathOperation> blendFunc(const AnimationBase*, ClipPathOperation* from, ClipPathOperation* to, double progress) { if (!from || !to) return to; @@ -134,44 +134,40 @@ static inline PassRefPtr<ClipPathOperation> blendFunc(const AnimationBase*, Clip if (from->type() != ClipPathOperation::Shape || to->type() != ClipPathOperation::Shape) return to; - const BasicShape* fromShape = static_cast<ShapeClipPathOperation*>(from)->basicShape(); - const BasicShape* toShape = static_cast<ShapeClipPathOperation*>(to)->basicShape(); + const BasicShape& fromShape = downcast<ShapeClipPathOperation>(*from).basicShape(); + const BasicShape& toShape = downcast<ShapeClipPathOperation>(*to).basicShape(); - if (!fromShape->canBlend(toShape)) + if (!fromShape.canBlend(toShape)) return to; - return ShapeClipPathOperation::create(toShape->blend(fromShape, progress)); + return ShapeClipPathOperation::create(toShape.blend(fromShape, progress)); } -#if ENABLE(CSS_SHAPES) -static inline PassRefPtr<ShapeValue> blendFunc(const AnimationBase*, ShapeValue* from, ShapeValue* to, double progress) +static inline RefPtr<ShapeValue> blendFunc(const AnimationBase*, ShapeValue* from, ShapeValue* to, double progress) { if (!from || !to) return to; - // FIXME Bug 102723: Shape-inside should be able to animate a value of 'outside-shape' when shape-outside is set to a BasicShape - if (from->type() != ShapeValue::Shape || to->type() != ShapeValue::Shape) + if (from->type() != ShapeValue::Type::Shape || to->type() != ShapeValue::Type::Shape) return to; - if (from->layoutBox() != to->layoutBox()) + if (from->cssBox() != to->cssBox()) return to; - const BasicShape* fromShape = from->shape(); - const BasicShape* toShape = to->shape(); + const BasicShape& fromShape = *from->shape(); + const BasicShape& toShape = *to->shape(); - if (!fromShape->canBlend(toShape)) + if (!fromShape.canBlend(toShape)) return to; - return ShapeValue::createShapeValue(toShape->blend(fromShape, progress), to->layoutBox()); + return ShapeValue::create(toShape.blend(fromShape, progress), to->cssBox()); } -#endif -#if ENABLE(CSS_FILTERS) -static inline PassRefPtr<FilterOperation> blendFunc(const AnimationBase* anim, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false) +static inline RefPtr<FilterOperation> blendFunc(const AnimationBase* animation, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false) { ASSERT(toOp); if (toOp->blendingNeedsRendererSize()) { - LayoutSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize(); + LayoutSize size = is<RenderBox>(*animation->renderer()) ? downcast<RenderBox>(*animation->renderer()).borderBoxRect().size() : LayoutSize(); return toOp->blend(fromOp, progress, size, blendToPassthrough); } return toOp->blend(fromOp, progress, blendToPassthrough); @@ -200,12 +196,18 @@ static inline FilterOperations blendFilterOperations(const AnimationBase* anim, return result; } -static inline FilterOperations blendFunc(const AnimationBase* anim, const FilterOperations& from, const FilterOperations& to, double progress) +static inline FilterOperations blendFunc(const AnimationBase* anim, const FilterOperations& from, const FilterOperations& to, double progress, bool animatingBackdropFilter = false) { FilterOperations result; // If we have a filter function list, use that to do a per-function animation. +#if ENABLE(FILTERS_LEVEL_2) + if ((!animatingBackdropFilter && anim->filterFunctionListsMatch()) || (animatingBackdropFilter && anim->backdropFilterFunctionListsMatch())) +#else + UNUSED_PARAM(animatingBackdropFilter); if (anim->filterFunctionListsMatch()) +#endif + result = blendFilterOperations(anim, from, to, progress); else { // If the filter function lists don't match, we could try to cross-fade, but don't yet have a way to represent that in CSS. @@ -216,20 +218,18 @@ static inline FilterOperations blendFunc(const AnimationBase* anim, const Filter return result; } -static inline PassRefPtr<StyleImage> blendFilter(const AnimationBase* anim, CachedImage* image, const FilterOperations& from, const FilterOperations& to, double progress) +static inline RefPtr<StyleImage> blendFilter(const AnimationBase* anim, CachedImage* image, const FilterOperations& from, const FilterOperations& to, double progress) { ASSERT(image); FilterOperations filterResult = blendFilterOperations(anim, from, to, progress); - RefPtr<StyleCachedImage> styledImage = StyleCachedImage::create(image); - auto imageValue = CSSImageValue::create(image->url(), styledImage.get()); - auto filterValue = ComputedStyleExtractor::valueForFilter(&anim->renderer()->style(), filterResult, DoNotAdjustPixelValues); + auto imageValue = CSSImageValue::create(*image); + auto filterValue = ComputedStyleExtractor::valueForFilter(anim->renderer()->style(), filterResult, DoNotAdjustPixelValues); - auto result = CSSFilterImageValue::create(std::move(imageValue), std::move(filterValue)); + auto result = CSSFilterImageValue::create(WTFMove(imageValue), WTFMove(filterValue)); result.get().setFilterOperations(filterResult); - return StyleGeneratedImage::create(std::move(result)); + return StyleGeneratedImage::create(WTFMove(result)); } -#endif // ENABLE(CSS_FILTERS) static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress) { @@ -252,12 +252,12 @@ static inline LengthBox blendFunc(const AnimationBase* anim, const LengthBox& fr return result; } -#if ENABLE(SVG) -static inline SVGLength blendFunc(const AnimationBase*, const SVGLength& from, const SVGLength& to, double progress) +static inline SVGLengthValue blendFunc(const AnimationBase*, const SVGLengthValue& from, const SVGLengthValue& to, double progress) { return to.blend(from, narrowPrecisionToFloat(progress)); } -static inline Vector<SVGLength> blendFunc(const AnimationBase*, const Vector<SVGLength>& from, const Vector<SVGLength>& to, double progress) + +static inline Vector<SVGLengthValue> blendFunc(const AnimationBase*, const Vector<SVGLengthValue>& from, const Vector<SVGLengthValue>& to, double progress) { size_t fromLength = from.size(); size_t toLength = to.size(); @@ -272,14 +272,13 @@ static inline Vector<SVGLength> blendFunc(const AnimationBase*, const Vector<SVG else resultLength = fromLength * toLength; } - Vector<SVGLength> result(resultLength); + Vector<SVGLengthValue> result(resultLength); for (size_t i = 0; i < resultLength; ++i) result[i] = to[i % toLength].blend(from[i % fromLength], narrowPrecisionToFloat(progress)); return result; } -#endif -static inline PassRefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleCachedImage* fromStyleImage, StyleCachedImage* toStyleImage, double progress) +static inline RefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleCachedImage* fromStyleImage, StyleCachedImage* toStyleImage, double progress) { // If progress is at one of the extremes, we want getComputedStyle to show the image, // not a completed cross-fade, so we hand back one of the existing images. @@ -287,69 +286,69 @@ static inline PassRefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleC return fromStyleImage; if (progress == 1) return toStyleImage; + if (!fromStyleImage->cachedImage() || !toStyleImage->cachedImage()) + return toStyleImage; - auto fromImageValue = CSSImageValue::create(fromStyleImage->cachedImage()->url(), fromStyleImage); - auto toImageValue = CSSImageValue::create(toStyleImage->cachedImage()->url(), toStyleImage); + auto fromImageValue = CSSImageValue::create(*fromStyleImage->cachedImage()); + auto toImageValue = CSSImageValue::create(*toStyleImage->cachedImage()); + auto percentageValue = CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER); - auto crossfadeValue = CSSCrossfadeValue::create(std::move(fromImageValue), std::move(toImageValue)); - crossfadeValue.get().setPercentage(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER)); - return StyleGeneratedImage::create(std::move(crossfadeValue)); + auto crossfadeValue = CSSCrossfadeValue::create(WTFMove(fromImageValue), WTFMove(toImageValue), WTFMove(percentageValue)); + return StyleGeneratedImage::create(WTFMove(crossfadeValue)); } -static inline PassRefPtr<StyleImage> blendFunc(const AnimationBase* anim, StyleImage* from, StyleImage* to, double progress) +static inline RefPtr<StyleImage> blendFunc(const AnimationBase* anim, StyleImage* from, StyleImage* to, double progress) { if (!from || !to) return to; // Animation between two generated images. Cross fade for all other cases. - if (from->isGeneratedImage() && to->isGeneratedImage()) { - CSSImageGeneratorValue& fromGenerated = toStyleGeneratedImage(from)->imageValue(); - CSSImageGeneratorValue& toGenerated = toStyleGeneratedImage(to)->imageValue(); + if (is<StyleGeneratedImage>(*from) && is<StyleGeneratedImage>(*to)) { + CSSImageGeneratorValue& fromGenerated = downcast<StyleGeneratedImage>(*from).imageValue(); + CSSImageGeneratorValue& toGenerated = downcast<StyleGeneratedImage>(*to).imageValue(); -#if ENABLE(CSS_FILTERS) - if (fromGenerated.isFilterImageValue() && toGenerated.isFilterImageValue()) { + if (is<CSSFilterImageValue>(fromGenerated) && is<CSSFilterImageValue>(toGenerated)) { // Animation of generated images just possible if input images are equal. // Otherwise fall back to cross fade animation. - CSSFilterImageValue& fromFilter = toCSSFilterImageValue(fromGenerated); - CSSFilterImageValue& toFilter = toCSSFilterImageValue(toGenerated); + CSSFilterImageValue& fromFilter = downcast<CSSFilterImageValue>(fromGenerated); + CSSFilterImageValue& toFilter = downcast<CSSFilterImageValue>(toGenerated); if (fromFilter.equalInputImages(toFilter) && fromFilter.cachedImage()) return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), toFilter.filterOperations(), progress); } -#endif - if (fromGenerated.isCrossfadeValue() && toGenerated.isCrossfadeValue()) { - CSSCrossfadeValue& fromCrossfade = toCSSCrossfadeValue(fromGenerated); - CSSCrossfadeValue& toCrossfade = toCSSCrossfadeValue(toGenerated); - if (fromCrossfade.equalInputImages(toCrossfade)) - return StyleGeneratedImage::create(*toCrossfade.blend(fromCrossfade, progress)); + if (is<CSSCrossfadeValue>(fromGenerated) && is<CSSCrossfadeValue>(toGenerated)) { + CSSCrossfadeValue& fromCrossfade = downcast<CSSCrossfadeValue>(fromGenerated); + CSSCrossfadeValue& toCrossfade = downcast<CSSCrossfadeValue>(toGenerated); + if (fromCrossfade.equalInputImages(toCrossfade)) { + if (auto crossfadeBlend = toCrossfade.blend(fromCrossfade, progress)) + return StyleGeneratedImage::create(*crossfadeBlend); + } } // FIXME: Add support for animation between two *gradient() functions. // https://bugs.webkit.org/show_bug.cgi?id=119956 -#if ENABLE(CSS_FILTERS) - } else if (from->isGeneratedImage() && to->isCachedImage()) { - CSSImageGeneratorValue& fromGenerated = toStyleGeneratedImage(from)->imageValue(); - if (fromGenerated.isFilterImageValue()) { - CSSFilterImageValue& fromFilter = toCSSFilterImageValue(fromGenerated); - if (fromFilter.cachedImage() && static_cast<StyleCachedImage*>(to)->cachedImage() == fromFilter.cachedImage()) + } else if (is<StyleGeneratedImage>(*from) && is<StyleCachedImage>(*to)) { + CSSImageGeneratorValue& fromGenerated = downcast<StyleGeneratedImage>(*from).imageValue(); + if (is<CSSFilterImageValue>(fromGenerated)) { + CSSFilterImageValue& fromFilter = downcast<CSSFilterImageValue>(fromGenerated); + if (fromFilter.cachedImage() && downcast<StyleCachedImage>(*to).cachedImage() == fromFilter.cachedImage()) return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), FilterOperations(), progress); } // FIXME: Add interpolation between cross-fade and image source. - } else if (from->isCachedImage() && to->isGeneratedImage()) { - CSSImageGeneratorValue& toGenerated = toStyleGeneratedImage(to)->imageValue(); - if (toGenerated.isFilterImageValue()) { - CSSFilterImageValue& toFilter = toCSSFilterImageValue(toGenerated); - if (toFilter.cachedImage() && static_cast<StyleCachedImage*>(from)->cachedImage() == toFilter.cachedImage()) + } else if (is<StyleCachedImage>(*from) && is<StyleGeneratedImage>(*to)) { + CSSImageGeneratorValue& toGenerated = downcast<StyleGeneratedImage>(*to).imageValue(); + if (is<CSSFilterImageValue>(toGenerated)) { + CSSFilterImageValue& toFilter = downcast<CSSFilterImageValue>(toGenerated); + if (toFilter.cachedImage() && downcast<StyleCachedImage>(*from).cachedImage() == toFilter.cachedImage()) return blendFilter(anim, toFilter.cachedImage(), FilterOperations(), toFilter.filterOperations(), progress); } -#endif // FIXME: Add interpolation between image source and cross-fade. } // FIXME: Add support cross fade between cached and generated images. // https://bugs.webkit.org/show_bug.cgi?id=78293 - if (from->isCachedImage() && to->isCachedImage()) - return crossfadeBlend(anim, static_cast<StyleCachedImage*>(from), static_cast<StyleCachedImage*>(to), progress); + if (is<StyleCachedImage>(*from) && is<StyleCachedImage>(*to)) + return crossfadeBlend(anim, downcast<StyleCachedImage>(from), downcast<StyleCachedImage>(to), progress); return to; } @@ -367,11 +366,31 @@ static inline NinePieceImage blendFunc(const AnimationBase* anim, const NinePiec if (from.image()->imageSize(anim->renderer(), 1.0) != to.image()->imageSize(anim->renderer(), 1.0)) return to; - RefPtr<StyleImage> newContentImage = blendFunc(anim, from.image(), to.image(), progress); + return NinePieceImage(blendFunc(anim, from.image(), to.image(), progress), + from.imageSlices(), from.fill(), from.borderSlices(), from.outset(), from.horizontalRule(), from.verticalRule()); +} + +#if ENABLE(VARIATION_FONTS) - return NinePieceImage(newContentImage, from.imageSlices(), from.fill(), from.borderSlices(), from.outset(), from.horizontalRule(), from.verticalRule()); +static inline FontVariationSettings blendFunc(const AnimationBase* anim, const FontVariationSettings& from, const FontVariationSettings& to, double progress) +{ + if (from.size() != to.size()) + return FontVariationSettings(); + FontVariationSettings result; + unsigned size = from.size(); + for (unsigned i = 0; i < size; ++i) { + auto& fromItem = from.at(i); + auto& toItem = to.at(i); + if (fromItem.tag() != toItem.tag()) + return FontVariationSettings(); + float interpolated = blendFunc(anim, fromItem.value(), toItem.value(), progress); + result.insert({ fromItem.tag(), interpolated }); + } + return result; } +#endif + class AnimationPropertyWrapperBase { WTF_MAKE_NONCOPYABLE(AnimationPropertyWrapperBase); WTF_MAKE_FAST_ALLOCATED; @@ -386,12 +405,14 @@ public: virtual bool isShorthandWrapper() const { return false; } virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0; virtual void blend(const AnimationBase*, RenderStyle*, const RenderStyle*, const RenderStyle*, double) const = 0; + +#if !LOG_DISABLED + virtual void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double) const = 0; +#endif CSSPropertyID property() const { return m_prop; } -#if USE(ACCELERATED_COMPOSITING) virtual bool animationIsAccelerated() const { return false; } -#endif private: CSSPropertyID m_prop; @@ -399,6 +420,7 @@ private: template <typename T> class PropertyWrapperGetter : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperGetter(CSSPropertyID prop, T (RenderStyle::*getter)() const) : AnimationPropertyWrapperBase(prop) @@ -406,23 +428,34 @@ public: { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. - if ((!a && !b) || a == b) + if (a == b) return true; if (!a || !b) return false; return (a->*m_getter)() == (b->*m_getter)(); } + T value(const RenderStyle* a) const + { + return (a->*m_getter)(); + } + +#if !LOG_DISABLED + void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final + { + LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result)); + } +#endif + protected: T (RenderStyle::*m_getter)() const; }; template <typename T> class PropertyWrapper : public PropertyWrapperGetter<T> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapper(CSSPropertyID prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) : PropertyWrapperGetter<T>(prop, getter) @@ -430,7 +463,7 @@ public: { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress)); } @@ -441,69 +474,131 @@ protected: template <typename T> class RefCountedPropertyWrapper : public PropertyWrapperGetter<T*> { + WTF_MAKE_FAST_ALLOCATED; public: - RefCountedPropertyWrapper(CSSPropertyID prop, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<T>)) + RefCountedPropertyWrapper(CSSPropertyID prop, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<T>&&)) : PropertyWrapperGetter<T*>(prop, getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T*>::m_getter)(), (b->*PropertyWrapperGetter<T*>::m_getter)(), progress)); } protected: - void (RenderStyle::*m_setter)(PassRefPtr<T>); + void (RenderStyle::*m_setter)(RefPtr<T>&&); }; template <typename T> class LengthPropertyWrapper : public PropertyWrapperGetter<const T&> { + WTF_MAKE_FAST_ALLOCATED; public: - LengthPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) + LengthPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) : PropertyWrapperGetter<const T&>(prop, getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const T&>::m_getter)(), (b->*PropertyWrapperGetter<const T&>::m_getter)(), progress)); } protected: - void (RenderStyle::*m_setter)(T); + void (RenderStyle::*m_setter)(T&&); }; class PropertyWrapperClipPath : public RefCountedPropertyWrapper<ClipPathOperation> { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperClipPath(CSSPropertyID prop, ClipPathOperation* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<ClipPathOperation>)) + PropertyWrapperClipPath(CSSPropertyID prop, ClipPathOperation* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<ClipPathOperation>&&)) : RefCountedPropertyWrapper<ClipPathOperation>(prop, getter, setter) { } + + bool equals(const RenderStyle* a, const RenderStyle* b) const override + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (a == b) + return true; + if (!a || !b) + return false; + + ClipPathOperation* clipPathA = (a->*m_getter)(); + ClipPathOperation* clipPathB = (b->*m_getter)(); + if (clipPathA == clipPathB) + return true; + if (!clipPathA || !clipPathB) + return false; + return *clipPathA == *clipPathB; + } +}; + +#if ENABLE(VARIATION_FONTS) +class PropertyWrapperFontVariationSettings : public PropertyWrapper<FontVariationSettings> { + WTF_MAKE_FAST_ALLOCATED; +public: + PropertyWrapperFontVariationSettings(CSSPropertyID prop, FontVariationSettings (RenderStyle::*getter)() const, void (RenderStyle::*setter)(FontVariationSettings)) + : PropertyWrapper<FontVariationSettings>(prop, getter, setter) + { + } + + bool equals(const RenderStyle* a, const RenderStyle* b) const override + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (a == b) + return true; + if (!a || !b) + return false; + + const FontVariationSettings& variationSettingsA = (a->*m_getter)(); + const FontVariationSettings& variationSettingsB = (b->*m_getter)(); + return variationSettingsA == variationSettingsB; + } }; +#endif -#if ENABLE(CSS_SHAPES) class PropertyWrapperShape : public RefCountedPropertyWrapper<ShapeValue> { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperShape(CSSPropertyID prop, ShapeValue* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<ShapeValue>)) + PropertyWrapperShape(CSSPropertyID prop, ShapeValue* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<ShapeValue>&&)) : RefCountedPropertyWrapper<ShapeValue>(prop, getter, setter) { } + + bool equals(const RenderStyle* a, const RenderStyle* b) const override + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (a == b) + return true; + if (!a || !b) + return false; + + ShapeValue* shapeA = (a->*m_getter)(); + ShapeValue* shapeB = (b->*m_getter)(); + if (shapeA == shapeB) + return true; + if (!shapeA || !shapeB) + return false; + return *shapeA == *shapeB; + } }; -#endif class StyleImagePropertyWrapper : public RefCountedPropertyWrapper<StyleImage> { + WTF_MAKE_FAST_ALLOCATED; public: - StyleImagePropertyWrapper(CSSPropertyID prop, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<StyleImage>)) + StyleImagePropertyWrapper(CSSPropertyID prop, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<StyleImage>&&)) : RefCountedPropertyWrapper<StyleImage>(prop, getter, setter) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. if (a == b) return true; if (!a || !b) @@ -511,67 +606,82 @@ public: StyleImage* imageA = (a->*m_getter)(); StyleImage* imageB = (b->*m_getter)(); - return StyleImage::imagesEquivalent(imageA, imageB); + return arePointingToEqualData(imageA, imageB); } }; -class PropertyWrapperColor : public PropertyWrapperGetter<Color> { +class PropertyWrapperColor : public PropertyWrapperGetter<const Color&> { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) - : PropertyWrapperGetter<Color>(prop, getter) + PropertyWrapperColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + : PropertyWrapperGetter<const Color&>(prop, getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<Color>::m_getter)(), (b->*PropertyWrapperGetter<Color>::m_getter)(), progress)); + (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const Color&>::m_getter)(), (b->*PropertyWrapperGetter<const Color&>::m_getter)(), progress)); } protected: void (RenderStyle::*m_setter)(const Color&); }; - -#if USE(ACCELERATED_COMPOSITING) class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperAcceleratedOpacity() : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity) { } - virtual bool animationIsAccelerated() const { return true; } + bool animationIsAccelerated() const override { return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - float fromOpacity = a->opacity(); - - // This makes sure we put the object being animated into a RenderLayer during the animation - dst->setOpacity(blendFunc(anim, (fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), progress)); + dst->setOpacity(blendFunc(anim, a->opacity(), b->opacity(), progress)); } }; class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperAcceleratedTransform() - : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform) + : PropertyWrapper<const TransformOperations&>(CSSPropertyTransform, &RenderStyle::transform, &RenderStyle::setTransform) { } - virtual bool animationIsAccelerated() const { return true; } + bool animationIsAccelerated() const override { return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { dst->setTransform(blendFunc(anim, a->transform(), b->transform(), progress)); } }; -#if ENABLE(CSS_FILTERS) class PropertyWrapperAcceleratedFilter : public PropertyWrapper<const FilterOperations&> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperAcceleratedFilter() - : PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitFilter, &RenderStyle::filter, &RenderStyle::setFilter) + : PropertyWrapper<const FilterOperations&>(CSSPropertyFilter, &RenderStyle::filter, &RenderStyle::setFilter) + { + } + + bool animationIsAccelerated() const override { return true; } + + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override + { + dst->setFilter(blendFunc(anim, a->filter(), b->filter(), progress)); + } +}; + +#if ENABLE(FILTERS_LEVEL_2) +class PropertyWrapperAcceleratedBackdropFilter : public PropertyWrapper<const FilterOperations&> { + WTF_MAKE_FAST_ALLOCATED; +public: + PropertyWrapperAcceleratedBackdropFilter() + : PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitBackdropFilter, &RenderStyle::backdropFilter, &RenderStyle::setBackdropFilter) { } @@ -579,11 +689,10 @@ public: virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const { - dst->setFilter(blendFunc(anim, a->filter(), b->filter(), progress)); + dst->setBackdropFilter(blendFunc(anim, a->backdropFilter(), b->backdropFilter(), progress, true)); } }; #endif -#endif // USE(ACCELERATED_COMPOSITING) static inline size_t shadowListLength(const ShadowData* shadow) { @@ -595,32 +704,37 @@ static inline size_t shadowListLength(const ShadowData* shadow) static inline const ShadowData* shadowForBlending(const ShadowData* srcShadow, const ShadowData* otherShadow) { - DEFINE_STATIC_LOCAL(ShadowData, defaultShadowData, (IntPoint(), 0, 0, Normal, false, Color::transparent)); - DEFINE_STATIC_LOCAL(ShadowData, defaultInsetShadowData, (IntPoint(), 0, 0, Inset, false, Color::transparent)); - - DEFINE_STATIC_LOCAL(ShadowData, defaultWebKitBoxShadowData, (IntPoint(), 0, 0, Normal, true, Color::transparent)); - DEFINE_STATIC_LOCAL(ShadowData, defaultInsetWebKitBoxShadowData, (IntPoint(), 0, 0, Inset, true, Color::transparent)); + static NeverDestroyed<ShadowData> defaultShadowData(IntPoint(), 0, 0, Normal, false, Color::transparent); + static NeverDestroyed<ShadowData> defaultInsetShadowData(IntPoint(), 0, 0, Inset, false, Color::transparent); + static NeverDestroyed<ShadowData> defaultWebKitBoxShadowData(IntPoint(), 0, 0, Normal, true, Color::transparent); + static NeverDestroyed<ShadowData> defaultInsetWebKitBoxShadowData(IntPoint(), 0, 0, Inset, true, Color::transparent); if (srcShadow) return srcShadow; if (otherShadow->style() == Inset) - return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData : &defaultInsetShadowData; + return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData.get() : &defaultInsetShadowData.get(); - return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData : &defaultShadowData; + return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData.get() : &defaultShadowData.get(); } class PropertyWrapperShadow : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperShadow(CSSPropertyID prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassOwnPtr<ShadowData>, bool)) + PropertyWrapperShadow(CSSPropertyID prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(std::unique_ptr<ShadowData>, bool)) : AnimationPropertyWrapperBase(prop) , m_getter(getter) , m_setter(setter) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { + if (a == b) + return true; + if (!a || !b) + return false; + const ShadowData* shadowA = (a->*m_getter)(); const ShadowData* shadowB = (b->*m_getter)(); @@ -643,7 +757,7 @@ public: return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { const ShadowData* shadowA = (a->*m_getter)(); const ShadowData* shadowB = (b->*m_getter)(); @@ -659,23 +773,31 @@ public: (dst->*m_setter)(blendMismatchedShadowLists(anim, progress, shadowA, shadowB, fromLength, toLength), false); } +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending ShadowData at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif + private: - PassOwnPtr<ShadowData> blendSimpleOrMatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB) const + std::unique_ptr<ShadowData> blendSimpleOrMatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB) const { - OwnPtr<ShadowData> newShadowData; + std::unique_ptr<ShadowData> newShadowData; ShadowData* lastShadow = 0; while (shadowA || shadowB) { const ShadowData* srcShadow = shadowForBlending(shadowA, shadowB); const ShadowData* dstShadow = shadowForBlending(shadowB, shadowA); - OwnPtr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); + std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); ShadowData* blendedShadowPtr = blendedShadow.get(); if (!lastShadow) - newShadowData = blendedShadow.release(); + newShadowData = WTFMove(blendedShadow); else - lastShadow->setNext(blendedShadow.release()); + lastShadow->setNext(WTFMove(blendedShadow)); lastShadow = blendedShadowPtr; @@ -683,10 +805,10 @@ private: shadowB = shadowB ? shadowB->next() : 0; } - return newShadowData.release(); + return newShadowData; } - PassOwnPtr<ShadowData> blendMismatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB, int fromLength, int toLength) const + std::unique_ptr<ShadowData> blendMismatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB, int fromLength, int toLength) const { // The shadows in ShadowData are stored in reverse order, so when animating mismatched lists, // reverse them and match from the end. @@ -702,7 +824,7 @@ private: shadowB = shadowB->next(); } - OwnPtr<ShadowData> newShadowData; + std::unique_ptr<ShadowData> newShadowData; int maxLength = std::max(fromLength, toLength); for (int i = 0; i < maxLength; ++i) { @@ -712,32 +834,38 @@ private: const ShadowData* srcShadow = shadowForBlending(fromShadow, toShadow); const ShadowData* dstShadow = shadowForBlending(toShadow, fromShadow); - OwnPtr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); + std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); // Insert at the start of the list to preserve the order. - blendedShadow->setNext(newShadowData.release()); - newShadowData = blendedShadow.release(); + blendedShadow->setNext(WTFMove(newShadowData)); + newShadowData = WTFMove(blendedShadow); } - return newShadowData.release(); + return newShadowData; } const ShadowData* (RenderStyle::*m_getter)() const; - void (RenderStyle::*m_setter)(PassOwnPtr<ShadowData>, bool); + void (RenderStyle::*m_setter)(std::unique_ptr<ShadowData>, bool); }; class PropertyWrapperMaybeInvalidColor : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperMaybeInvalidColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + PropertyWrapperMaybeInvalidColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) : AnimationPropertyWrapperBase(prop) , m_getter(getter) , m_setter(setter) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - Color fromColor = (a->*m_getter)(); - Color toColor = (b->*m_getter)(); + if (a == b) + return true; + if (!a || !b) + return false; + + Color fromColor = value(a); + Color toColor = value(b); if (!fromColor.isValid() && !toColor.isValid()) return true; @@ -750,10 +878,10 @@ public: return fromColor == toColor; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - Color fromColor = (a->*m_getter)(); - Color toColor = (b->*m_getter)(); + Color fromColor = value(a); + Color toColor = value(b); if (!fromColor.isValid() && !toColor.isValid()) return; @@ -765,46 +893,69 @@ public: (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress)); } + Color value(const RenderStyle* a) const + { + return (a->*m_getter)(); + } + +#if !LOG_DISABLED + void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result)); + } +#endif + private: - Color (RenderStyle::*m_getter)() const; + const Color& (RenderStyle::*m_getter)() const; void (RenderStyle::*m_setter)(const Color&); }; enum MaybeInvalidColorTag { MaybeInvalidColor }; class PropertyWrapperVisitedAffectedColor : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), - Color (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) + PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), + const Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) : AnimationPropertyWrapperBase(prop) - , m_wrapper(adoptPtr(new PropertyWrapperColor(prop, getter, setter))) - , m_visitedWrapper(adoptPtr(new PropertyWrapperColor(prop, visitedGetter, visitedSetter))) + , m_wrapper(std::make_unique<PropertyWrapperColor>(prop, getter, setter)) + , m_visitedWrapper(std::make_unique<PropertyWrapperColor>(prop, visitedGetter, visitedSetter)) { } - PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, MaybeInvalidColorTag, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), - Color (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) + PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, MaybeInvalidColorTag, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), + const Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) : AnimationPropertyWrapperBase(prop) - , m_wrapper(adoptPtr(new PropertyWrapperMaybeInvalidColor(prop, getter, setter))) - , m_visitedWrapper(adoptPtr(new PropertyWrapperMaybeInvalidColor(prop, visitedGetter, visitedSetter))) + , m_wrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, getter, setter)) + , m_visitedWrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, visitedGetter, visitedSetter)) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { return m_wrapper->equals(a, b) && m_visitedWrapper->equals(a, b); } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { m_wrapper->blend(anim, dst, a, b, progress); m_visitedWrapper->blend(anim, dst, a, b, progress); } +#if !LOG_DISABLED + void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final + { + m_wrapper->logBlend(a, b, result, progress); + m_visitedWrapper->logBlend(a, b, result, progress); + } +#endif + private: - OwnPtr<AnimationPropertyWrapperBase> m_wrapper; - OwnPtr<AnimationPropertyWrapperBase> m_visitedWrapper; + std::unique_ptr<AnimationPropertyWrapperBase> m_wrapper; + std::unique_ptr<AnimationPropertyWrapperBase> m_visitedWrapper; }; // Wrapper base class for an animatable property in a FillLayer class FillLayerAnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: FillLayerAnimationPropertyWrapperBase() { @@ -818,6 +969,7 @@ public: template <typename T> class FillLayerPropertyWrapperGetter : public FillLayerAnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter); public: FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const) @@ -825,13 +977,11 @@ public: { } - virtual bool equals(const FillLayer* a, const FillLayer* b) const + bool equals(const FillLayer* a, const FillLayer* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. - if ((!a && !b) || a == b) - return true; - if (!a || !b) + if (a == b) + return true; + if (!a || !b) return false; return (a->*m_getter)() == (b->*m_getter)(); } @@ -842,6 +992,7 @@ protected: template <typename T> class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<const T&> { + WTF_MAKE_FAST_ALLOCATED; public: FillLayerPropertyWrapper(const T& (FillLayer::*getter)() const, void (FillLayer::*setter)(T)) : FillLayerPropertyWrapperGetter<const T&>(getter) @@ -849,7 +1000,7 @@ public: { } - virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const + void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), progress)); } @@ -860,33 +1011,33 @@ protected: template <typename T> class FillLayerRefCountedPropertyWrapper : public FillLayerPropertyWrapperGetter<T*> { + WTF_MAKE_FAST_ALLOCATED; public: - FillLayerRefCountedPropertyWrapper(T* (FillLayer::*getter)() const, void (FillLayer::*setter)(PassRefPtr<T>)) + FillLayerRefCountedPropertyWrapper(T* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<T>&&)) : FillLayerPropertyWrapperGetter<T*>(getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const + void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), progress)); } protected: - void (FillLayer::*m_setter)(PassRefPtr<T>); + void (FillLayer::*m_setter)(RefPtr<T>&&); }; class FillLayerStyleImagePropertyWrapper : public FillLayerRefCountedPropertyWrapper<StyleImage> { + WTF_MAKE_FAST_ALLOCATED; public: - FillLayerStyleImagePropertyWrapper(StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(PassRefPtr<StyleImage>)) + FillLayerStyleImagePropertyWrapper(StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<StyleImage>&&)) : FillLayerRefCountedPropertyWrapper<StyleImage>(getter, setter) { } - virtual bool equals(const FillLayer* a, const FillLayer* b) const + bool equals(const FillLayer* a, const FillLayer* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. if (a == b) return true; if (!a || !b) @@ -894,14 +1045,15 @@ public: StyleImage* imageA = (a->*m_getter)(); StyleImage* imageB = (b->*m_getter)(); - return StyleImage::imagesEquivalent(imageA, imageB); + return arePointingToEqualData(imageA, imageB); } }; class FillLayersPropertyWrapper : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - typedef const FillLayer* (RenderStyle::*LayersGetter)() const; - typedef FillLayer* (RenderStyle::*LayersAccessor)(); + typedef const FillLayer& (RenderStyle::*LayersGetter)() const; + typedef FillLayer& (RenderStyle::*LayersAccessor)(); FillLayersPropertyWrapper(CSSPropertyID prop, LayersGetter getter, LayersAccessor accessor) : AnimationPropertyWrapperBase(prop) @@ -911,29 +1063,34 @@ public: switch (prop) { case CSSPropertyBackgroundPositionX: case CSSPropertyWebkitMaskPositionX: - m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::xPosition, &FillLayer::setXPosition); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<Length>>(&FillLayer::xPosition, &FillLayer::setXPosition); break; case CSSPropertyBackgroundPositionY: case CSSPropertyWebkitMaskPositionY: - m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::yPosition, &FillLayer::setYPosition); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<Length>>(&FillLayer::yPosition, &FillLayer::setYPosition); break; case CSSPropertyBackgroundSize: case CSSPropertyWebkitBackgroundSize: case CSSPropertyWebkitMaskSize: - m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<LengthSize>(&FillLayer::sizeLength, &FillLayer::setSizeLength); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<LengthSize>>(&FillLayer::sizeLength, &FillLayer::setSizeLength); break; case CSSPropertyBackgroundImage: - m_fillLayerPropertyWrapper = new FillLayerStyleImagePropertyWrapper(&FillLayer::image, &FillLayer::setImage); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerStyleImagePropertyWrapper>(&FillLayer::image, &FillLayer::setImage); break; default: break; } } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - const FillLayer* fromLayer = (a->*m_layersGetter)(); - const FillLayer* toLayer = (b->*m_layersGetter)(); + if (a == b) + return true; + if (!a || !b) + return false; + + auto* fromLayer = &(a->*m_layersGetter)(); + auto* toLayer = &(b->*m_layersGetter)(); while (fromLayer && toLayer) { if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer)) @@ -946,11 +1103,11 @@ public: return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - const FillLayer* aLayer = (a->*m_layersGetter)(); - const FillLayer* bLayer = (b->*m_layersGetter)(); - FillLayer* dstLayer = (dst->*m_layersAccessor)(); + auto* aLayer = &(a->*m_layersGetter)(); + auto* bLayer = &(b->*m_layersGetter)(); + auto* dstLayer = &(dst->*m_layersAccessor)(); while (aLayer && bLayer && dstLayer) { m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress); @@ -960,57 +1117,77 @@ public: } } +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending FillLayers at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif + private: - FillLayerAnimationPropertyWrapperBase* m_fillLayerPropertyWrapper; + std::unique_ptr<FillLayerAnimationPropertyWrapperBase> m_fillLayerPropertyWrapper; LayersGetter m_layersGetter; LayersAccessor m_layersAccessor; }; class ShorthandPropertyWrapper : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: ShorthandPropertyWrapper(CSSPropertyID property, Vector<AnimationPropertyWrapperBase*> longhandWrappers) : AnimationPropertyWrapperBase(property) + , m_propertyWrappers(WTFMove(longhandWrappers)) { - m_propertyWrappers.swap(longhandWrappers); } - virtual bool isShorthandWrapper() const { return true; } + bool isShorthandWrapper() const override { return true; } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - Vector<AnimationPropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); - for (Vector<AnimationPropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) { - if (!(*it)->equals(a, b)) + if (a == b) + return true; + if (!a || !b) + return false; + + for (auto& wrapper : m_propertyWrappers) { + if (!wrapper->equals(a, b)) return false; } return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override + { + for (auto& wrapper : m_propertyWrappers) + wrapper->blend(anim, dst, a, b, progress); + } + +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final { - Vector<AnimationPropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); - for (Vector<AnimationPropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) - (*it)->blend(anim, dst, a, b, progress); + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending shorthand property " << getPropertyName(property()) << " at " << TextStream::FormatNumberRespectingIntegers(progress)); } +#endif - const Vector<AnimationPropertyWrapperBase*> propertyWrappers() const { return m_propertyWrappers; } + const Vector<AnimationPropertyWrapperBase*>& propertyWrappers() const { return m_propertyWrappers; } private: Vector<AnimationPropertyWrapperBase*> m_propertyWrappers; }; class PropertyWrapperFlex : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperFlex() : AnimationPropertyWrapperBase(CSSPropertyWebkitFlex) + PropertyWrapperFlex() + : AnimationPropertyWrapperBase(CSSPropertyFlex) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. - if ((!a && !b) || a == b) + if (a == b) return true; if (!a || !b) return false; @@ -1018,18 +1195,26 @@ public: return a->flexBasis() == b->flexBasis() && a->flexGrow() == b->flexGrow() && a->flexShrink() == b->flexShrink(); } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { dst->setFlexBasis(blendFunc(anim, a->flexBasis(), b->flexBasis(), progress)); dst->setFlexGrow(blendFunc(anim, a->flexGrow(), b->flexGrow(), progress)); dst->setFlexShrink(blendFunc(anim, a->flexShrink(), b->flexShrink(), progress)); } + +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending flex at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif }; -#if ENABLE(SVG) class PropertyWrapperSVGPaint : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperSVGPaint(CSSPropertyID prop, const SVGPaint::SVGPaintType& (RenderStyle::*paintTypeGetter)() const, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + PropertyWrapperSVGPaint(CSSPropertyID prop, const SVGPaintType& (RenderStyle::*paintTypeGetter)() const, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) : AnimationPropertyWrapperBase(prop) , m_paintTypeGetter(paintTypeGetter) , m_getter(getter) @@ -1037,15 +1222,20 @@ public: { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { + if (a == b) + return true; + if (!a || !b) + return false; + if ((a->*m_paintTypeGetter)() != (b->*m_paintTypeGetter)()) return false; // We only support animations between SVGPaints that are pure Color values. // For everything else we must return true for this method, otherwise // we will try to animate between values forever. - if ((a->*m_paintTypeGetter)() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR) { + if ((a->*m_paintTypeGetter)() == SVG_PAINTTYPE_RGBCOLOR) { Color fromColor = (a->*m_getter)(); Color toColor = (b->*m_getter)(); @@ -1062,10 +1252,10 @@ public: return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - if ((a->*m_paintTypeGetter)() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR - || (b->*m_paintTypeGetter)() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR) + if ((a->*m_paintTypeGetter)() != SVG_PAINTTYPE_RGBCOLOR + || (b->*m_paintTypeGetter)() != SVG_PAINTTYPE_RGBCOLOR) return; Color fromColor = (a->*m_getter)(); @@ -1081,32 +1271,38 @@ public: (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress)); } +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending SVGPaint at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif + private: - const SVGPaint::SVGPaintType& (RenderStyle::*m_paintTypeGetter)() const; + const SVGPaintType& (RenderStyle::*m_paintTypeGetter)() const; Color (RenderStyle::*m_getter)() const; void (RenderStyle::*m_setter)(const Color&); }; -#endif class CSSPropertyAnimationWrapperMap { + WTF_MAKE_FAST_ALLOCATED; public: - static CSSPropertyAnimationWrapperMap& instance() + static CSSPropertyAnimationWrapperMap& singleton() { - // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed? - DEFINE_STATIC_LOCAL(OwnPtr<CSSPropertyAnimationWrapperMap>, map, ()); - if (!map) - map = adoptPtr(new CSSPropertyAnimationWrapperMap); - return *map; + // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last CSSAnimationController is destroyed? + static NeverDestroyed<CSSPropertyAnimationWrapperMap> map; + return map; } AnimationPropertyWrapperBase* wrapperForProperty(CSSPropertyID propertyID) { if (propertyID < firstCSSProperty || propertyID > lastCSSProperty) - return 0; + return nullptr; unsigned wrapperIndex = indexFromPropertyID(propertyID); if (wrapperIndex == cInvalidPropertyWrapperIndex) - return 0; + return nullptr; return m_propertyWrappers[wrapperIndex].get(); } @@ -1124,15 +1320,19 @@ public: private: CSSPropertyAnimationWrapperMap(); + ~CSSPropertyAnimationWrapperMap() = delete; + unsigned char& indexFromPropertyID(CSSPropertyID propertyID) { return m_propertyToIdMap[propertyID - firstCSSProperty]; } - Vector<OwnPtr<AnimationPropertyWrapperBase>> m_propertyWrappers; + Vector<std::unique_ptr<AnimationPropertyWrapperBase>> m_propertyWrappers; unsigned char m_propertyToIdMap[numCSSProperties]; static const unsigned char cInvalidPropertyWrapperIndex = UCHAR_MAX; + + friend class WTF::NeverDestroyed<CSSPropertyAnimationWrapperMap>; }; CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() @@ -1154,10 +1354,10 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperFlex(), - new PropertyWrapper<unsigned>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth), - new PropertyWrapper<unsigned>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth), - new PropertyWrapper<unsigned>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth), - new PropertyWrapper<unsigned>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth), + new PropertyWrapper<float>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth), + new PropertyWrapper<float>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth), + new PropertyWrapper<float>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth), + new PropertyWrapper<float>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth), new LengthPropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft), new LengthPropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight), new LengthPropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop), @@ -1170,7 +1370,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperVisitedAffectedColor(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor, &RenderStyle::visitedLinkBackgroundColor, &RenderStyle::setVisitedLinkBackgroundColor), - new FillLayersPropertyWrapper(CSSPropertyBackgroundImage, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyBackgroundImage, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), new StyleImagePropertyWrapper(CSSPropertyListStyleImage, &RenderStyle::listStyleImage, &RenderStyle::setListStyleImage), new StyleImagePropertyWrapper(CSSPropertyWebkitMaskImage, &RenderStyle::maskImage, &RenderStyle::setMaskImage), @@ -1182,47 +1382,38 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new StyleImagePropertyWrapper(CSSPropertyWebkitMaskBoxImageSource, &RenderStyle::maskBoxImageSource, &RenderStyle::setMaskBoxImageSource), new PropertyWrapper<const NinePieceImage&>(CSSPropertyWebkitMaskBoxImage, &RenderStyle::maskBoxImage, &RenderStyle::setMaskBoxImage), - new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - - new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers), - new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers), - new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers), - - new PropertyWrapper<float>(CSSPropertyFontSize, - // Must pass a specified size to setFontSize if Text Autosizing is enabled, but a computed size - // if text zoom is enabled (if neither is enabled it's irrelevant as they're probably the same). - // FIXME: Find some way to assert that text zoom isn't activated when Text Autosizing is compiled in. -#if ENABLE(TEXT_AUTOSIZING) - &RenderStyle::specifiedFontSize, -#else - &RenderStyle::computedFontSize, -#endif - &RenderStyle::setFontSize), - new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth), - new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap), - new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount), - new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth), - new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing), - new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing), + new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + + new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers), + new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers), + new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers), + + new PropertyWrapper<float>(CSSPropertyFontSize, &RenderStyle::computedFontSize, &RenderStyle::setFontSize), + new PropertyWrapper<unsigned short>(CSSPropertyColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth), + new PropertyWrapper<float>(CSSPropertyColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap), + new PropertyWrapper<unsigned short>(CSSPropertyColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount), + new PropertyWrapper<float>(CSSPropertyColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth), + new PropertyWrapper<float>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing), + new PropertyWrapper<float>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing), new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex), new PropertyWrapper<short>(CSSPropertyOrphans, &RenderStyle::orphans, &RenderStyle::setOrphans), new PropertyWrapper<short>(CSSPropertyWidows, &RenderStyle::widows, &RenderStyle::setWidows), new LengthPropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight), - new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset), - new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth), + new PropertyWrapper<float>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset), + new PropertyWrapper<float>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth), new PropertyWrapper<float>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing), new LengthPropertyWrapper<Length>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing), new LengthPropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent), - new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY), - new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ), + new PropertyWrapper<float>(CSSPropertyPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective), + new LengthPropertyWrapper<Length>(CSSPropertyPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX), + new LengthPropertyWrapper<Length>(CSSPropertyPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY), + new LengthPropertyWrapper<Length>(CSSPropertyTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX), + new LengthPropertyWrapper<Length>(CSSPropertyTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY), + new PropertyWrapper<float>(CSSPropertyTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ), new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius), new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius), new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius), @@ -1232,30 +1423,19 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new LengthPropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip), -#if USE(ACCELERATED_COMPOSITING) new PropertyWrapperAcceleratedOpacity(), new PropertyWrapperAcceleratedTransform(), -#if ENABLE(CSS_FILTERS) new PropertyWrapperAcceleratedFilter(), +#if ENABLE(FILTERS_LEVEL_2) + new PropertyWrapperAcceleratedBackdropFilter(), #endif -#else - new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity), - new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform), -#if ENABLE(CSS_FILTERS) - new PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitFilter, &RenderStyle::filter, &RenderStyle::setFilter), -#endif -#endif - new PropertyWrapperClipPath(CSSPropertyWebkitClipPath, &RenderStyle::clipPath, &RenderStyle::setClipPath), -#if ENABLE(CSS_SHAPES) - new PropertyWrapperShape(CSSPropertyWebkitShapeInside, &RenderStyle::shapeInside, &RenderStyle::setShapeInside), - new PropertyWrapperShape(CSSPropertyWebkitShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin), - new PropertyWrapper<float>(CSSPropertyWebkitShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold), -#endif + new PropertyWrapperShape(CSSPropertyShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside), + new LengthPropertyWrapper<Length>(CSSPropertyShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin), + new PropertyWrapper<float>(CSSPropertyShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold), - new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor), + new PropertyWrapperVisitedAffectedColor(CSSPropertyColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor), new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextStrokeColor, MaybeInvalidColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor, &RenderStyle::visitedLinkTextStrokeColor, &RenderStyle::setVisitedLinkTextStrokeColor), new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextFillColor, MaybeInvalidColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor, &RenderStyle::visitedLinkTextFillColor, &RenderStyle::setVisitedLinkTextFillColor), new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderLeftColor, MaybeInvalidColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor, &RenderStyle::visitedLinkBorderLeftColor, &RenderStyle::setVisitedLinkBorderLeftColor), @@ -1268,17 +1448,24 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow), new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow), -#if ENABLE(SVG) new PropertyWrapperSVGPaint(CSSPropertyFill, &RenderStyle::fillPaintType, &RenderStyle::fillPaintColor, &RenderStyle::setFillPaintColor), new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity), new PropertyWrapperSVGPaint(CSSPropertyStroke, &RenderStyle::strokePaintType, &RenderStyle::strokePaintColor, &RenderStyle::setStrokePaintColor), new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity), - new PropertyWrapper<SVGLength>(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth), - new PropertyWrapper< Vector<SVGLength>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray), - new PropertyWrapper<SVGLength>(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset), + new PropertyWrapper<Vector<SVGLengthValue>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray), new PropertyWrapper<float>(CSSPropertyStrokeMiterlimit, &RenderStyle::strokeMiterLimit, &RenderStyle::setStrokeMiterLimit), + new LengthPropertyWrapper<Length>(CSSPropertyCx, &RenderStyle::cx, &RenderStyle::setCx), + new LengthPropertyWrapper<Length>(CSSPropertyCy, &RenderStyle::cy, &RenderStyle::setCy), + new LengthPropertyWrapper<Length>(CSSPropertyR, &RenderStyle::r, &RenderStyle::setR), + new LengthPropertyWrapper<Length>(CSSPropertyRx, &RenderStyle::rx, &RenderStyle::setRx), + new LengthPropertyWrapper<Length>(CSSPropertyRy, &RenderStyle::ry, &RenderStyle::setRy), + new LengthPropertyWrapper<Length>(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset), + new LengthPropertyWrapper<Length>(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth), + new LengthPropertyWrapper<Length>(CSSPropertyX, &RenderStyle::x, &RenderStyle::setX), + new LengthPropertyWrapper<Length>(CSSPropertyY, &RenderStyle::y, &RenderStyle::setY), + new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity), new PropertyWrapperMaybeInvalidColor(CSSPropertyFloodColor, &RenderStyle::floodColor, &RenderStyle::setFloodColor), @@ -1287,8 +1474,10 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperMaybeInvalidColor(CSSPropertyLightingColor, &RenderStyle::lightingColor, &RenderStyle::setLightingColor), - new PropertyWrapper<SVGLength>(CSSPropertyBaselineShift, &RenderStyle::baselineShiftValue, &RenderStyle::setBaselineShiftValue), - new PropertyWrapper<SVGLength>(CSSPropertyKerning, &RenderStyle::kerning, &RenderStyle::setKerning), + new PropertyWrapper<SVGLengthValue>(CSSPropertyBaselineShift, &RenderStyle::baselineShiftValue, &RenderStyle::setBaselineShiftValue), + new PropertyWrapper<SVGLengthValue>(CSSPropertyKerning, &RenderStyle::kerning, &RenderStyle::setKerning), +#if ENABLE(VARIATION_FONTS) + new PropertyWrapperFontVariationSettings(CSSPropertyFontVariationSettings, &RenderStyle::fontVariationSettings, &RenderStyle::setFontVariationSettings), #endif }; const unsigned animatableLonghandPropertiesCount = WTF_ARRAY_LENGTH(animatableLonghandPropertyWrappers); @@ -1311,9 +1500,9 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() CSSPropertyOutline, CSSPropertyPadding, CSSPropertyWebkitTextStroke, - CSSPropertyWebkitColumnRule, + CSSPropertyColumnRule, CSSPropertyWebkitBorderRadius, - CSSPropertyWebkitTransformOrigin + CSSPropertyTransformOrigin }; const unsigned animatableShorthandPropertiesCount = WTF_ARRAY_LENGTH(animatableShorthandProperties); @@ -1323,7 +1512,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() // // Compound properties that have components that should be animatable: // - // CSSPropertyWebkitColumns + // CSSPropertyColumns // CSSPropertyWebkitBoxReflect // Make sure unused slots have a value @@ -1338,7 +1527,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() for (unsigned i = 0; i < animatableLonghandPropertiesCount; ++i) { AnimationPropertyWrapperBase* wrapper = animatableLonghandPropertyWrappers[i]; - m_propertyWrappers.uncheckedAppend(adoptPtr(wrapper)); + m_propertyWrappers.uncheckedAppend(std::unique_ptr<AnimationPropertyWrapperBase>(wrapper)); indexFromPropertyID(wrapper->property()) = i; } @@ -1359,7 +1548,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() longhandWrappers.uncheckedAppend(m_propertyWrappers[wrapperIndex].get()); } - m_propertyWrappers.uncheckedAppend(adoptPtr(new ShorthandPropertyWrapper(propertyID, longhandWrappers))); + m_propertyWrappers.uncheckedAppend(std::make_unique<ShorthandPropertyWrapper>(propertyID, WTFMove(longhandWrappers))); indexFromPropertyID(propertyID) = animatableLonghandPropertiesCount + i; } } @@ -1370,11 +1559,8 @@ static bool gatherEnclosingShorthandProperties(CSSPropertyID property, Animation return false; ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper); - bool contained = false; - for (size_t i = 0; i < shorthandWrapper->propertyWrappers().size(); ++i) { - AnimationPropertyWrapperBase* currWrapper = shorthandWrapper->propertyWrappers()[i]; - + for (auto& currWrapper : shorthandWrapper->propertyWrappers()) { if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property) contained = true; } @@ -1390,31 +1576,27 @@ bool CSSPropertyAnimation::blendProperties(const AnimationBase* anim, CSSPropert { ASSERT(prop != CSSPropertyInvalid); - AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop); + AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); if (wrapper) { wrapper->blend(anim, dst, a, b, progress); -#if USE(ACCELERATED_COMPOSITING) - return !wrapper->animationIsAccelerated() || !anim->isAccelerated(); -#else - return true; +#if !LOG_DISABLED + wrapper->logBlend(a, b, dst, progress); #endif + return !wrapper->animationIsAccelerated() || !anim->isAccelerated(); } - return false; } -#if USE(ACCELERATED_COMPOSITING) bool CSSPropertyAnimation::animationOfPropertyIsAccelerated(CSSPropertyID prop) { - AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop); + AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); return wrapper ? wrapper->animationIsAccelerated() : false; } -#endif // Note: this is inefficient. It's only called from pauseTransitionAtTime(). HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProperty(CSSPropertyID property) { - CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::instance(); + CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton(); HashSet<CSSPropertyID> foundProperties; for (unsigned i = 0; i < map.size(); ++i) @@ -1425,7 +1607,7 @@ HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProper bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b) { - AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop); + AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); if (wrapper) return wrapper->equals(a, b); return true; @@ -1433,7 +1615,7 @@ bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, bool& isShorthand) { - CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::instance(); + CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton(); if (i < 0 || static_cast<unsigned>(i) >= map.size()) return CSSPropertyInvalid; @@ -1445,7 +1627,7 @@ CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, bool& isShorthand) int CSSPropertyAnimation::getNumProperties() { - return CSSPropertyAnimationWrapperMap::instance().size(); + return CSSPropertyAnimationWrapperMap::singleton().size(); } } diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.h b/Source/WebCore/page/animation/CSSPropertyAnimation.h index c41c7dd90..98ec9a154 100644 --- a/Source/WebCore/page/animation/CSSPropertyAnimation.h +++ b/Source/WebCore/page/animation/CSSPropertyAnimation.h @@ -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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CSSPropertyAnimation_h -#define CSSPropertyAnimation_h +#pragma once #include "CSSPropertyNames.h" #include "RenderStyleConstants.h" @@ -40,9 +39,7 @@ class RenderStyle; class CSSPropertyAnimation { public: -#if USE(ACCELERATED_COMPOSITING) static bool animationOfPropertyIsAccelerated(CSSPropertyID); -#endif static bool propertiesEqual(CSSPropertyID, const RenderStyle* a, const RenderStyle* b); static CSSPropertyID getPropertyAtIndex(int, bool& isShorthand); static int getNumProperties(); @@ -54,5 +51,3 @@ public: }; } // namespace WebCore - -#endif // CSSPropertyAnimation_h diff --git a/Source/WebCore/page/animation/CompositeAnimation.cpp b/Source/WebCore/page/animation/CompositeAnimation.cpp index e18779ea7..f89f1e514 100644 --- a/Source/WebCore/page/animation/CompositeAnimation.cpp +++ b/Source/WebCore/page/animation/CompositeAnimation.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,7 +29,7 @@ #include "config.h" #include "CompositeAnimation.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPropertyAnimation.h" #include "CSSPropertyNames.h" #include "ImplicitAnimation.h" @@ -37,14 +37,15 @@ #include "Logging.h" #include "RenderElement.h" #include "RenderStyle.h" +#include <wtf/NeverDestroyed.h> #include <wtf/text/CString.h> namespace WebCore { -CompositeAnimation::CompositeAnimation(AnimationControllerPrivate* animationController) +CompositeAnimation::CompositeAnimation(CSSAnimationControllerPrivate& animationController) : m_animationController(animationController) { - m_suspended = animationController->isSuspended() && !animationController->allowsNewAnimationsWhileSuspended(); + m_suspended = m_animationController.isSuspended() && !m_animationController.allowsNewAnimationsWhileSuspended(); } CompositeAnimation::~CompositeAnimation() @@ -62,25 +63,21 @@ void CompositeAnimation::clearRenderer() if (!m_transitions.isEmpty()) { // Clear the renderers from all running animations, in case we are in the middle of // an animation callback (see https://bugs.webkit.org/show_bug.cgi?id=22052) - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* transition = it->value.get(); - animationController()->animationWillBeRemoved(transition); + for (auto& transition : m_transitions.values()) { + animationController().animationWillBeRemoved(transition.get()); transition->clear(); } } if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - animationController()->animationWillBeRemoved(anim); - anim->clear(); + for (auto& animation : m_keyframeAnimations.values()) { + animationController().animationWillBeRemoved(animation.get()); + animation->clear(); } } } -void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +void CompositeAnimation::updateTransitions(RenderElement* renderer, const RenderStyle* currentStyle, const RenderStyle* targetStyle) { // If currentStyle is null or there are no old or new transitions, just skip it if (!currentStyle || (!targetStyle->transitions() && m_transitions.isEmpty())) @@ -88,20 +85,19 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // Mark all existing transitions as no longer active. We will mark the still active ones // in the next loop and then toss the ones that didn't get marked. - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) - it->value->setActive(false); + for (auto& transition : m_transitions.values()) + transition->setActive(false); - RefPtr<RenderStyle> modifiedCurrentStyle; + std::unique_ptr<RenderStyle> modifiedCurrentStyle; // Check to see if we need to update the active transitions if (targetStyle->transitions()) { for (size_t i = 0; i < targetStyle->transitions()->size(); ++i) { - const Animation& animation = targetStyle->transitions()->animation(i); + auto& animation = targetStyle->transitions()->animation(i); bool isActiveTransition = !m_suspended && (animation.duration() || animation.delay() > 0); Animation::AnimationMode mode = animation.animationMode(); - if (mode == Animation::AnimateNone) + if (mode == Animation::AnimateNone || mode == Animation::AnimateUnknownProperty) continue; CSSPropertyID prop = animation.property(); @@ -125,8 +121,8 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // If there is a running animation for this property, the transition is overridden // and we have to use the unanimatedStyle from the animation. We do the test // against the unanimated style here, but we "override" the transition later. - RefPtr<KeyframeAnimation> keyframeAnim = getAnimationForProperty(prop); - RenderStyle* fromStyle = keyframeAnim ? keyframeAnim->unanimatedStyle() : currentStyle; + auto* keyframeAnimation = animationForProperty(prop); + auto* fromStyle = keyframeAnimation ? keyframeAnimation->unanimatedStyle() : currentStyle; // See if there is a current transition for this prop ImplicitAnimation* implAnim = m_transitions.get(prop); @@ -148,18 +144,16 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // list. In this case, the latter one overrides the earlier one, so we // behave as though this is a running animation being replaced. if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) { -#if USE(ACCELERATED_COMPOSITING) // For accelerated animations we need to return a new RenderStyle with the _current_ value // of the property, so that restarted transitions use the correct starting point. if (CSSPropertyAnimation::animationOfPropertyIsAccelerated(prop) && implAnim->isAccelerated()) { if (!modifiedCurrentStyle) - modifiedCurrentStyle = RenderStyle::clone(currentStyle); + modifiedCurrentStyle = RenderStyle::clonePtr(*currentStyle); implAnim->blendPropertyValueInStyle(prop, modifiedCurrentStyle.get()); } -#endif LOG(Animations, "Removing existing ImplicitAnimation %p for property %s", implAnim, getPropertyName(prop)); - animationController()->animationWillBeRemoved(implAnim); + animationController().animationWillBeRemoved(implAnim); m_transitions.remove(prop); equal = false; } @@ -174,11 +168,11 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // <https://bugs.webkit.org/show_bug.cgi?id=24787> if (!equal && isActiveTransition) { // Add the new transition - RefPtr<ImplicitAnimation> implicitAnimation = ImplicitAnimation::create(animation, prop, renderer, this, modifiedCurrentStyle ? modifiedCurrentStyle.get() : fromStyle); - LOG(Animations, "Created ImplicitAnimation %p for property %s duration %.2f delay %.2f", implicitAnimation.get(), getPropertyName(prop), animation.duration(), animation.delay()); - m_transitions.set(prop, implicitAnimation.release()); + auto implicitAnimation = ImplicitAnimation::create(animation, prop, renderer, this, modifiedCurrentStyle ? modifiedCurrentStyle.get() : fromStyle); + LOG(Animations, "Created ImplicitAnimation %p on renderer %p for property %s duration %.2f delay %.2f", implicitAnimation.ptr(), renderer, getPropertyName(prop), animation.duration(), animation.delay()); + m_transitions.set(prop, WTFMove(implicitAnimation)); } - + // We only need one pass for the single prop case if (!all) break; @@ -188,158 +182,184 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // Make a list of transitions to be removed Vector<int> toBeRemoved; - end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (!anim->active()) { - animationController()->animationWillBeRemoved(anim); - toBeRemoved.append(anim->animatingProperty()); - LOG(Animations, "Removing ImplicitAnimation %p for property %s", anim, getPropertyName(anim->animatingProperty())); + for (auto& transition : m_transitions.values()) { + if (!transition->active()) { + animationController().animationWillBeRemoved(transition.get()); + toBeRemoved.append(transition->animatingProperty()); + LOG(Animations, "Removing ImplicitAnimation %p from renderer %p for property %s", transition.get(), renderer, getPropertyName(transition->animatingProperty())); } } // Now remove the transitions from the list - for (size_t j = 0; j < toBeRemoved.size(); ++j) - m_transitions.remove(toBeRemoved[j]); + for (auto propertyToRemove : toBeRemoved) + m_transitions.remove(propertyToRemove); } -void CompositeAnimation::updateKeyframeAnimations(RenderElement* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +void CompositeAnimation::updateKeyframeAnimations(RenderElement* renderer, const RenderStyle* currentStyle, const RenderStyle* targetStyle) { // Nothing to do if we don't have any animations, and didn't have any before if (m_keyframeAnimations.isEmpty() && !targetStyle->hasAnimations()) return; m_keyframeAnimations.checkConsistency(); + + if (currentStyle && currentStyle->hasAnimations() && targetStyle->hasAnimations() && *(currentStyle->animations()) == *(targetStyle->animations())) + return; + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + m_hasScrollTriggeredAnimation = false; +#endif + + AnimationNameMap newAnimations; + + // Toss the animation order map. + m_keyframeAnimationOrderMap.clear(); - AnimationNameMap::const_iterator kfend = m_keyframeAnimations.end(); + static NeverDestroyed<const AtomicString> none("none", AtomicString::ConstructFromLiteral); - if (currentStyle && currentStyle->hasAnimations() && targetStyle->hasAnimations() && *(currentStyle->animations()) == *(targetStyle->animations())) { - // The current and target animations are the same so we just need to toss any - // animation which is finished (postActive). - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) { - if (it->value->postActive()) - it->value->setIndex(-1); - } - } else { - // Mark all existing animations as no longer active. - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) - it->value->setIndex(-1); + // Now mark any still active animations as active and add any new animations. + if (targetStyle->animations()) { + int numAnims = targetStyle->animations()->size(); + for (int i = 0; i < numAnims; ++i) { + auto& animation = targetStyle->animations()->animation(i); + AtomicString animationName(animation.name()); + + if (!animation.isValidAnimation()) + continue; - // Toss the animation order map. - m_keyframeAnimationOrderMap.clear(); + // See if there is a current animation for this name. + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl()); + if (keyframeAnim) { + newAnimations.add(keyframeAnim->name().impl(), keyframeAnim); - DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral)); - - // Now mark any still active animations as active and add any new animations. - if (targetStyle->animations()) { - int numAnims = targetStyle->animations()->size(); - for (int i = 0; i < numAnims; ++i) { - const Animation& animation = targetStyle->animations()->animation(i); - AtomicString animationName(animation.name()); - - if (!animation.isValidAnimation()) + if (keyframeAnim->postActive()) continue; - - // See if there is a current animation for this name. - RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl()); - - if (keyframeAnim) { - // If this animation is postActive, skip it so it gets removed at the end of this function. - if (keyframeAnim->postActive()) - continue; - - // This one is still active. - - // Animations match, but play states may differ. Update if needed. - keyframeAnim->updatePlayState(animation.playState()); - - // Set the saved animation to this new one, just in case the play state has changed. - keyframeAnim->setAnimation(animation); - keyframeAnim->setIndex(i); - } else if ((animation.duration() || animation.delay()) && animation.iterationCount() && animationName != none) { - keyframeAnim = KeyframeAnimation::create(animation, renderer, i, this, targetStyle); - LOG(Animations, "Creating KeyframeAnimation %p with keyframes %s, duration %.2f, delay %.2f, iterations %.2f", keyframeAnim.get(), animation.name().utf8().data(), animation.duration(), animation.delay(), animation.iterationCount()); - if (m_suspended) { - keyframeAnim->updatePlayState(AnimPlayStatePaused); - LOG(Animations, " (created in suspended/paused state)"); - } -#if !LOG_DISABLED - for (auto it = keyframeAnim->keyframes().beginProperties(), end = keyframeAnim->keyframes().endProperties(); it != end; ++it) - LOG(Animations, " property %s", getPropertyName(*it)); + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (animation.trigger()->isScrollAnimationTrigger()) + m_hasScrollTriggeredAnimation = true; #endif - m_keyframeAnimations.set(keyframeAnim->name().impl(), keyframeAnim); + + // Animations match, but play states may differ. Update if needed. + keyframeAnim->updatePlayState(animation.playState()); + + // Set the saved animation to this new one, just in case the play state has changed. + keyframeAnim->setAnimation(animation); + } else if ((animation.duration() || animation.delay()) && animation.iterationCount() && animationName != none) { + keyframeAnim = KeyframeAnimation::create(animation, renderer, this, targetStyle); + LOG(Animations, "Creating KeyframeAnimation %p on renderer %p with keyframes %s, duration %.2f, delay %.2f, iterations %.2f", keyframeAnim.get(), renderer, animation.name().utf8().data(), animation.duration(), animation.delay(), animation.iterationCount()); + + if (m_suspended) { + keyframeAnim->updatePlayState(AnimPlayStatePaused); + LOG(Animations, " (created in suspended/paused state)"); } - - // Add this to the animation order map. - if (keyframeAnim) - m_keyframeAnimationOrderMap.append(keyframeAnim->name().impl()); +#if !LOG_DISABLED + for (auto propertyID : keyframeAnim->keyframes().properties()) + LOG(Animations, " property %s", getPropertyName(propertyID)); +#endif + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (animation.trigger()->isScrollAnimationTrigger()) + m_hasScrollTriggeredAnimation = true; +#endif + + newAnimations.set(keyframeAnim->name().impl(), keyframeAnim); } + + // Add this to the animation order map. + if (keyframeAnim) + m_keyframeAnimationOrderMap.append(keyframeAnim->name().impl()); } } // Make a list of animations to be removed. - Vector<AtomicStringImpl*> animsToBeRemoved; - kfend = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) { - KeyframeAnimation* keyframeAnim = it->value.get(); - if (keyframeAnim->index() < 0) { - animsToBeRemoved.append(keyframeAnim->name().impl()); - animationController()->animationWillBeRemoved(keyframeAnim); - keyframeAnim->clear(); - LOG(Animations, "Removing KeyframeAnimation %p", keyframeAnim); + for (auto& animation : m_keyframeAnimations.values()) { + if (!newAnimations.contains(animation->name().impl())) { + animationController().animationWillBeRemoved(animation.get()); + animation->clear(); + LOG(Animations, "Removing KeyframeAnimation %p from renderer %p", animation.get(), renderer); } } - // Now remove the animations from the list. - for (size_t j = 0; j < animsToBeRemoved.size(); ++j) - m_keyframeAnimations.remove(animsToBeRemoved[j]); + std::swap(newAnimations, m_keyframeAnimations); } -PassRef<RenderStyle> CompositeAnimation::animate(RenderElement& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle) +bool CompositeAnimation::animate(RenderElement& renderer, const RenderStyle* currentStyle, const RenderStyle& targetStyle, std::unique_ptr<RenderStyle>& blendedStyle) { - RefPtr<RenderStyle> resultStyle; - // We don't do any transitions if we don't have a currentStyle (on startup). updateTransitions(&renderer, currentStyle, &targetStyle); updateKeyframeAnimations(&renderer, currentStyle, &targetStyle); m_keyframeAnimations.checkConsistency(); + bool animationStateChanged = false; + bool forceStackingContext = false; + if (currentStyle) { // Now that we have transition objects ready, let them know about the new goal state. We want them // to fill in a RenderStyle*& only if needed. - if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - if (ImplicitAnimation* anim = it->value.get()) - anim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle); - } + bool checkForStackingContext = false; + for (auto& transition : m_transitions.values()) { + bool didBlendStyle = false; + if (transition->animate(this, &renderer, currentStyle, &targetStyle, blendedStyle, didBlendStyle)) + animationStateChanged = true; + + if (didBlendStyle) + checkForStackingContext |= WillChangeData::propertyCreatesStackingContext(transition->animatingProperty()); + } + + if (blendedStyle && checkForStackingContext) { + // Note that this is similar to code in StyleResolver::adjustRenderStyle() but only needs to consult + // animatable properties that can trigger stacking context. + if (blendedStyle->opacity() < 1.0f + || blendedStyle->hasTransformRelatedProperty() + || blendedStyle->hasMask() + || blendedStyle->clipPath() + || blendedStyle->boxReflect() + || blendedStyle->hasFilter() +#if ENABLE(FILTERS_LEVEL_2) + || blendedStyle->hasBackdropFilter() +#endif + ) + forceStackingContext = true; } } // Now that we have animation objects ready, let them know about the new goal state. We want them // to fill in a RenderStyle*& only if needed. - for (Vector<AtomicStringImpl*>::const_iterator it = m_keyframeAnimationOrderMap.begin(); it != m_keyframeAnimationOrderMap.end(); ++it) { - RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(*it); - if (keyframeAnim) - keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle); + for (auto& name : m_keyframeAnimationOrderMap) { + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name); + if (keyframeAnim) { + bool didBlendStyle = false; + if (keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, blendedStyle, didBlendStyle)) + animationStateChanged = true; + + forceStackingContext |= didBlendStyle && keyframeAnim->triggersStackingContext(); + m_hasAnimationThatDependsOnLayout |= keyframeAnim->dependsOnLayout(); + } } - return resultStyle ? resultStyle.releaseNonNull() : targetStyle; + // https://drafts.csswg.org/css-animations-1/ + // While an animation is applied but has not finished, or has finished but has an animation-fill-mode of forwards or both, + // the user agent must act as if the will-change property ([css-will-change-1]) on the element additionally + // includes all the properties animated by the animation. + if (forceStackingContext && blendedStyle) { + if (blendedStyle->hasAutoZIndex()) + blendedStyle->setZIndex(0); + } + + return animationStateChanged; } -PassRefPtr<RenderStyle> CompositeAnimation::getAnimatedStyle() const +std::unique_ptr<RenderStyle> CompositeAnimation::getAnimatedStyle() const { - RefPtr<RenderStyle> resultStyle; - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - if (ImplicitAnimation* implicitAnimation = it->value.get()) - implicitAnimation->getAnimatedStyle(resultStyle); - } + std::unique_ptr<RenderStyle> resultStyle; + for (auto& transition : m_transitions.values()) + transition->getAnimatedStyle(resultStyle); m_keyframeAnimations.checkConsistency(); - for (Vector<AtomicStringImpl*>::const_iterator it = m_keyframeAnimationOrderMap.begin(); it != m_keyframeAnimationOrderMap.end(); ++it) { - RefPtr<KeyframeAnimation> keyframeAnimation = m_keyframeAnimations.get(*it); + for (auto& name : m_keyframeAnimationOrderMap) { + RefPtr<KeyframeAnimation> keyframeAnimation = m_keyframeAnimations.get(name); if (keyframeAnimation) keyframeAnimation->getAnimatedStyle(resultStyle); } @@ -354,10 +374,10 @@ double CompositeAnimation::timeToNextService() const double minT = -1; if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* transition = it->value.get(); - double t = transition ? transition->timeToNextService() : -1; + for (auto& transition : m_transitions.values()) { + double t = transition->timeToNextService(); + if (t == -1) + continue; if (t < minT || minT == -1) minT = t; if (minT == 0) @@ -366,10 +386,10 @@ double CompositeAnimation::timeToNextService() const } if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* animation = it->value.get(); - double t = animation ? animation->timeToNextService() : -1; + for (auto& animation : m_keyframeAnimations.values()) { + double t = animation->timeToNextService(); + if (t == -1) + continue; if (t < minT || minT == -1) minT = t; if (minT == 0) @@ -380,23 +400,53 @@ double CompositeAnimation::timeToNextService() const return minT; } -PassRefPtr<KeyframeAnimation> CompositeAnimation::getAnimationForProperty(CSSPropertyID property) const +KeyframeAnimation* CompositeAnimation::animationForProperty(CSSPropertyID property) const { - RefPtr<KeyframeAnimation> retval; - + KeyframeAnimation* result = nullptr; + // We want to send back the last animation with the property if there are multiples. // So we need to iterate through all animations if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - RefPtr<KeyframeAnimation> anim = it->value; - if (anim->hasAnimationForProperty(property)) - retval = anim; + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->hasAnimationForProperty(property)) + result = animation.get(); } } + + return result; +} + +bool CompositeAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const +{ + // If more than one transition and animation affect transform, give up. + bool seenTransformAnimation = false; + + for (auto& animation : m_keyframeAnimations.values()) { + if (!animation->hasAnimationForProperty(CSSPropertyTransform)) + continue; + + if (seenTransformAnimation) + return false; + + seenTransformAnimation = true; + + if (!animation->computeExtentOfTransformAnimation(bounds)) + return false; + } + + for (auto& transition : m_transitions.values()) { + if (transition->animatingProperty() != CSSPropertyTransform || !transition->hasStyle()) + continue; + + if (seenTransformAnimation) + return false; + + if (!transition->computeExtentOfTransformAnimation(bounds)) + return false; + } - return retval; + return true; } void CompositeAnimation::suspendAnimations() @@ -408,18 +458,14 @@ void CompositeAnimation::suspendAnimations() if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - if (KeyframeAnimation* anim = it->value.get()) - anim->updatePlayState(AnimPlayStatePaused); - } + for (auto& animation : m_keyframeAnimations.values()) + animation->updatePlayState(AnimPlayStatePaused); } + if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->hasStyle()) - anim->updatePlayState(AnimPlayStatePaused); + for (auto& transition : m_transitions.values()) { + if (transition->hasStyle()) + transition->updatePlayState(AnimPlayStatePaused); } } } @@ -433,32 +479,26 @@ void CompositeAnimation::resumeAnimations() if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - if (anim && anim->playStatePlaying()) - anim->updatePlayState(AnimPlayStatePlaying); + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->playStatePlaying()) + animation->updatePlayState(AnimPlayStatePlaying); } } if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->hasStyle()) - anim->updatePlayState(AnimPlayStatePlaying); + for (auto& transition : m_transitions.values()) { + if (transition->hasStyle()) + transition->updatePlayState(AnimPlayStatePlaying); } } } void CompositeAnimation::overrideImplicitAnimations(CSSPropertyID property) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); if (!m_transitions.isEmpty()) { - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->animatingProperty() == property) - anim->setOverridden(true); + for (auto& transition : m_transitions.values()) { + if (transition->animatingProperty() == property) + transition->setOverridden(true); } } } @@ -466,32 +506,26 @@ void CompositeAnimation::overrideImplicitAnimations(CSSPropertyID property) void CompositeAnimation::resumeOverriddenImplicitAnimations(CSSPropertyID property) { if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->animatingProperty() == property) - anim->setOverridden(false); + for (auto& transition : m_transitions.values()) { + if (transition->animatingProperty() == property) + transition->setOverridden(false); } } } -bool CompositeAnimation::isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, bool isRunningNow) const +bool CompositeAnimation::isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, AnimationBase::RunningState runningState) const { if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - if (anim && anim->isAnimatingProperty(property, acceleratedOnly, isRunningNow)) + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->isAnimatingProperty(property, acceleratedOnly, runningState)) return true; } } if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->isAnimatingProperty(property, acceleratedOnly, isRunningNow)) + for (auto& transition : m_transitions.values()) { + if (transition->isAnimatingProperty(property, acceleratedOnly, runningState)) return true; } } @@ -506,13 +540,8 @@ bool CompositeAnimation::pauseAnimationAtTime(const AtomicString& name, double t if (!keyframeAnim || !keyframeAnim->running()) return false; - double count = keyframeAnim->m_animation->iterationCount(); - if ((t >= 0.0) && ((count == Animation::IterationCountInfinite) || (t <= count * keyframeAnim->duration()))) { - keyframeAnim->freezeAtTime(t); - return true; - } - - return false; + keyframeAnim->freezeAtTime(t); + return true; } bool CompositeAnimation::pauseTransitionAtTime(CSSPropertyID property, double t) @@ -526,9 +555,8 @@ bool CompositeAnimation::pauseTransitionAtTime(CSSPropertyID property, double t) // This code is only used for testing, so performance is not critical here. HashSet<CSSPropertyID> shorthandProperties = CSSPropertyAnimation::animatableShorthandsAffectingProperty(property); bool anyPaused = false; - HashSet<CSSPropertyID>::const_iterator end = shorthandProperties.end(); - for (HashSet<CSSPropertyID>::const_iterator it = shorthandProperties.begin(); it != end; ++it) { - if (pauseTransitionAtTime(*it, t)) + for (auto propertyID : shorthandProperties) { + if (pauseTransitionAtTime(propertyID, t)) anyPaused = true; } return anyPaused; @@ -549,23 +577,15 @@ unsigned CompositeAnimation::numberOfActiveAnimations() const { unsigned count = 0; - if (!m_keyframeAnimations.isEmpty()) { - m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - if (anim->running()) - ++count; - } + m_keyframeAnimations.checkConsistency(); + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->running()) + ++count; } - if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim->running()) - ++count; - } + for (auto& transition : m_transitions.values()) { + if (transition->running()) + ++count; } return count; diff --git a/Source/WebCore/page/animation/CompositeAnimation.h b/Source/WebCore/page/animation/CompositeAnimation.h index 48c679ef2..715de586d 100644 --- a/Source/WebCore/page/animation/CompositeAnimation.h +++ b/Source/WebCore/page/animation/CompositeAnimation.h @@ -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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CompositeAnimation_h -#define CompositeAnimation_h +#pragma once #include "ImplicitAnimation.h" #include "KeyframeAnimation.h" @@ -37,30 +36,32 @@ namespace WebCore { -class AnimationControllerPrivate; -class AnimationController; +class CSSAnimationControllerPrivate; +class CSSAnimationController; class RenderElement; class RenderStyle; // A CompositeAnimation represents a collection of animations that are running // on a single RenderElement, such as a number of properties transitioning at once. class CompositeAnimation : public RefCounted<CompositeAnimation> { + WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<CompositeAnimation> create(AnimationControllerPrivate* animationController) + static Ref<CompositeAnimation> create(CSSAnimationControllerPrivate& animationController) { - return adoptRef(new CompositeAnimation(animationController)); + return adoptRef(*new CompositeAnimation(animationController)); }; ~CompositeAnimation(); void clearRenderer(); - PassRef<RenderStyle> animate(RenderElement&, RenderStyle* currentStyle, RenderStyle& targetStyle); - PassRefPtr<RenderStyle> getAnimatedStyle() const; + bool animate(RenderElement&, const RenderStyle* currentStyle, const RenderStyle& targetStyle, std::unique_ptr<RenderStyle>& blendedStyle); + std::unique_ptr<RenderStyle> getAnimatedStyle() const; + bool computeExtentOfTransformAnimation(LayoutRect&) const; double timeToNextService() const; - AnimationControllerPrivate* animationController() const { return m_animationController; } + CSSAnimationControllerPrivate& animationController() const { return m_animationController; } void suspendAnimations(); void resumeAnimations(); @@ -68,9 +69,9 @@ public: bool hasAnimations() const { return !m_transitions.isEmpty() || !m_keyframeAnimations.isEmpty(); } - bool isAnimatingProperty(CSSPropertyID, bool acceleratedOnly, bool isRunningNow) const; + bool isAnimatingProperty(CSSPropertyID, bool acceleratedOnly, AnimationBase::RunningState) const; - PassRefPtr<KeyframeAnimation> getAnimationForProperty(CSSPropertyID) const; + KeyframeAnimation* animationForProperty(CSSPropertyID) const; void overrideImplicitAnimations(CSSPropertyID); void resumeOverriddenImplicitAnimations(CSSPropertyID); @@ -79,22 +80,30 @@ public: bool pauseTransitionAtTime(CSSPropertyID, double); unsigned numberOfActiveAnimations() const; +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool hasScrollTriggeredAnimation() const { return m_hasScrollTriggeredAnimation; } +#endif + + bool hasAnimationThatDependsOnLayout() const { return m_hasAnimationThatDependsOnLayout; } + private: - CompositeAnimation(AnimationControllerPrivate*); + CompositeAnimation(CSSAnimationControllerPrivate&); - void updateTransitions(RenderElement*, RenderStyle* currentStyle, RenderStyle* targetStyle); - void updateKeyframeAnimations(RenderElement*, RenderStyle* currentStyle, RenderStyle* targetStyle); + void updateTransitions(RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle); + void updateKeyframeAnimations(RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle); typedef HashMap<int, RefPtr<ImplicitAnimation>> CSSPropertyTransitionsMap; - typedef HashMap<AtomicStringImpl*, RefPtr<KeyframeAnimation>> AnimationNameMap; + typedef HashMap<AtomicStringImpl*, RefPtr<KeyframeAnimation>> AnimationNameMap; - AnimationControllerPrivate* m_animationController; + CSSAnimationControllerPrivate& m_animationController; CSSPropertyTransitionsMap m_transitions; AnimationNameMap m_keyframeAnimations; Vector<AtomicStringImpl*> m_keyframeAnimationOrderMap; bool m_suspended; + bool m_hasAnimationThatDependsOnLayout { false }; +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool m_hasScrollTriggeredAnimation { false }; +#endif }; } // namespace WebCore - -#endif // CompositeAnimation_h diff --git a/Source/WebCore/page/animation/ImplicitAnimation.cpp b/Source/WebCore/page/animation/ImplicitAnimation.cpp index af1d78819..924eb4b35 100644 --- a/Source/WebCore/page/animation/ImplicitAnimation.cpp +++ b/Source/WebCore/page/animation/ImplicitAnimation.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. * @@ -27,24 +27,24 @@ */ #include "config.h" +#include "ImplicitAnimation.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPropertyAnimation.h" #include "CompositeAnimation.h" #include "EventNames.h" -#include "ImplicitAnimation.h" +#include "GeometryUtilities.h" #include "KeyframeAnimation.h" -#include "RenderBoxModelObject.h" +#include "RenderBox.h" +#include "StylePendingResources.h" namespace WebCore { -ImplicitAnimation::ImplicitAnimation(const Animation& transition, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compAnim, RenderStyle* fromStyle) +ImplicitAnimation::ImplicitAnimation(const Animation& transition, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compAnim, const RenderStyle* fromStyle) : AnimationBase(transition, renderer, compAnim) + , m_fromStyle(RenderStyle::clonePtr(*fromStyle)) , m_transitionProperty(transition.property()) , m_animatingProperty(animatingProperty) - , m_overridden(false) - , m_active(true) - , m_fromStyle(fromStyle) { ASSERT(animatingProperty != CSSPropertyInvalid); } @@ -61,12 +61,14 @@ bool ImplicitAnimation::shouldSendEventForListener(Document::ListenerType inList return m_object->document().hasListenerType(inListenerType); } -void ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +bool ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const RenderStyle*, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) { // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // So just return. Everything is already all cleaned up. if (postActive()) - return; + return false; + + AnimationState oldState = state(); // Reset to start the transition if we are new if (isNew()) @@ -75,40 +77,64 @@ void ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const Rende // Run a cycle of animation. // We know we will need a new render style, so make one if needed if (!animatedStyle) - animatedStyle = RenderStyle::clone(targetStyle); + animatedStyle = RenderStyle::clonePtr(*targetStyle); -#if USE(ACCELERATED_COMPOSITING) - bool needsAnim = CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); + CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); // FIXME: we also need to detect cases where we have to software animate for other reasons, // such as a child using inheriting the transform. https://bugs.webkit.org/show_bug.cgi?id=23902 - if (!needsAnim) - // If we are running an accelerated animation, set a flag in the style which causes the style - // to compare as different to any other style. This ensures that changes to the property - // that is animating are correctly detected during the animation (e.g. when a transition - // gets interrupted). - animatedStyle->setIsRunningAcceleratedAnimation(); -#endif // Fire the start timeout if needed fireAnimationEventsIfNeeded(); + + didBlendStyle = true; + return state() != oldState; } -void ImplicitAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) +void ImplicitAnimation::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle) { if (!animatedStyle) - animatedStyle = RenderStyle::clone(m_toStyle.get()); + animatedStyle = RenderStyle::clonePtr(*m_toStyle); + + CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); +} + +bool ImplicitAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const +{ + ASSERT(hasStyle()); + + if (!is<RenderBox>(m_object)) + return true; // Non-boxes don't get transformed; - CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); + ASSERT(m_animatingProperty == CSSPropertyTransform); + + RenderBox& box = downcast<RenderBox>(*m_object); + FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor()); + + LayoutRect startBounds = bounds; + LayoutRect endBounds = bounds; + + if (transformFunctionListsMatch()) { + if (!computeTransformedExtentViaTransformList(rendererBox, *m_fromStyle, startBounds)) + return false; + + if (!computeTransformedExtentViaTransformList(rendererBox, *m_toStyle, endBounds)) + return false; + } else { + if (!computeTransformedExtentViaMatrix(rendererBox, *m_fromStyle, startBounds)) + return false; + + if (!computeTransformedExtentViaMatrix(rendererBox, *m_toStyle, endBounds)) + return false; + } + + bounds = unionRect(startBounds, endBounds); + return true; } bool ImplicitAnimation::startAnimation(double timeOffset) { -#if USE(ACCELERATED_COMPOSITING) if (m_object && m_object->isComposited()) - return toRenderBoxModelObject(m_object)->startTransition(timeOffset, m_animatingProperty, m_fromStyle.get(), m_toStyle.get()); -#else - UNUSED_PARAM(timeOffset); -#endif + return downcast<RenderBoxModelObject>(*m_object).startTransition(timeOffset, m_animatingProperty, m_fromStyle.get(), m_toStyle.get()); return false; } @@ -117,12 +143,8 @@ void ImplicitAnimation::pauseAnimation(double timeOffset) if (!m_object) return; -#if USE(ACCELERATED_COMPOSITING) if (m_object->isComposited()) - toRenderBoxModelObject(m_object)->transitionPaused(timeOffset, m_animatingProperty); -#else - UNUSED_PARAM(timeOffset); -#endif + downcast<RenderBoxModelObject>(*m_object).transitionPaused(timeOffset, m_animatingProperty); // Restore the original (unanimated) style if (!paused()) setNeedsStyleRecalc(m_object->element()); @@ -130,10 +152,8 @@ void ImplicitAnimation::pauseAnimation(double timeOffset) void ImplicitAnimation::endAnimation() { -#if USE(ACCELERATED_COMPOSITING) if (m_object && m_object->isComposited()) - toRenderBoxModelObject(m_object)->transitionFinished(m_animatingProperty); -#endif + downcast<RenderBoxModelObject>(*m_object).transitionFinished(m_animatingProperty); } void ImplicitAnimation::onAnimationEnd(double elapsedTime) @@ -143,10 +163,9 @@ void ImplicitAnimation::onAnimationEnd(double elapsedTime) // running. But now that the transition has completed, we need to update this style with its new // destination. If we didn't, the next time through we would think a transition had started // (comparing the old unanimated style with the new final style of the transition). - RefPtr<KeyframeAnimation> keyframeAnim = m_compAnim->getAnimationForProperty(m_animatingProperty); - if (keyframeAnim) - keyframeAnim->setUnanimatedStyle(m_toStyle); - + if (auto* animation = m_compositeAnimation->animationForProperty(m_animatingProperty)) + animation->setUnanimatedStyle(RenderStyle::clonePtr(*m_toStyle)); + sendTransitionEvent(eventNames().transitionendEvent, elapsedTime); endAnimation(); } @@ -162,12 +181,12 @@ bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, doubl // Dispatch the event RefPtr<Element> element = m_object->element(); - ASSERT(!element || !element->document().inPageCache()); + ASSERT(!element || element->document().pageCacheState() == Document::NotInPageCache); if (!element) return false; // Schedule event handling - m_compAnim->animationController()->addEventToDispatch(element, eventType, propertyName, elapsedTime); + m_compositeAnimation->animationController().addEventToDispatch(*element, eventType, propertyName, elapsedTime); // Restore the original (unanimated) style if (eventType == eventNames().transitionendEvent && element->renderer()) @@ -180,21 +199,25 @@ bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, doubl return false; // Didn't dispatch an event } -void ImplicitAnimation::reset(RenderStyle* to) +void ImplicitAnimation::reset(const RenderStyle* to) { ASSERT(to); ASSERT(m_fromStyle); - m_toStyle = to; + m_toStyle = RenderStyle::clonePtr(*to); + + if (m_object && m_object->element()) + Style::loadPendingResources(*m_toStyle, m_object->element()->document(), m_object->element()); // Restart the transition if (m_fromStyle && m_toStyle) - updateStateMachine(AnimationStateInputRestartAnimation, -1); + updateStateMachine(AnimationStateInput::RestartAnimation, -1); // set the transform animation list validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + checkForMatchingBackdropFilterFunctionLists(); #endif } @@ -204,7 +227,7 @@ void ImplicitAnimation::setOverridden(bool b) return; m_overridden = b; - updateStateMachine(m_overridden ? AnimationStateInputPauseOverride : AnimationStateInputResumeOverride, -1); + updateStateMachine(m_overridden ? AnimationStateInput::PauseOverride : AnimationStateInput::ResumeOverride, -1); } bool ImplicitAnimation::affectsProperty(CSSPropertyID property) const @@ -229,12 +252,12 @@ void ImplicitAnimation::blendPropertyValueInStyle(CSSPropertyID prop, RenderStyl if (!m_toStyle) return; - CSSPropertyAnimation::blendProperties(this, prop, currentStyle, m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); + CSSPropertyAnimation::blendProperties(this, prop, currentStyle, m_fromStyle.get(), m_toStyle.get(), progress()); } void ImplicitAnimation::validateTransformFunctionList() { - m_transformFunctionListValid = false; + m_transformFunctionListsMatch = false; if (!m_fromStyle || !m_toStyle) return; @@ -248,44 +271,53 @@ void ImplicitAnimation::validateTransformFunctionList() if (val->operations().isEmpty()) return; - // An emtpy transform list matches anything. + // An empty transform list matches anything. if (val != toVal && !toVal->operations().isEmpty() && !val->operationsMatch(*toVal)) return; // Transform lists match. - m_transformFunctionListValid = true; + m_transformFunctionListsMatch = true; +} + +static bool filterOperationsMatch(const FilterOperations* fromOperations, const FilterOperations& toOperations) +{ + if (fromOperations->operations().isEmpty()) + fromOperations = &toOperations; + + if (fromOperations->operations().isEmpty()) + return false; + + if (fromOperations != &toOperations && !toOperations.operations().isEmpty() && !fromOperations->operationsMatch(toOperations)) + return false; + + return true; } -#if ENABLE(CSS_FILTERS) void ImplicitAnimation::checkForMatchingFilterFunctionLists() { m_filterFunctionListsMatch = false; - + if (!m_fromStyle || !m_toStyle) return; - - const FilterOperations* val = &m_fromStyle->filter(); - const FilterOperations* toVal = &m_toStyle->filter(); - if (val->operations().isEmpty()) - val = toVal; + m_filterFunctionListsMatch = filterOperationsMatch(&m_fromStyle->filter(), m_toStyle->filter()); +} - if (val->operations().isEmpty()) - return; - - // An emtpy filter list matches anything. - if (val != toVal && !toVal->operations().isEmpty() && !val->operationsMatch(*toVal)) +#if ENABLE(FILTERS_LEVEL_2) +void ImplicitAnimation::checkForMatchingBackdropFilterFunctionLists() +{ + m_backdropFilterFunctionListsMatch = false; + + if (!m_fromStyle || !m_toStyle) return; - // Filter lists match. - m_filterFunctionListsMatch = true; + m_backdropFilterFunctionListsMatch = filterOperationsMatch(&m_fromStyle->backdropFilter(), m_toStyle->backdropFilter()); } #endif double ImplicitAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); -#if USE(ACCELERATED_COMPOSITING) if (t != 0 || preActive()) return t; @@ -295,7 +327,6 @@ double ImplicitAnimation::timeToNextService() bool isLooping; getTimeToNextEvent(t, isLooping); } -#endif return t; } diff --git a/Source/WebCore/page/animation/ImplicitAnimation.h b/Source/WebCore/page/animation/ImplicitAnimation.h index df651141e..9447417b0 100644 --- a/Source/WebCore/page/animation/ImplicitAnimation.h +++ b/Source/WebCore/page/animation/ImplicitAnimation.h @@ -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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ImplicitAnimation_h -#define ImplicitAnimation_h +#pragma once #include "AnimationBase.h" #include "CSSPropertyNames.h" @@ -41,27 +40,29 @@ class RenderElement; // for a single RenderElement. class ImplicitAnimation : public AnimationBase { public: - static PassRefPtr<ImplicitAnimation> create(const Animation& animation, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compositeAnimation, RenderStyle* fromStyle) + static Ref<ImplicitAnimation> create(const Animation& animation, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compositeAnimation, const RenderStyle* fromStyle) { - return adoptRef(new ImplicitAnimation(animation, animatingProperty, renderer, compositeAnimation, fromStyle)); + return adoptRef(*new ImplicitAnimation(animation, animatingProperty, renderer, compositeAnimation, fromStyle)); }; CSSPropertyID transitionProperty() const { return m_transitionProperty; } CSSPropertyID animatingProperty() const { return m_animatingProperty; } - virtual void onAnimationEnd(double elapsedTime) override; - virtual bool startAnimation(double timeOffset) override; - virtual void pauseAnimation(double timeOffset) override; - virtual void endAnimation() override; + void onAnimationEnd(double elapsedTime) override; + bool startAnimation(double timeOffset) override; + void pauseAnimation(double timeOffset) override; + void endAnimation() override; - virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override; - virtual void getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) override; - virtual void reset(RenderStyle* to); + bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) override; + void getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle) override; + void reset(const RenderStyle* to); + + bool computeExtentOfTransformAnimation(LayoutRect&) const override; void setOverridden(bool); - virtual bool overridden() const override { return m_overridden; } + bool overridden() const override { return m_overridden; } - virtual bool affectsProperty(CSSPropertyID) const override; + bool affectsProperty(CSSPropertyID) const override; bool hasStyle() const { return m_fromStyle && m_toStyle; } @@ -69,7 +70,7 @@ public: void blendPropertyValueInStyle(CSSPropertyID, RenderStyle*); - virtual double timeToNextService() override; + double timeToNextService() override; bool active() const { return m_active; } void setActive(bool b) { m_active = b; } @@ -79,24 +80,24 @@ protected: bool sendTransitionEvent(const AtomicString&, double elapsedTime); void validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) void checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + void checkForMatchingBackdropFilterFunctionLists(); #endif private: - ImplicitAnimation(const Animation&, CSSPropertyID, RenderElement*, CompositeAnimation*, RenderStyle*); + ImplicitAnimation(const Animation&, CSSPropertyID, RenderElement*, CompositeAnimation*, const RenderStyle*); virtual ~ImplicitAnimation(); + // The two styles that we are blending. + std::unique_ptr<RenderStyle> m_fromStyle; + std::unique_ptr<RenderStyle> m_toStyle; + CSSPropertyID m_transitionProperty; // Transition property as specified in the RenderStyle. CSSPropertyID m_animatingProperty; // Specific property for this ImplicitAnimation - bool m_overridden; // true when there is a keyframe animation that overrides the transitioning property - bool m_active; // used for culling the list of transitions - // The two styles that we are blending. - RefPtr<RenderStyle> m_fromStyle; - RefPtr<RenderStyle> m_toStyle; + bool m_active { true }; // Used for culling the list of transitions. + bool m_overridden { false }; // True when there is a keyframe animation that overrides the transitioning property }; } // namespace WebCore - -#endif // ImplicitAnimation_h diff --git a/Source/WebCore/page/animation/KeyframeAnimation.cpp b/Source/WebCore/page/animation/KeyframeAnimation.cpp index 8f661c6c3..5294b0a31 100644 --- a/Source/WebCore/page/animation/KeyframeAnimation.cpp +++ b/Source/WebCore/page/animation/KeyframeAnimation.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,33 +29,37 @@ #include "config.h" #include "KeyframeAnimation.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPropertyAnimation.h" #include "CSSPropertyNames.h" #include "CompositeAnimation.h" #include "EventNames.h" -#include "RenderBoxModelObject.h" +#include "GeometryUtilities.h" +#include "RenderBox.h" #include "RenderStyle.h" +#include "StylePendingResources.h" #include "StyleResolver.h" +#include "StyleScope.h" +#include "TranslateTransformOperation.h" +#include "WillChangeData.h" namespace WebCore { -KeyframeAnimation::KeyframeAnimation(const Animation& animation, RenderElement* renderer, int index, CompositeAnimation* compAnim, RenderStyle* unanimatedStyle) - : AnimationBase(animation, renderer, compAnim) +KeyframeAnimation::KeyframeAnimation(const Animation& animation, RenderElement* renderer, CompositeAnimation* compositeAnimation, const RenderStyle* unanimatedStyle) + : AnimationBase(animation, renderer, compositeAnimation) , m_keyframes(animation.name()) - , m_index(index) - , m_startEventDispatched(false) - , m_unanimatedStyle(unanimatedStyle) + , m_unanimatedStyle(RenderStyle::clonePtr(*unanimatedStyle)) { - // Get the keyframe RenderStyles - if (m_object && m_object->element()) - m_object->document().ensureStyleResolver().keyframeStylesForAnimation(m_object->element(), unanimatedStyle, m_keyframes); + resolveKeyframeStyles(); // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match. validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + checkForMatchingBackdropFilterFunctionLists(); #endif + computeStackingContextImpact(); + computeLayoutDependency(); } KeyframeAnimation::~KeyframeAnimation() @@ -65,38 +69,60 @@ KeyframeAnimation::~KeyframeAnimation() endAnimation(); } -static const Animation* getAnimationFromStyleByName(const RenderStyle* style, const AtomicString& name) +void KeyframeAnimation::computeStackingContextImpact() { - if (!style->animations()) - return 0; - - for (size_t i = 0; i < style->animations()->size(); i++) { - if (name == style->animations()->animation(i).name()) - return &style->animations()->animation(i); + for (auto propertyID : m_keyframes.properties()) { + if (WillChangeData::propertyCreatesStackingContext(propertyID)) { + m_triggersStackingContext = true; + break; + } } +} + +void KeyframeAnimation::computeLayoutDependency() +{ + if (!m_keyframes.containsProperty(CSSPropertyTransform)) + return; - return 0; + size_t numKeyframes = m_keyframes.size(); + for (size_t i = 0; i < numKeyframes; i++) { + auto* keyframeStyle = m_keyframes[i].style(); + if (!keyframeStyle) { + ASSERT_NOT_REACHED(); + continue; + } + if (keyframeStyle->hasTransform()) { + auto& transformOperations = keyframeStyle->transform(); + for (auto operation : transformOperations.operations()) { + if (operation->isTranslateTransformOperationType()) { + auto translation = downcast<TranslateTransformOperation>(operation.get()); + if (translation->x().isPercent() || translation->y().isPercent()) { + m_dependsOnLayout = true; + return; + } + } + } + } + } } void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const { + size_t numKeyframes = m_keyframes.size(); + if (!numKeyframes) + return; + // Find the first key double elapsedTime = getElapsedTime(); if (m_animation->duration() && m_animation->iterationCount() != Animation::IterationCountInfinite) elapsedTime = std::min(elapsedTime, m_animation->duration() * m_animation->iterationCount()); const double fractionalTime = this->fractionalTime(1, elapsedTime, 0); - - size_t numKeyframes = m_keyframes.size(); - if (!numKeyframes) - return; - ASSERT(!m_keyframes[0].key()); ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1); - + int prevIndex = -1; int nextIndex = -1; - // FIXME: with a lot of keys, this linear search will be slow. We could binary search. for (size_t i = 0; i < numKeyframes; ++i) { const KeyframeValue& currKeyFrame = m_keyframes[i]; @@ -108,16 +134,11 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property nextIndex = i; break; } - prevIndex = i; } - double scale = 1; - double offset = 0; - if (prevIndex == -1) prevIndex = 0; - if (nextIndex == -1) nextIndex = m_keyframes.size() - 1; @@ -126,32 +147,32 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property fromStyle = prevKeyframe.style(); toStyle = nextKeyframe.style(); - - offset = prevKeyframe.key(); - scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key()); - const TimingFunction* timingFunction = 0; - if (const Animation* matchedAnimation = getAnimationFromStyleByName(fromStyle, name())) - timingFunction = matchedAnimation->timingFunction().get(); + double offset = prevKeyframe.key(); + double scale = 1.0 / (nextIndex == prevIndex ? 1 : (nextKeyframe.key() - prevKeyframe.key())); - prog = progress(scale, offset, timingFunction); + prog = progress(scale, offset, prevKeyframe.timingFunction(name())); } -void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +bool KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderElement*, const RenderStyle*, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) { // Fire the start timeout if needed fireAnimationEventsIfNeeded(); // If we have not yet started, we will not have a valid start time, so just start the animation if needed. - if (isNew() && m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended()) - updateStateMachine(AnimationStateInputStartAnimation, -1); + if (isNew()) { + if (m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended()) + updateStateMachine(AnimationStateInput::StartAnimation, -1); + else if (m_animation->playState() == AnimPlayStatePaused) + updateStateMachine(AnimationStateInput::PlayStatePaused, -1); + } // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // If so, we need to send back the targetStyle. if (postActive()) { if (!animatedStyle) - animatedStyle = const_cast<RenderStyle*>(targetStyle); - return; + animatedStyle = RenderStyle::clonePtr(*targetStyle); + return false; } // If we are waiting for the start timer, we don't want to change the style yet. @@ -160,65 +181,95 @@ void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderEl // Special case 2 - if there is a backwards fill mode, then we want to continue // through to the style blend so that we get the fromStyle. if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) - return; + return false; // If we have no keyframes, don't animate. if (!m_keyframes.size()) { - updateStateMachine(AnimationStateInputEndAnimation, -1); - return; + updateStateMachine(AnimationStateInput::EndAnimation, -1); + return false; } + // FIXME: the code below never changes the state, so this function always returns false. + AnimationState oldState = state(); + // Run a cycle of animation. // We know we will need a new render style, so make one if needed. if (!animatedStyle) - animatedStyle = RenderStyle::clone(targetStyle); + animatedStyle = RenderStyle::clonePtr(*targetStyle); // FIXME: we need to be more efficient about determining which keyframes we are animating between. // We should cache the last pair or something. - HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { + for (auto propertyID : m_keyframes.properties()) { // Get the from/to styles and progress between - const RenderStyle* fromStyle = 0; - const RenderStyle* toStyle = 0; - double progress = 0.0; - fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress); - -#if USE(ACCELERATED_COMPOSITING) - bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); - if (!needsAnim) - // If we are running an accelerated animation, set a flag in the style - // to indicate it. This can be used to make sure we get an updated - // style for hit testing, etc. - animatedStyle->setIsRunningAcceleratedAnimation(); -#endif + const RenderStyle* fromStyle = nullptr; + const RenderStyle* toStyle = nullptr; + double progress = 0; + fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress); + + CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); } + + didBlendStyle = true; + return state() != oldState; } -void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) +void KeyframeAnimation::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle) { - // If we're in the delay phase and we're not backwards filling, tell the caller - // to use the current style. - if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) + // If we're done, or in the delay phase and we're not backwards filling, tell the caller to use the current style. + if (postActive() || (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())) return; if (!m_keyframes.size()) return; if (!animatedStyle) - animatedStyle = RenderStyle::clone(&m_object->style()); + animatedStyle = RenderStyle::clonePtr(m_object->style()); - HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { + for (auto propertyID : m_keyframes.properties()) { // Get the from/to styles and progress between - const RenderStyle* fromStyle = 0; - const RenderStyle* toStyle = 0; - double progress = 0.0; - fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress); + const RenderStyle* fromStyle = nullptr; + const RenderStyle* toStyle = nullptr; + double progress = 0; + fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress); - CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); + CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); } } +bool KeyframeAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const +{ + ASSERT(m_keyframes.containsProperty(CSSPropertyTransform)); + + if (!is<RenderBox>(m_object)) + return true; // Non-boxes don't get transformed; + + RenderBox& box = downcast<RenderBox>(*m_object); + FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor()); + + FloatRect cumulativeBounds = bounds; + + for (auto& keyframe : m_keyframes.keyframes()) { + if (!keyframe.containsProperty(CSSPropertyTransform)) + continue; + + LayoutRect keyframeBounds = bounds; + + bool canCompute; + if (transformFunctionListsMatch()) + canCompute = computeTransformedExtentViaTransformList(rendererBox, *keyframe.style(), keyframeBounds); + else + canCompute = computeTransformedExtentViaMatrix(rendererBox, *keyframe.style(), keyframeBounds); + + if (!canCompute) + return false; + + cumulativeBounds.unite(keyframeBounds); + } + + bounds = LayoutRect(cumulativeBounds); + return true; +} + bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const { return m_keyframes.containsProperty(property); @@ -226,13 +277,8 @@ bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const bool KeyframeAnimation::startAnimation(double timeOffset) { -#if USE(ACCELERATED_COMPOSITING) - if (m_object && m_object->isComposited()) { - return toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes); - } -#else - UNUSED_PARAM(timeOffset); -#endif + if (m_object && m_object->isComposited()) + return downcast<RenderBoxModelObject>(*m_object).startAnimation(timeOffset, m_animation.ptr(), m_keyframes); return false; } @@ -241,12 +287,9 @@ void KeyframeAnimation::pauseAnimation(double timeOffset) if (!m_object) return; -#if USE(ACCELERATED_COMPOSITING) if (m_object->isComposited()) - toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName()); -#else - UNUSED_PARAM(timeOffset); -#endif + downcast<RenderBoxModelObject>(*m_object).animationPaused(timeOffset, m_keyframes.animationName()); + // Restore the original (unanimated) style if (!paused()) setNeedsStyleRecalc(m_object->element()); @@ -257,10 +300,9 @@ void KeyframeAnimation::endAnimation() if (!m_object) return; -#if USE(ACCELERATED_COMPOSITING) if (m_object->isComposited()) - toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName()); -#endif + downcast<RenderBoxModelObject>(*m_object).animationFinished(m_keyframes.animationName()); + // Restore the original (unanimated) style if (!paused()) setNeedsStyleRecalc(m_object->element()); @@ -273,17 +315,17 @@ bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listen void KeyframeAnimation::onAnimationStart(double elapsedTime) { - sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime); + sendAnimationEvent(eventNames().animationstartEvent, elapsedTime); } void KeyframeAnimation::onAnimationIteration(double elapsedTime) { - sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime); + sendAnimationEvent(eventNames().animationiterationEvent, elapsedTime); } void KeyframeAnimation::onAnimationEnd(double elapsedTime) { - sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime); + sendAnimationEvent(eventNames().animationendEvent, elapsedTime); // End the animation if we don't fill forwards. Forward filling // animations are ended properly in the class destructor. if (!m_animation->fillsForwards()) @@ -293,12 +335,12 @@ void KeyframeAnimation::onAnimationEnd(double elapsedTime) bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime) { Document::ListenerType listenerType; - if (eventType == eventNames().webkitAnimationIterationEvent) + if (eventType == eventNames().webkitAnimationIterationEvent || eventType == eventNames().animationiterationEvent) listenerType = Document::ANIMATIONITERATION_LISTENER; - else if (eventType == eventNames().webkitAnimationEndEvent) + else if (eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent) listenerType = Document::ANIMATIONEND_LISTENER; else { - ASSERT(eventType == eventNames().webkitAnimationStartEvent); + ASSERT(eventType == eventNames().webkitAnimationStartEvent || eventType == eventNames().animationstartEvent); if (m_startEventDispatched) return false; m_startEventDispatched = true; @@ -309,15 +351,15 @@ bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double // Dispatch the event RefPtr<Element> element = m_object->element(); - ASSERT(!element || !element->document().inPageCache()); + ASSERT(!element || element->document().pageCacheState() == Document::NotInPageCache); if (!element) return false; // Schedule event handling - m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime); + m_compositeAnimation->animationController().addEventToDispatch(*element, eventType, m_keyframes.animationName(), elapsedTime); // Restore the original (unanimated) style - if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) + if ((eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent) && element->renderer()) setNeedsStyleRecalc(element.get()); return true; // Did dispatch an event @@ -329,17 +371,15 @@ bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double void KeyframeAnimation::overrideAnimations() { // This will override implicit animations that match the properties in the keyframe animation - HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) - compositeAnimation()->overrideImplicitAnimations(*it); + for (auto propertyID : m_keyframes.properties()) + compositeAnimation()->overrideImplicitAnimations(propertyID); } void KeyframeAnimation::resumeOverriddenAnimations() { // This will resume overridden implicit animations - HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) - compositeAnimation()->resumeOverriddenImplicitAnimations(*it); + for (auto propertyID : m_keyframes.properties()) + compositeAnimation()->resumeOverriddenImplicitAnimations(propertyID); } bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const @@ -347,11 +387,27 @@ bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const return m_keyframes.containsProperty(property); } +void KeyframeAnimation::resolveKeyframeStyles() +{ + if (!m_object || !m_object->element()) + return; + auto& element = *m_object->element(); + + if (auto* styleScope = Style::Scope::forOrdinal(element, m_animation->nameStyleScopeOrdinal())) + styleScope->resolver().keyframeStylesForAnimation(*m_object->element(), m_unanimatedStyle.get(), m_keyframes); + + // Ensure resource loads for all the frames. + for (auto& keyframe : m_keyframes.keyframes()) { + if (auto* style = const_cast<RenderStyle*>(keyframe.style())) + Style::loadPendingResources(*style, element.document(), &element); + } +} + void KeyframeAnimation::validateTransformFunctionList() { - m_transformFunctionListValid = false; + m_transformFunctionListsMatch = false; - if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform)) + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyTransform)) return; // Empty transforms match anything, so find the first non-empty entry as the reference @@ -384,16 +440,14 @@ void KeyframeAnimation::validateTransformFunctionList() return; } - // Keyframes are valid - m_transformFunctionListValid = true; + m_transformFunctionListsMatch = true; } -#if ENABLE(CSS_FILTERS) void KeyframeAnimation::checkForMatchingFilterFunctionLists() { m_filterFunctionListsMatch = false; - if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter)) + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyFilter)) return; // Empty filters match anything, so find the first non-empty entry as the reference @@ -401,8 +455,7 @@ void KeyframeAnimation::checkForMatchingFilterFunctionLists() size_t firstNonEmptyFilterKeyframeIndex = numKeyframes; for (size_t i = 0; i < numKeyframes; ++i) { - const KeyframeValue& currentKeyframe = m_keyframes[i]; - if (currentKeyframe.style()->filter().operations().size()) { + if (m_keyframes[i].style()->filter().operations().size()) { firstNonEmptyFilterKeyframeIndex = i; break; } @@ -410,39 +463,74 @@ void KeyframeAnimation::checkForMatchingFilterFunctionLists() if (firstNonEmptyFilterKeyframeIndex == numKeyframes) return; - - const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter(); + + auto& firstVal = m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter(); for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) { - const KeyframeValue& currentKeyframe = m_keyframes[i]; - const FilterOperations* val = ¤tKeyframe.style()->filter(); - + auto& value = m_keyframes[i].style()->filter(); + // An emtpy filter list matches anything. - if (val->operations().isEmpty()) + if (value.operations().isEmpty()) continue; - - if (!firstVal->operationsMatch(*val)) + + if (!firstVal.operationsMatch(value)) return; } - + m_filterFunctionListsMatch = true; } + +#if ENABLE(FILTERS_LEVEL_2) +void KeyframeAnimation::checkForMatchingBackdropFilterFunctionLists() +{ + m_backdropFilterFunctionListsMatch = false; + + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitBackdropFilter)) + return; + + // Empty filters match anything, so find the first non-empty entry as the reference + size_t numKeyframes = m_keyframes.size(); + size_t firstNonEmptyFilterKeyframeIndex = numKeyframes; + + for (size_t i = 0; i < numKeyframes; ++i) { + if (m_keyframes[i].style()->backdropFilter().operations().size()) { + firstNonEmptyFilterKeyframeIndex = i; + break; + } + } + + if (firstNonEmptyFilterKeyframeIndex == numKeyframes) + return; + + auto& firstVal = m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->backdropFilter(); + + for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) { + auto& value = m_keyframes[i].style()->backdropFilter(); + + // An emtpy filter list matches anything. + if (value.operations().isEmpty()) + continue; + + if (!firstVal.operationsMatch(value)) + return; + } + + m_backdropFilterFunctionListsMatch = true; +} #endif double KeyframeAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); -#if USE(ACCELERATED_COMPOSITING) if (t != 0 || preActive()) return t; // A return value of 0 means we need service. But if we only have accelerated animations we // only need service at the end of the transition - HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); bool acceleratedPropertiesOnly = true; - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { - if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isAccelerated()) { + for (auto propertyID : m_keyframes.properties()) { + if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(propertyID) || !isAccelerated()) { acceleratedPropertiesOnly = false; break; } @@ -452,7 +540,7 @@ double KeyframeAnimation::timeToNextService() bool isLooping; getTimeToNextEvent(t, isLooping); } -#endif + return t; } diff --git a/Source/WebCore/page/animation/KeyframeAnimation.h b/Source/WebCore/page/animation/KeyframeAnimation.h index 34ebd9951..8731d343c 100644 --- a/Source/WebCore/page/animation/KeyframeAnimation.h +++ b/Source/WebCore/page/animation/KeyframeAnimation.h @@ -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. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef KeyframeAnimation_h -#define KeyframeAnimation_h +#pragma once #include "AnimationBase.h" #include "Document.h" @@ -40,66 +39,72 @@ class RenderStyle; // A KeyframeAnimation tracks the state of an explicit animation for a single RenderElement. class KeyframeAnimation final : public AnimationBase { public: - static RefPtr<KeyframeAnimation> create(const Animation& animation, RenderElement* renderer, int index, CompositeAnimation* compositeAnimation, RenderStyle* unanimatedStyle) + static Ref<KeyframeAnimation> create(const Animation& animation, RenderElement* renderer, CompositeAnimation* compositeAnimation, const RenderStyle* unanimatedStyle) { - return adoptRef(new KeyframeAnimation(animation, renderer, index, compositeAnimation, unanimatedStyle)); + return adoptRef(*new KeyframeAnimation(animation, renderer, compositeAnimation, unanimatedStyle)); } - virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override; - virtual void getAnimatedStyle(RefPtr<RenderStyle>&) override; + bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) override; + void getAnimatedStyle(std::unique_ptr<RenderStyle>&) override; + + bool computeExtentOfTransformAnimation(LayoutRect&) const override; const KeyframeList& keyframes() const { return m_keyframes; } const AtomicString& name() const { return m_keyframes.animationName(); } - int index() const { return m_index; } - void setIndex(int i) { m_index = i; } bool hasAnimationForProperty(CSSPropertyID) const; - - void setUnanimatedStyle(PassRefPtr<RenderStyle> style) { m_unanimatedStyle = style; } + + bool triggersStackingContext() const { return m_triggersStackingContext; } + bool dependsOnLayout() const { return m_dependsOnLayout; } + + void setUnanimatedStyle(std::unique_ptr<RenderStyle> style) { m_unanimatedStyle = WTFMove(style); } RenderStyle* unanimatedStyle() const { return m_unanimatedStyle.get(); } - virtual double timeToNextService() override; + double timeToNextService() override; protected: - virtual void onAnimationStart(double elapsedTime) override; - virtual void onAnimationIteration(double elapsedTime) override; - virtual void onAnimationEnd(double elapsedTime) override; - virtual bool startAnimation(double timeOffset) override; - virtual void pauseAnimation(double timeOffset) override; - virtual void endAnimation() override; + void onAnimationStart(double elapsedTime) override; + void onAnimationIteration(double elapsedTime) override; + void onAnimationEnd(double elapsedTime) override; + bool startAnimation(double timeOffset) override; + void pauseAnimation(double timeOffset) override; + void endAnimation() override; - virtual void overrideAnimations() override; - virtual void resumeOverriddenAnimations() override; + void overrideAnimations() override; + void resumeOverriddenAnimations() override; bool shouldSendEventForListener(Document::ListenerType inListenerType) const; bool sendAnimationEvent(const AtomicString&, double elapsedTime); - virtual bool affectsProperty(CSSPropertyID) const override; + bool affectsProperty(CSSPropertyID) const override; + bool computeExtentOfAnimationForMatrixAnimation(const FloatRect& rendererBox, LayoutRect&) const; + + bool computeExtentOfAnimationForMatchingTransformLists(const FloatRect& rendererBox, LayoutRect&) const; + + void computeStackingContextImpact(); + void computeLayoutDependency(); + void resolveKeyframeStyles(); void validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) void checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + void checkForMatchingBackdropFilterFunctionLists(); #endif private: - KeyframeAnimation(const Animation&, RenderElement*, int index, CompositeAnimation*, RenderStyle* unanimatedStyle); + KeyframeAnimation(const Animation&, RenderElement*, CompositeAnimation*, const RenderStyle* unanimatedStyle); virtual ~KeyframeAnimation(); // Get the styles for the given property surrounding the current animation time and the progress between them. void fetchIntervalEndpointsForProperty(CSSPropertyID, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& progress) const; - // The keyframes that we are blending. KeyframeList m_keyframes; + std::unique_ptr<RenderStyle> m_unanimatedStyle; // The style just before we started animation - // The order in which this animation appears in the animation-name style. - int m_index; - bool m_startEventDispatched; - - // The style just before we started animation - RefPtr<RenderStyle> m_unanimatedStyle; + bool m_startEventDispatched { false }; + bool m_triggersStackingContext { false }; + bool m_dependsOnLayout { false }; }; } // namespace WebCore - -#endif // KeyframeAnimation_h |