Title: [252253] trunk/Source/WebCore
Revision
252253
Author
grao...@webkit.org
Date
2019-11-08 12:40:57 -0800 (Fri, 08 Nov 2019)

Log Message

[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):

Modified Paths

Added Paths

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);
     }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to