diff options
Diffstat (limited to 'Source/WebCore/page/animation/KeyframeAnimation.cpp')
-rw-r--r-- | Source/WebCore/page/animation/KeyframeAnimation.cpp | 342 |
1 files changed, 215 insertions, 127 deletions
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; } |