Diff
Modified: trunk/Source/WebCore/ChangeLog (252252 => 252253)
--- trunk/Source/WebCore/ChangeLog 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/ChangeLog 2019-11-08 20:40:57 UTC (rev 252253)
@@ -1,3 +1,72 @@
+2019-11-08 Antoine Quint <grao...@apple.com>
+
+ [Web Animations] Use a keyframe effect stack to resolve animations on an element
+ https://bugs.webkit.org/show_bug.cgi?id=204010
+
+ Reviewed by Dean Jackson.
+
+ Until now, when resolving animations for an element, we would call animationsForElement() during each resolution which
+ means doing several hash table lookups to locate the various classes of animations for that given element, sorting each
+ of those animations and inserting them into a new Vector.
+
+ We now use a KeyframeEffectStack which keeps a list of KeyframeEffect objects that apply to a given target, provided the
+ effect also has a valid animation and that animation has a valid timeline, all pre-conditions for that effect to produce
+ an animated value. Any time one of those pre-conditions change, we update the membership of that effect in the stack.
+ The KeyframeEffectStack is a new member of ElementRareData.
+
+ Now, each time we resolve an animation for an element, we iterate over the KeyframeEffect objects returned by calling
+ sortEffects() on the KeyframeEffectStack which will sort the stack's effects only if a new effect had been added since
+ the last iteration, which means that simple animations that are not mutated will require sorting of the stack just once,
+ and the addition of several animations in a single animation frame will require sorting just once as well.
+
+ It was also found while doing this work that Style::TreeResolver::createAnimatedElementUpdate would call RenderStyle::clonePtr()
+ for any element that was part of a document containing a timeline, regardless of whether that element had any animations. Now
+ we check whether that element's KeyframeEffectStack contains any effects prior to cloning the style.
+
+ No tests or changes to existed test expectations as this should not yield any change in behavior.
+
+ * Sources.txt: Add the new KeyframeEffectStack.
+ * WebCore.xcodeproj/project.pbxproj:
+ * animation/AnimationEffect.h:
+ (WebCore::AnimationEffect::setAnimation):
+ * animation/AnimationTimeline.cpp:
+ (WebCore::AnimationTimeline::removeAnimation):
+ (WebCore::AnimationTimeline::updateCSSAnimationsForElement): Since we need to know the order of CSS @keyframes rules listed in animation-name
+ when sorting effects, we must compile the ordered list of those @keyframe rules as we update CSS animations for an element and store it on its
+ KeyframeEffectStack.
+ * animation/DocumentTimeline.cpp:
+ (WebCore::DocumentTimeline::resolveAnimationsForElement): Deleted. Replaced by Element::applyKeyframeEffects().
+ * animation/DocumentTimeline.h:
+ * animation/KeyframeEffect.cpp:
+ (WebCore::KeyframeEffect::animationTimelineDidChange):
+ (WebCore::KeyframeEffect::setAnimation):
+ (WebCore::KeyframeEffect::setTarget):
+ * animation/KeyframeEffect.h:
+ * animation/KeyframeEffectStack.cpp: Added.
+ (WebCore::KeyframeEffectStack::KeyframeEffectStack):
+ (WebCore::KeyframeEffectStack::~KeyframeEffectStack):
+ (WebCore::KeyframeEffectStack::addEffect):
+ (WebCore::KeyframeEffectStack::removeEffect):
+ (WebCore::KeyframeEffectStack::sortedEffects):
+ (WebCore::KeyframeEffectStack::ensureEffectsAreSorted):
+ (WebCore::KeyframeEffectStack::setCSSAnimationNames):
+ * animation/KeyframeEffectStack.h: Added.
+ (WebCore::KeyframeEffectStack::hasEffects const):
+ * animation/WebAnimation.cpp:
+ (WebCore::WebAnimation::setTimelineInternal):
+ (WebCore::WebAnimation::persist):
+ * dom/Element.cpp:
+ (WebCore::Element::ensureKeyframeEffectStack):
+ (WebCore::Element::hasKeyframeEffects const):
+ (WebCore::Element::applyKeyframeEffects):
+ * dom/Element.h:
+ * dom/ElementRareData.cpp:
+ * dom/ElementRareData.h:
+ (WebCore::ElementRareData::keyframeEffectStack):
+ (WebCore::ElementRareData::setKeyframeEffectStack):
+ * style/StyleTreeResolver.cpp:
+ (WebCore::Style::TreeResolver::createAnimatedElementUpdate):
+
2019-11-07 Dean Jackson <d...@apple.com>
Add ANGLE backend for iOS device
Modified: trunk/Source/WebCore/Sources.txt (252252 => 252253)
--- trunk/Source/WebCore/Sources.txt 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/Sources.txt 2019-11-08 20:40:57 UTC (rev 252253)
@@ -448,6 +448,7 @@
accessibility/isolatedtree/AXIsolatedTreeNode.cpp
animation/AnimationEffect.cpp
+animation/KeyframeEffectStack.cpp
animation/AnimationPlaybackEvent.cpp
animation/AnimationTimeline.cpp
animation/CSSAnimation.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (252252 => 252253)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2019-11-08 20:40:57 UTC (rev 252253)
@@ -2092,6 +2092,7 @@
71729F7E20F3BB4700801CE6 /* JSDocumentTimelineOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 71729F7C20F3BAB900801CE6 /* JSDocumentTimelineOptions.h */; };
71A1B6081DEE5AD70073BCFB /* modern-media-controls-localized-strings.js in Resources */ = {isa = PBXBuildFile; fileRef = 71A1B6061DEE5A820073BCFB /* modern-media-controls-localized-strings.js */; };
71A57DF2154BE25C0009D120 /* SVGPathUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 71A57DF0154BE25C0009D120 /* SVGPathUtilities.h */; };
+ 71A58196236F467600D81A24 /* KeyframeEffectStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 71A58193236F466400D81A24 /* KeyframeEffectStack.h */; settings = {ATTRIBUTES = (Private, ); }; };
71B28427203CEC4C0036AA5D /* JSCSSAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B28426203CEC0D0036AA5D /* JSCSSAnimation.h */; settings = {ATTRIBUTES = (Private, ); }; };
71B5AB2621F1D9F400376E5C /* PointerCaptureController.h in Headers */ = {isa = PBXBuildFile; fileRef = 71B5AB2421F1D9E200376E5C /* PointerCaptureController.h */; settings = {ATTRIBUTES = (Private, ); }; };
71B7EE0D21B5C6870031C1EF /* TouchAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 71AEE4EB21B5A49C00DDB036 /* TouchAction.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -9474,6 +9475,8 @@
71A1B6071DEE5A820073BCFB /* en */ = {isa = PBXFileReference; lastKnownFileType = sourcecode._javascript_; name = en; path = "en.lproj/modern-media-controls-localized-strings.js"; sourceTree = SOURCE_ROOT; };
71A57DEF154BE25C0009D120 /* SVGPathUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGPathUtilities.cpp; sourceTree = "<group>"; };
71A57DF0154BE25C0009D120 /* SVGPathUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGPathUtilities.h; sourceTree = "<group>"; };
+ 71A58193236F466400D81A24 /* KeyframeEffectStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyframeEffectStack.h; sourceTree = "<group>"; };
+ 71A58195236F466500D81A24 /* KeyframeEffectStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KeyframeEffectStack.cpp; sourceTree = "<group>"; };
71AEE4EB21B5A49C00DDB036 /* TouchAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchAction.h; sourceTree = "<group>"; };
71B0460A1DD3C2EE00EE19CF /* status-support.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode._javascript_; path = "status-support.js"; sourceTree = "<group>"; };
71B28424203CEC0B0036AA5D /* JSCSSAnimation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCSSAnimation.cpp; sourceTree = "<group>"; };
@@ -20854,6 +20857,8 @@
71556CAB1F9F099C00E78D08 /* KeyframeEffect.idl */,
71247E321FEA5F7F008C08CE /* KeyframeEffectOptions.h */,
71247E301FEA5F7E008C08CE /* KeyframeEffectOptions.idl */,
+ 71A58195236F466500D81A24 /* KeyframeEffectStack.cpp */,
+ 71A58193236F466400D81A24 /* KeyframeEffectStack.h */,
7120733D216DFAF100C78329 /* OptionalEffectTiming.h */,
7120733F216DFAF200C78329 /* OptionalEffectTiming.idl */,
712BE47E1FE8649D002031CC /* PlaybackDirection.h */,
@@ -30668,7 +30673,6 @@
77A17AA712F28B2A004E02F6 /* JSOESVertexArrayObject.h in Headers */,
FDF6BAF9134A4C9800822920 /* JSOfflineAudioCompletionEvent.h in Headers */,
FDA9326716703BA9008982DC /* JSOfflineAudioContext.h in Headers */,
- E45BA6AA2374926C004DFC07 /* MatchedDeclarationsCache.h in Headers */,
314877E61FAAB02500C05759 /* JSOffscreenCanvas.h in Headers */,
3140C5271FDF558200D2A873 /* JSOffscreenCanvasRenderingContext2D.h in Headers */,
57E233651DC7DB1F00F28D01 /* JsonWebKey.h in Headers */,
@@ -31082,6 +31086,7 @@
71247E391FEA5F86008C08CE /* KeyframeAnimationOptions.h in Headers */,
71556CB41F9F09BA00E78D08 /* KeyframeEffect.h in Headers */,
71247E3A1FEA5F86008C08CE /* KeyframeEffectOptions.h in Headers */,
+ 71A58196236F467600D81A24 /* KeyframeEffectStack.h in Headers */,
BC5EBA110E823E4700B25965 /* KeyframeList.h in Headers */,
E15FF7D518C9553800FE4C87 /* KeypressCommand.h in Headers */,
450CEBF115073BBE002BB149 /* LabelableElement.h in Headers */,
@@ -31173,6 +31178,7 @@
93309DF8099E64920056E581 /* markup.h in Headers */,
9728C3141268E4390041E89B /* MarkupAccumulator.h in Headers */,
00C60E3F13D76D7E0092A275 /* MarkupTokenizerInlines.h in Headers */,
+ E45BA6AA2374926C004DFC07 /* MatchedDeclarationsCache.h in Headers */,
FABE72F51059C1EB00D888CC /* MathMLAnnotationElement.h in Headers */,
FABE72F51059C1EB00D999DD /* MathMLElement.h in Headers */,
44A28AAC12DFB8AC00AE923B /* MathMLElementFactory.h in Headers */,
Modified: trunk/Source/WebCore/animation/AnimationEffect.h (252252 => 252253)
--- trunk/Source/WebCore/animation/AnimationEffect.h 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/AnimationEffect.h 2019-11-08 20:40:57 UTC (rev 252253)
@@ -47,7 +47,7 @@
namespace WebCore {
-class AnimationEffect : public RefCounted<AnimationEffect> {
+class AnimationEffect : public RefCounted<AnimationEffect>, public CanMakeWeakPtr<AnimationEffect> {
public:
virtual ~AnimationEffect();
@@ -62,9 +62,10 @@
virtual void invalidate() = 0;
virtual void animationDidSeek() = 0;
virtual void animationSuspensionStateDidChange(bool) = 0;
+ virtual void animationTimelineDidChange(AnimationTimeline*) = 0;
WebAnimation* animation() const { return m_animation.get(); }
- void setAnimation(WebAnimation* animation) { m_animation = makeWeakPtr(animation); }
+ virtual void setAnimation(WebAnimation* animation) { m_animation = makeWeakPtr(animation); }
Seconds delay() const { return m_delay; }
void setDelay(const Seconds&);
Modified: trunk/Source/WebCore/animation/AnimationTimeline.cpp (252252 => 252253)
--- trunk/Source/WebCore/animation/AnimationTimeline.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/AnimationTimeline.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -36,6 +36,7 @@
#include "DocumentTimeline.h"
#include "Element.h"
#include "KeyframeEffect.h"
+#include "KeyframeEffectStack.h"
#include "RenderStyle.h"
#include "RenderView.h"
#include "StylePropertyShorthand.h"
@@ -75,8 +76,10 @@
ASSERT(!animation.timeline() || animation.timeline() == this);
m_animations.remove(&animation);
if (is<KeyframeEffect>(animation.effect())) {
- if (auto* target = downcast<KeyframeEffect>(animation.effect())->target())
+ if (auto* target = downcast<KeyframeEffect>(animation.effect())->target()) {
animationWasRemovedFromElement(animation, *target);
+ target->ensureKeyframeEffectStack().removeEffect(*downcast<KeyframeEffect>(animation.effect()));
+ }
}
}
@@ -243,6 +246,8 @@
void AnimationTimeline::updateCSSAnimationsForElement(Element& element, const RenderStyle* currentStyle, const RenderStyle& afterChangeStyle)
{
+ Vector<String> animationNames;
+
// In case this element is newly getting a "display: none" we need to cancel all of its animations and disregard new ones.
if (currentStyle && currentStyle->hasAnimations() && currentStyle->display() != DisplayType::None && afterChangeStyle.display() == DisplayType::None) {
if (m_elementToCSSAnimationByName.contains(&element)) {
@@ -249,6 +254,7 @@
for (const auto& cssAnimationsByNameMapItem : m_elementToCSSAnimationByName.take(&element))
cancelDeclarativeAnimation(*cssAnimationsByNameMapItem.value);
}
+ element.ensureKeyframeEffectStack().setCSSAnimationNames(WTFMove(animationNames));
return;
}
@@ -275,6 +281,7 @@
for (size_t i = 0; i < currentAnimations->size(); ++i) {
auto& currentAnimation = currentAnimations->animation(i);
auto& name = currentAnimation.name();
+ animationNames.append(name);
if (namesOfPreviousAnimations.contains(name)) {
// We've found the name of this animation in our list of previous animations, this means we've already
// created a CSSAnimation object for it and need to ensure that this CSSAnimation is backed by the current
@@ -296,6 +303,8 @@
if (auto animation = cssAnimationsByName.take(nameOfAnimationToRemove))
cancelDeclarativeAnimation(*animation);
}
+
+ element.ensureKeyframeEffectStack().setCSSAnimationNames(WTFMove(animationNames));
}
RefPtr<WebAnimation> AnimationTimeline::cssAnimationForElementAndProperty(Element& element, CSSPropertyID property)
Modified: trunk/Source/WebCore/animation/DocumentTimeline.cpp (252252 => 252253)
--- trunk/Source/WebCore/animation/DocumentTimeline.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/DocumentTimeline.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -28,7 +28,6 @@
#include "AnimationPlaybackEvent.h"
#include "CSSAnimation.h"
-#include "CSSPropertyAnimation.h"
#include "CSSTransition.h"
#include "DOMWindow.h"
#include "DeclarativeAnimation.h"
@@ -673,32 +672,6 @@
}
}
-bool DocumentTimeline::resolveAnimationsForElement(Element& element, RenderStyle& targetStyle)
-{
- bool hasNonAcceleratedAnimationProperty = false;
-
- for (const auto& animation : animationsForElement(element)) {
- animation->resolve(targetStyle);
-
- if (hasNonAcceleratedAnimationProperty)
- continue;
-
- auto* effect = animation->effect();
- if (!effect || !is<KeyframeEffect>(effect))
- continue;
-
- auto* keyframeEffect = downcast<KeyframeEffect>(effect);
- for (auto cssPropertyId : keyframeEffect->animatedProperties()) {
- if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId)) {
- hasNonAcceleratedAnimationProperty = true;
- break;
- }
- }
- }
-
- return !hasNonAcceleratedAnimationProperty;
-}
-
bool DocumentTimeline::runningAnimationsForElementAreAllAccelerated(Element& element) const
{
return m_elementsWithRunningAcceleratedAnimations.contains(&element);
Modified: trunk/Source/WebCore/animation/DocumentTimeline.h (252252 => 252253)
--- trunk/Source/WebCore/animation/DocumentTimeline.h 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/DocumentTimeline.h 2019-11-08 20:40:57 UTC (rev 252253)
@@ -67,7 +67,6 @@
void animationAcceleratedRunningStateDidChange(WebAnimation&);
void applyPendingAcceleratedAnimations();
bool runningAnimationsForElementAreAllAccelerated(Element&) const;
- bool resolveAnimationsForElement(Element&, RenderStyle&);
void detachFromDocument();
void enqueueAnimationPlaybackEvent(AnimationPlaybackEvent&);
Modified: trunk/Source/WebCore/animation/KeyframeEffect.cpp (252252 => 252253)
--- trunk/Source/WebCore/animation/KeyframeEffect.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/KeyframeEffect.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -44,6 +44,7 @@
#include "JSCompositeOperationOrAuto.h"
#include "JSDOMConvert.h"
#include "JSKeyframeEffect.h"
+#include "KeyframeEffectStack.h"
#include "RenderBox.h"
#include "RenderBoxModelObject.h"
#include "RenderElement.h"
@@ -990,6 +991,29 @@
}
}
+void KeyframeEffect::animationTimelineDidChange(AnimationTimeline* timeline)
+{
+ if (!m_target)
+ return;
+
+ if (timeline)
+ m_target->ensureKeyframeEffectStack().addEffect(*this);
+ else
+ m_target->ensureKeyframeEffectStack().removeEffect(*this);
+}
+
+void KeyframeEffect::setAnimation(WebAnimation* animation)
+{
+ bool animationChanged = animation != this->animation();
+ AnimationEffect::setAnimation(animation);
+ if (m_target && animationChanged) {
+ if (animation)
+ m_target->ensureKeyframeEffectStack().addEffect(*this);
+ else
+ m_target->ensureKeyframeEffectStack().removeEffect(*this);
+ }
+}
+
void KeyframeEffect::setTarget(RefPtr<Element>&& newTarget)
{
if (m_target == newTarget)
@@ -1009,6 +1033,11 @@
// Likewise, we need to invalidate styles on the previous target so that
// any animated styles are removed immediately.
invalidateElement(previousTarget.get());
+
+ if (previousTarget)
+ previousTarget->ensureKeyframeEffectStack().removeEffect(*this);
+ if (m_target)
+ m_target->ensureKeyframeEffectStack().addEffect(*this);
}
void KeyframeEffect::apply(RenderStyle& targetStyle)
Modified: trunk/Source/WebCore/animation/KeyframeEffect.h (252252 => 252253)
--- trunk/Source/WebCore/animation/KeyframeEffect.h 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/KeyframeEffect.h 2019-11-08 20:40:57 UTC (rev 252253)
@@ -114,10 +114,13 @@
void invalidate() override;
void animationDidSeek() final;
void animationSuspensionStateDidChange(bool) final;
+ void animationTimelineDidChange(AnimationTimeline*) final;
void applyPendingAcceleratedActions();
bool isRunningAccelerated() const { return m_lastRecordedAcceleratedAction != AcceleratedAction::Stop; }
bool hasPendingAcceleratedAction() const { return !m_pendingAcceleratedActions.isEmpty() && isRunningAccelerated(); }
+ void setAnimation(WebAnimation*) final;
+
RenderElement* renderer() const override;
const RenderStyle& currentStyle() const override;
bool isAccelerated() const override { return m_shouldRunAccelerated; }
Added: trunk/Source/WebCore/animation/KeyframeEffectStack.cpp (0 => 252253)
--- trunk/Source/WebCore/animation/KeyframeEffectStack.cpp (rev 0)
+++ trunk/Source/WebCore/animation/KeyframeEffectStack.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "KeyframeEffectStack.h"
+
+#include "CSSAnimation.h"
+#include "CSSTransition.h"
+#include "KeyframeEffect.h"
+#include "WebAnimation.h"
+
+namespace WebCore {
+
+KeyframeEffectStack::KeyframeEffectStack()
+{
+}
+
+KeyframeEffectStack::~KeyframeEffectStack()
+{
+ ASSERT(m_effects.isEmpty());
+}
+
+void KeyframeEffectStack::addEffect(KeyframeEffect& effect)
+{
+ // To qualify for membership in an effect stack, an effect must have a target, an animation and a timeline.
+ // This method will be called in WebAnimation and KeyframeEffect as those properties change.
+ if (!effect.target() || !effect.animation() || !effect.animation()->timeline())
+ return;
+
+ m_effects.append(makeWeakPtr(&effect));
+ m_isSorted = false;
+}
+
+void KeyframeEffectStack::removeEffect(KeyframeEffect& effect)
+{
+ m_effects.removeFirst(&effect);
+}
+
+Vector<WeakPtr<KeyframeEffect>> KeyframeEffectStack::sortedEffects()
+{
+ ensureEffectsAreSorted();
+ return m_effects;
+}
+
+void KeyframeEffectStack::ensureEffectsAreSorted()
+{
+ if (m_isSorted || m_effects.size() < 2)
+ return;
+
+ std::sort(m_effects.begin(), m_effects.end(), [&](auto& lhs, auto& rhs) {
+ auto* lhsAnimation = lhs->animation();
+ auto* rhsAnimation = rhs->animation();
+
+ ASSERT(lhsAnimation);
+ ASSERT(rhsAnimation);
+
+ // CSS Transitions sort first.
+ bool lhsIsCSSTransition = is<CSSTransition>(lhsAnimation);
+ bool rhsIsCSSTransition = is<CSSTransition>(rhsAnimation);
+ if (lhsIsCSSTransition || rhsIsCSSTransition) {
+ if (lhsIsCSSTransition == rhsIsCSSTransition) {
+ // Sort transitions first by their generation time, and then by transition-property.
+ // https://drafts.csswg.org/css-transitions-2/#animation-composite-order
+ auto* lhsCSSTransition = downcast<CSSTransition>(lhsAnimation);
+ auto* rhsCSSTransition = downcast<CSSTransition>(rhsAnimation);
+ if (lhsCSSTransition->generationTime() != rhsCSSTransition->generationTime())
+ return lhsCSSTransition->generationTime() < rhsCSSTransition->generationTime();
+ return lhsCSSTransition->transitionProperty().utf8() < rhsCSSTransition->transitionProperty().utf8();
+ }
+ return !rhsIsCSSTransition;
+ }
+
+ // CSS Animations sort next.
+ bool lhsIsCSSAnimation = is<CSSAnimation>(lhsAnimation);
+ bool rhsIsCSSAnimation = is<CSSAnimation>(rhsAnimation);
+ if (lhsIsCSSAnimation || rhsIsCSSAnimation) {
+ if (lhsIsCSSAnimation == rhsIsCSSAnimation) {
+ // https://drafts.csswg.org/css-animations-2/#animation-composite-order
+ // Sort A and B based on their position in the computed value of the animation-name property of the (common) owning element.
+ auto& lhsCSSAnimationName = downcast<CSSAnimation>(lhsAnimation)->backingAnimation().name();
+ auto& rhsCSSAnimationName = downcast<CSSAnimation>(rhsAnimation)->backingAnimation().name();
+
+ for (auto& animationName : m_cssAnimationNames) {
+ if (animationName == lhsCSSAnimationName)
+ return true;
+ if (animationName == rhsCSSAnimationName)
+ return false;
+ }
+ // We should have found either of those CSS animations in the CSS animations list.
+ ASSERT_NOT_REACHED();
+ }
+ return !rhsIsCSSAnimation;
+ }
+
+ // JS-originated animations sort last based on their position in the global animation list.
+ // https://drafts.csswg.org/web-animations-1/#animation-composite-order
+ return lhsAnimation->globalPosition() < rhsAnimation->globalPosition();
+ });
+
+ m_isSorted = true;
+}
+
+void KeyframeEffectStack::setCSSAnimationNames(Vector<String>&& animationNames)
+{
+ m_cssAnimationNames = WTFMove(animationNames);
+ // Since the list of animation names has changed, the sorting order of the animation effects may have changed as well.
+ m_isSorted = false;
+}
+
+} // namespace WebCore
Added: trunk/Source/WebCore/animation/KeyframeEffectStack.h (0 => 252253)
--- trunk/Source/WebCore/animation/KeyframeEffectStack.h (rev 0)
+++ trunk/Source/WebCore/animation/KeyframeEffectStack.h 2019-11-08 20:40:57 UTC (rev 252253)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+
+class KeyframeEffect;
+
+class KeyframeEffectStack {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ explicit KeyframeEffectStack();
+ ~KeyframeEffectStack();
+
+ void addEffect(KeyframeEffect&);
+ void removeEffect(KeyframeEffect&);
+ bool hasEffects() const { return !m_effects.isEmpty(); }
+ Vector<WeakPtr<KeyframeEffect>> sortedEffects();
+ void setCSSAnimationNames(Vector<String>&&);
+
+private:
+ void ensureEffectsAreSorted();
+
+ Vector<WeakPtr<KeyframeEffect>> m_effects;
+ Vector<String> m_cssAnimationNames;
+ bool m_isSorted { true };
+
+};
+
+} // namespace WebCore
Modified: trunk/Source/WebCore/animation/WebAnimation.cpp (252252 => 252253)
--- trunk/Source/WebCore/animation/WebAnimation.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/animation/WebAnimation.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -237,6 +237,9 @@
m_timeline->removeAnimation(*this);
m_timeline = WTFMove(timeline);
+
+ if (m_effect)
+ m_effect->animationTimelineDidChange(m_timeline.get());
}
void WebAnimation::effectTargetDidChange(Element* previousTarget, Element* newTarget)
@@ -1258,8 +1261,12 @@
auto previousReplaceState = std::exchange(m_replaceState, ReplaceState::Persisted);
if (previousReplaceState == ReplaceState::Removed && m_timeline) {
- if (is<KeyframeEffect>(m_effect))
- m_timeline->animationWasAddedToElement(*this, *downcast<KeyframeEffect>(m_effect.get())->target());
+ if (is<KeyframeEffect>(m_effect)) {
+ auto& keyframeEffect = downcast<KeyframeEffect>(*m_effect);
+ auto& target = *keyframeEffect.target();
+ m_timeline->animationWasAddedToElement(*this, target);
+ target.ensureKeyframeEffectStack().addEffect(keyframeEffect);
+ }
}
}
Modified: trunk/Source/WebCore/dom/Element.cpp (252252 => 252253)
--- trunk/Source/WebCore/dom/Element.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/dom/Element.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -31,6 +31,7 @@
#include "AttributeChangeInvalidation.h"
#include "CSSAnimationController.h"
#include "CSSParser.h"
+#include "CSSPropertyAnimation.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "ClassChangeInvalidation.h"
@@ -3677,6 +3678,47 @@
}
#endif
+KeyframeEffectStack& Element::ensureKeyframeEffectStack()
+{
+ auto& rareData = ensureElementRareData();
+ if (!rareData.keyframeEffectStack())
+ rareData.setKeyframeEffectStack(makeUnique<KeyframeEffectStack>());
+ return *rareData.keyframeEffectStack();
+}
+
+bool Element::hasKeyframeEffects() const
+{
+ if (!hasRareData())
+ return false;
+
+ auto* keyframeEffectStack = elementRareData()->keyframeEffectStack();
+ return keyframeEffectStack && keyframeEffectStack->hasEffects();
+}
+
+bool Element::applyKeyframeEffects(RenderStyle& targetStyle)
+{
+ bool hasNonAcceleratedAnimationProperty = false;
+
+ for (const auto& effect : ensureKeyframeEffectStack().sortedEffects()) {
+ ASSERT(effect->animation());
+ effect->animation()->resolve(targetStyle);
+
+ if (hasNonAcceleratedAnimationProperty)
+ continue;
+
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=204009
+ // KeyframeEffectStack and KeyframeEffect should indicate whether it only contains accelerated animation properties
+ for (auto cssPropertyId : effect->animatedProperties()) {
+ if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId)) {
+ hasNonAcceleratedAnimationProperty = true;
+ break;
+ }
+ }
+ }
+
+ return !hasNonAcceleratedAnimationProperty;
+}
+
#if ENABLE(RESIZE_OBSERVER)
void Element::disconnectFromResizeObservers()
{
Modified: trunk/Source/WebCore/dom/Element.h (252252 => 252253)
--- trunk/Source/WebCore/dom/Element.h 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/dom/Element.h 2019-11-08 20:40:57 UTC (rev 252253)
@@ -47,6 +47,7 @@
class HTMLDocument;
class IntSize;
class JSCustomElementInterface;
+class KeyframeEffectStack;
class KeyboardEvent;
class Locale;
class PlatformKeyboardEvent;
@@ -489,6 +490,10 @@
void setHasCSSAnimation();
void clearHasCSSAnimation();
+ KeyframeEffectStack& ensureKeyframeEffectStack();
+ bool hasKeyframeEffects() const;
+ bool applyKeyframeEffects(RenderStyle&);
+
#if ENABLE(FULLSCREEN_API)
WEBCORE_EXPORT bool containsFullScreenElement() const;
void setContainsFullScreenElement(bool);
Modified: trunk/Source/WebCore/dom/ElementRareData.cpp (252252 => 252253)
--- trunk/Source/WebCore/dom/ElementRareData.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/dom/ElementRareData.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -43,7 +43,7 @@
#endif
LayoutSize sizeForResizing;
IntPoint savedLayerScrollPosition;
- void* pointers[10];
+ void* pointers[11];
#if ENABLE(INTERSECTION_OBSERVER)
void* intersectionObserverData;
#endif
Modified: trunk/Source/WebCore/dom/ElementRareData.h (252252 => 252253)
--- trunk/Source/WebCore/dom/ElementRareData.h 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/dom/ElementRareData.h 2019-11-08 20:40:57 UTC (rev 252253)
@@ -25,6 +25,7 @@
#include "DOMTokenList.h"
#include "DatasetDOMStringMap.h"
#include "IntersectionObserver.h"
+#include "KeyframeEffectStack.h"
#include "NamedNodeMap.h"
#include "NodeRareData.h"
#include "PseudoElement.h"
@@ -100,6 +101,9 @@
bool hasCSSAnimation() const { return m_hasCSSAnimation; }
void setHasCSSAnimation(bool value) { m_hasCSSAnimation = value; }
+ KeyframeEffectStack* keyframeEffectStack() { return m_keyframeEffectStack.get(); }
+ void setKeyframeEffectStack(std::unique_ptr<KeyframeEffectStack>&& keyframeEffectStack) { m_keyframeEffectStack = WTFMove(keyframeEffectStack); }
+
bool hasElementIdentifier() const { return m_hasElementIdentifier; }
void setHasElementIdentifier(bool value) { m_hasElementIdentifier = value; }
@@ -186,6 +190,8 @@
std::unique_ptr<ResizeObserverData> m_resizeObserverData;
#endif
+ std::unique_ptr<KeyframeEffectStack> m_keyframeEffectStack;
+
RefPtr<PseudoElement> m_beforePseudoElement;
RefPtr<PseudoElement> m_afterPseudoElement;
Modified: trunk/Source/WebCore/style/StyleTreeResolver.cpp (252252 => 252253)
--- trunk/Source/WebCore/style/StyleTreeResolver.cpp 2019-11-08 20:00:32 UTC (rev 252252)
+++ trunk/Source/WebCore/style/StyleTreeResolver.cpp 2019-11-08 20:40:57 UTC (rev 252253)
@@ -317,11 +317,11 @@
}
}
- if (auto timeline = m_document.existingTimeline()) {
- // Now we can update all Web animations, which will include CSS Animations as well
- // as animations created via the JS API.
+ // Now we can update all Web animations, which will include CSS Animations as well
+ // as animations created via the JS API.
+ if (element.hasKeyframeEffects()) {
auto animatedStyle = RenderStyle::clonePtr(*newStyle);
- shouldRecompositeLayer = timeline->resolveAnimationsForElement(element, *animatedStyle);
+ shouldRecompositeLayer = element.applyKeyframeEffects(*animatedStyle);
newStyle = WTFMove(animatedStyle);
}