Eliminate styleDidChange with StyleDifferenceEqual when updates are actually necessary
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Apr 2015 04:59:48 +0000 (04:59 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Apr 2015 04:59:48 +0000 (04:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=144198

Reviewed by Darin Adler, Antti Koivisto.

Source/WebCore:

SyntheticStyleChange style recalcs are triggered for cases where behavior depends
on state which is outside of RenderStyle; this includes triggering compositing for
animations, for video and canvas, and for iframes with composited content.

In these cases, we'd run through RenderElement::setStyle() and its fan-out, but
with diff == StyleDifferenceEqual, and so be unable to determine if there
is actual work to be done.

This patch enforces the contract that the diff is never StyleDifferenceEqual if
compositing or other work has to happen from setStyle(). This is achieved by
passing in a 'hasSideEffects' flag, which causes the diff to become at least
StyleDifferenceRecompositeLayer.

RenderLayerCompositor::layerStyleChanged() can now safely early return
if the diff is equal. Future patches will reduce redundant work even more.

Test: compositing/animation/no-style-recalc-during-accelerated-animation.html

* page/animation/AnimationBase.h:
(WebCore::AnimationBase::animate): Returns a bool now if the state changed.
(WebCore::AnimationBase::state):
* page/animation/AnimationController.cpp:
(WebCore::AnimationController::updateAnimations): bool out param which indicates
whether any animations changed state.
* page/animation/AnimationController.h:
* page/animation/CompositeAnimation.cpp:
(WebCore::CompositeAnimation::animate): If any transitions or animations changed
state, set the animationStateChanged out param to true.
* page/animation/CompositeAnimation.h:
* page/animation/ImplicitAnimation.cpp:
(WebCore::ImplicitAnimation::animate): Return true if the state changed.
* page/animation/ImplicitAnimation.h:
* page/animation/KeyframeAnimation.cpp:
(WebCore::KeyframeAnimation::animate): Return true if the state changed.
* page/animation/KeyframeAnimation.h:
* rendering/RenderElement.cpp:
(WebCore::RenderElement::adjustStyleDifference): We may enter here now with diff
!= StyleDifferenceEqual, but still need to do the check to see if layers changed.
(WebCore::RenderElement::initializeStyle): When setting style for the first time,
don't use StyleDifferenceEqual.
(WebCore::RenderElement::setStyle): Additional flag to indicate whether this style
change involves side effects. If the diff is equal but the flag is set, change
the diff to StyleDifferenceRecompositeLayer (the "lowest" non-zero diff).
* rendering/RenderElement.h:
(WebCore::RenderElement::setAnimatableStyle): Pass true to setStyle() if hasSideEffects
is true, or if animation state changed.
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::styleChanged): Pass the diff down.
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::layerStyleChanged): Return if the diff is equal.
* rendering/RenderLayerCompositor.h:
* rendering/style/RenderStyleConstants.h: StyleDifferenceNewStyle is used when
setting style for the first time.
* style/StyleResolveTree.cpp:
(WebCore::Style::createRendererIfNeeded): Provide animationsChanged bool (which is unused).
(WebCore::Style::resolveLocal): If the style change is synthetic, set the flag that
says there are side-effects.

LayoutTests:

New test that detects whether a "hardware" animation is firing the style recalc
timer on every frame, which happened during development of this patch.

* compositing/animation/no-style-recalc-during-accelerated-animation-expected.txt: Added.
* compositing/animation/no-style-recalc-during-accelerated-animation.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@183454 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
Source/WebCore/ChangeLog
Source/WebCore/page/animation/AnimationBase.h
Source/WebCore/page/animation/AnimationController.cpp
Source/WebCore/page/animation/AnimationController.h
Source/WebCore/page/animation/CompositeAnimation.cpp
Source/WebCore/page/animation/CompositeAnimation.h
Source/WebCore/page/animation/ImplicitAnimation.cpp
Source/WebCore/page/animation/ImplicitAnimation.h
Source/WebCore/page/animation/KeyframeAnimation.cpp
Source/WebCore/page/animation/KeyframeAnimation.h
Source/WebCore/rendering/RenderElement.cpp
Source/WebCore/rendering/RenderElement.h
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayerCompositor.cpp
Source/WebCore/rendering/RenderLayerCompositor.h
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/RenderStyleConstants.h
Source/WebCore/rendering/style/StyleRareNonInheritedData.h
Source/WebCore/style/StyleResolveTree.cpp

index aa9d8ad..74a3e55 100644 (file)
@@ -1,3 +1,16 @@
+2015-04-25  Simon Fraser  <simon.fraser@apple.com>
+
+        Eliminate styleDidChange with StyleDifferenceEqual when updates are actually necessary
+        https://bugs.webkit.org/show_bug.cgi?id=144198
+
+        Reviewed by Darin Adler, Antti Koivisto.
+        
+        New test that detects whether a "hardware" animation is firing the style recalc
+        timer on every frame, which happened during development of this patch.
+
+        * compositing/animation/no-style-recalc-during-accelerated-animation-expected.txt: Added.
+        * compositing/animation/no-style-recalc-during-accelerated-animation.html: Added.
+
 2015-04-27  Benjamin Poulain  <bpoulain@apple.com>
 
         [JSC] Add support for typed arrays to the Array profiling
index 5999616..a7fa668 100644 (file)
@@ -1,3 +1,68 @@
+2015-04-25  Simon Fraser  <simon.fraser@apple.com>
+
+        Eliminate styleDidChange with StyleDifferenceEqual when updates are actually necessary
+        https://bugs.webkit.org/show_bug.cgi?id=144198
+
+        Reviewed by Darin Adler, Antti Koivisto.
+        
+        SyntheticStyleChange style recalcs are triggered for cases where behavior depends
+        on state which is outside of RenderStyle; this includes triggering compositing for
+        animations, for video and canvas, and for iframes with composited content.
+        
+        In these cases, we'd run through RenderElement::setStyle() and its fan-out, but
+        with diff == StyleDifferenceEqual, and so be unable to determine if there
+        is actual work to be done.
+        
+        This patch enforces the contract that the diff is never StyleDifferenceEqual if
+        compositing or other work has to happen from setStyle(). This is achieved by
+        passing in a 'hasSideEffects' flag, which causes the diff to become at least
+        StyleDifferenceRecompositeLayer.
+        
+        RenderLayerCompositor::layerStyleChanged() can now safely early return
+        if the diff is equal. Future patches will reduce redundant work even more.
+
+        Test: compositing/animation/no-style-recalc-during-accelerated-animation.html
+
+        * page/animation/AnimationBase.h:
+        (WebCore::AnimationBase::animate): Returns a bool now if the state changed.
+        (WebCore::AnimationBase::state):
+        * page/animation/AnimationController.cpp:
+        (WebCore::AnimationController::updateAnimations): bool out param which indicates
+        whether any animations changed state.
+        * page/animation/AnimationController.h:
+        * page/animation/CompositeAnimation.cpp:
+        (WebCore::CompositeAnimation::animate): If any transitions or animations changed
+        state, set the animationStateChanged out param to true.
+        * page/animation/CompositeAnimation.h:
+        * page/animation/ImplicitAnimation.cpp:
+        (WebCore::ImplicitAnimation::animate): Return true if the state changed.
+        * page/animation/ImplicitAnimation.h:
+        * page/animation/KeyframeAnimation.cpp:
+        (WebCore::KeyframeAnimation::animate): Return true if the state changed.
+        * page/animation/KeyframeAnimation.h:
+        * rendering/RenderElement.cpp:
+        (WebCore::RenderElement::adjustStyleDifference): We may enter here now with diff
+        != StyleDifferenceEqual, but still need to do the check to see if layers changed.
+        (WebCore::RenderElement::initializeStyle): When setting style for the first time,
+        don't use StyleDifferenceEqual.
+        (WebCore::RenderElement::setStyle): Additional flag to indicate whether this style
+        change involves side effects. If the diff is equal but the flag is set, change
+        the diff to StyleDifferenceRecompositeLayer (the "lowest" non-zero diff).
+        * rendering/RenderElement.h:
+        (WebCore::RenderElement::setAnimatableStyle): Pass true to setStyle() if hasSideEffects
+        is true, or if animation state changed.
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::styleChanged): Pass the diff down.
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::layerStyleChanged): Return if the diff is equal.
+        * rendering/RenderLayerCompositor.h:
+        * rendering/style/RenderStyleConstants.h: StyleDifferenceNewStyle is used when
+        setting style for the first time.
+        * style/StyleResolveTree.cpp:
+        (WebCore::Style::createRendererIfNeeded): Provide animationsChanged bool (which is unused).
+        (WebCore::Style::resolveLocal): If the style change is synthetic, set the flag that
+        says there are side-effects.
+
 2015-04-27  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [GTK] Add one single option to control all OpenGL-related options
index 354da40..8d2cf0e 100644 (file)
@@ -135,7 +135,8 @@ public:
 
     double progress(double scale, double offset, const TimingFunction*) const;
 
-    virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* /*currentStyle*/, RenderStyle* /*targetStyle*/, RefPtr<RenderStyle>& /*animatedStyle*/) = 0;
+    // Returns true if the animation state changed.
+    virtual bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* /*currentStyle*/, RenderStyle* /*targetStyle*/, RefPtr<RenderStyle>& /*animatedStyle*/) = 0;
     virtual void getAnimatedStyle(RefPtr<RenderStyle>& /*animatedStyle*/) = 0;
 
     virtual bool computeExtentOfTransformAnimation(LayoutRect&) const = 0;
@@ -232,6 +233,7 @@ protected:
     void goIntoEndingOrLoopingState();
 
     bool isAccelerated() const { return m_isAccelerated; }
+    AnimationState state() const { return m_animationState; }
 
     static void setNeedsStyleRecalc(Element*);
     
index 2a8403f..c547623 100644 (file)
@@ -568,20 +568,18 @@ void AnimationController::cancelAnimations(RenderElement& renderer)
         element->setNeedsStyleRecalc(SyntheticStyleChange);
 }
 
-Ref<RenderStyle> AnimationController::updateAnimations(RenderElement& renderer, Ref<RenderStyle>&& newStyle)
+bool AnimationController::updateAnimations(RenderElement& renderer, RenderStyle& newStyle, Ref<RenderStyle>& animatedStyle)
 {
-    // Don't do anything if we're in the cache
-    if (renderer.document().inPageCache())
-        return WTF::move(newStyle);
-
     RenderStyle* oldStyle = renderer.hasInitializedStyle() ? &renderer.style() : nullptr;
+    if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.animations() && !newStyle.transitions()))
+        return false;
 
-    if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.get().animations() && !newStyle.get().transitions()))
-        return WTF::move(newStyle);
+    if (renderer.document().inPageCache())
+        return false;
 
     // Don't run transitions when printing.
     if (renderer.view().printing())
-        return WTF::move(newStyle);
+        return false;
 
     // Fetch our current set of implicit animations from a hashtable.  We then compare them
     // against the animations in the style and make sure we're in sync.  If destination values
@@ -591,26 +589,24 @@ Ref<RenderStyle> AnimationController::updateAnimations(RenderElement& renderer,
     // We don't support anonymous pseudo elements like :first-line or :first-letter.
     ASSERT(renderer.element());
 
-    Ref<RenderStyle> newStyleBeforeAnimation(WTF::move(newStyle));
-
     CompositeAnimation& rendererAnimations = m_data->ensureCompositeAnimation(renderer);
-    auto blendedStyle = rendererAnimations.animate(renderer, oldStyle, newStyleBeforeAnimation);
+    bool animationStateChanged = rendererAnimations.animate(renderer, oldStyle, newStyle, animatedStyle);
 
-    if (renderer.parent() || newStyleBeforeAnimation->animations() || (oldStyle && oldStyle->animations())) {
+    if (renderer.parent() || newStyle.animations() || (oldStyle && oldStyle->animations())) {
         m_data->updateAnimationTimerForRenderer(renderer);
 #if ENABLE(REQUEST_ANIMATION_FRAME)
         renderer.view().frameView().scheduleAnimation();
 #endif
     }
 
-    if (blendedStyle.ptr() != newStyleBeforeAnimation.ptr()) {
+    if (animatedStyle.ptr() != &newStyle) {
         // If the animations/transitions change opacity or transform, we need to update
         // the style to impose the stacking rules. Note that this is also
         // done in StyleResolver::adjustRenderStyle().
-        if (blendedStyle.get().hasAutoZIndex() && (blendedStyle.get().opacity() < 1.0f || blendedStyle.get().hasTransform()))
-            blendedStyle.get().setZIndex(0);
+        if (animatedStyle.get().hasAutoZIndex() && (animatedStyle.get().opacity() < 1.0f || animatedStyle.get().hasTransform()))
+            animatedStyle.get().setZIndex(0);
     }
-    return blendedStyle;
+    return animationStateChanged;
 }
 
 PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderElement& renderer)
index 98736a3..3350b19 100644 (file)
@@ -49,7 +49,7 @@ public:
     ~AnimationController();
 
     void cancelAnimations(RenderElement&);
-    Ref<RenderStyle> updateAnimations(RenderElement&, Ref<RenderStyle>&& newStyle);
+    bool updateAnimations(RenderElement&, RenderStyle& newStyle, Ref<RenderStyle>& animatedStyle);
     PassRefPtr<RenderStyle> getAnimatedStyleForRenderer(RenderElement&);
 
     // If possible, compute the visual extent of any transform animation on the given renderer
index ca1ed14..07d84e0 100644 (file)
@@ -297,21 +297,22 @@ void CompositeAnimation::updateKeyframeAnimations(RenderElement* renderer, Rende
         m_keyframeAnimations.remove(nameForRemoval);
 }
 
-Ref<RenderStyle> CompositeAnimation::animate(RenderElement& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle)
+bool CompositeAnimation::animate(RenderElement& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle, Ref<RenderStyle>& blendedStyle)
 {
-    RefPtr<RenderStyle> resultStyle;
-
     // We don't do any transitions if we don't have a currentStyle (on startup).
     updateTransitions(&renderer, currentStyle, &targetStyle);
     updateKeyframeAnimations(&renderer, currentStyle, &targetStyle);
     m_keyframeAnimations.checkConsistency();
 
+    RefPtr<RenderStyle> animatedStyle;
+    bool animationStateChanged = false;
+
     if (currentStyle) {
         // Now that we have transition objects ready, let them know about the new goal state.  We want them
         // to fill in a RenderStyle*& only if needed.
-        if (!m_transitions.isEmpty()) {
-            for (auto& transition : m_transitions.values())
-                transition->animate(this, &renderer, currentStyle, &targetStyle, resultStyle);
+        for (auto& transition : m_transitions.values()) {
+            if (transition->animate(this, &renderer, currentStyle, &targetStyle, animatedStyle))
+                animationStateChanged = true;
         }
     }
 
@@ -319,14 +320,16 @@ Ref<RenderStyle> CompositeAnimation::animate(RenderElement& renderer, RenderStyl
     // to fill in a RenderStyle*& only if needed.
     for (auto& name : m_keyframeAnimationOrderMap) {
         RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name);
-        if (keyframeAnim)
-            keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle);
+        if (keyframeAnim && keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, animatedStyle))
+            animationStateChanged = true;
     }
 
-    if (resultStyle)
-        return resultStyle.releaseNonNull();
+    if (animatedStyle)
+        blendedStyle = animatedStyle.releaseNonNull();
+    else
+        blendedStyle = targetStyle;
 
-    return targetStyle;
+    return animationStateChanged;
 }
 
 PassRefPtr<RenderStyle> CompositeAnimation::getAnimatedStyle() const
index a553ffc..b61144f 100644 (file)
@@ -56,7 +56,7 @@ public:
     
     void clearRenderer();
 
-    Ref<RenderStyle> animate(RenderElement&, RenderStyle* currentStyle, RenderStyle& targetStyle);
+    bool animate(RenderElement&, RenderStyle* currentStyle, RenderStyle& targetStyle, Ref<RenderStyle>& blendedStyle);
     PassRefPtr<RenderStyle> getAnimatedStyle() const;
     bool computeExtentOfTransformAnimation(LayoutRect&) const;
 
index d021597..0149d67 100644 (file)
@@ -60,12 +60,14 @@ bool ImplicitAnimation::shouldSendEventForListener(Document::ListenerType inList
     return m_object->document().hasListenerType(inListenerType);
 }
 
-void ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
+bool ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
 {
     // If we get this far and the animation is done, it means we are cleaning up a just finished animation.
     // So just return. Everything is already all cleaned up.
     if (postActive())
-        return;
+        return false;
+
+    AnimationState oldState = state();
 
     // Reset to start the transition if we are new
     if (isNew())
@@ -79,15 +81,18 @@ void ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const Rende
     bool needsAnim = CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0));
     // FIXME: we also need to detect cases where we have to software animate for other reasons,
     // such as a child using inheriting the transform. https://bugs.webkit.org/show_bug.cgi?id=23902
-    if (!needsAnim)
+    if (!needsAnim) {
         // If we are running an accelerated animation, set a flag in the style which causes the style
         // to compare as different to any other style. This ensures that changes to the property
         // that is animating are correctly detected during the animation (e.g. when a transition
         // gets interrupted).
+        // FIXME: still need this hack?
         animatedStyle->setIsRunningAcceleratedAnimation();
+    }
 
     // Fire the start timeout if needed
     fireAnimationEventsIfNeeded();
+    return state() != oldState;
 }
 
 void ImplicitAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
index 3f29bd9..16d6702 100644 (file)
@@ -54,7 +54,7 @@ public:
     virtual void pauseAnimation(double timeOffset) override;
     virtual void endAnimation() override;
 
-    virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override;
+    virtual bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override;
     virtual void getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) override;
     virtual void reset(RenderStyle* to);
 
index 9abb8f3..e9fc903 100644 (file)
@@ -118,7 +118,7 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property
     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*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
 {
     // Fire the start timeout if needed
     fireAnimationEventsIfNeeded();
@@ -132,7 +132,7 @@ void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderEl
     if (postActive()) {
         if (!animatedStyle)
             animatedStyle = const_cast<RenderStyle*>(targetStyle);
-        return;
+        return false;
     }
 
     // If we are waiting for the start timer, we don't want to change the style yet.
@@ -141,14 +141,16 @@ 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(AnimationStateInput::EndAnimation, -1);
-        return;
+        return 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)
@@ -168,8 +170,11 @@ void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderEl
             // 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.
+            // FIXME: still need this?
             animatedStyle->setIsRunningAcceleratedAnimation();
     }
+    
+    return state() != oldState;
 }
 
 void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle)
index 149a3cb..e43243c 100644 (file)
@@ -45,7 +45,7 @@ public:
         return adoptRef(new KeyframeAnimation(animation, renderer, index, compositeAnimation, unanimatedStyle));
     }
 
-    virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override;
+    virtual bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override;
     virtual void getAnimatedStyle(RefPtr<RenderStyle>&) override;
 
     bool computeExtentOfTransformAnimation(LayoutRect&) const override;
index f2a5102..3ebad26 100644 (file)
@@ -296,7 +296,7 @@ StyleDifference RenderElement::adjustStyleDifference(StyleDifference diff, unsig
     // The answer to requiresLayer() for plugins, iframes, and canvas can change without the actual
     // style changing, since it depends on whether we decide to composite these elements. When the
     // layer status of one of these elements changes, we need to force a layout.
-    if (diff == StyleDifferenceEqual && isRenderLayerModelObject()) {
+    if (diff < StyleDifferenceLayout && isRenderLayerModelObject()) {
         if (hasLayer() != downcast<RenderLayerModelObject>(*this).requiresLayer())
             diff = StyleDifferenceLayout;
     }
@@ -366,7 +366,7 @@ void RenderElement::updateShapeImage(const ShapeValue* oldShapeValue, const Shap
 
 void RenderElement::initializeStyle()
 {
-    styleWillChange(StyleDifferenceEqual, style());
+    styleWillChange(StyleDifferenceNewStyle, style());
 
     m_hasInitializedStyle = true;
 
@@ -385,7 +385,7 @@ void RenderElement::initializeStyle()
     if (m_style->outlineWidth() > 0 && m_style->outlineSize() > maximalOutlineSize(PaintPhaseOutline))
         view().setMaximalOutlineSize(std::max(theme().platformFocusRingMaxWidth(), static_cast<int>(m_style->outlineSize())));
 
-    styleDidChange(StyleDifferenceEqual, nullptr);
+    styleDidChange(StyleDifferenceNewStyle, nullptr);
 
     // We shouldn't have any text children that would need styleDidChange at this point.
     ASSERT(!childrenOfType<RenderText>(*this).first());
@@ -394,7 +394,7 @@ void RenderElement::initializeStyle()
     // have their parent set before getting a call to initializeStyle() :|
 }
 
-void RenderElement::setStyle(Ref<RenderStyle>&& style)
+void RenderElement::setStyle(Ref<RenderStyle>&& style, StyleDifference minimalStyleDifference)
 {
     // FIXME: Should change RenderView so it can use initializeStyle too.
     // If we do that, we can assert m_hasInitializedStyle unconditionally,
@@ -408,6 +408,7 @@ void RenderElement::setStyle(Ref<RenderStyle>&& style)
         ASSERT(!isRenderIFrame());
         ASSERT(!isEmbeddedObject());
         ASSERT(!isCanvas());
+        ASSERT(minimalStyleDifference == StyleDifferenceEqual);
         return;
     }
 
@@ -416,6 +417,8 @@ void RenderElement::setStyle(Ref<RenderStyle>&& style)
     if (m_hasInitializedStyle)
         diff = m_style->diff(style.get(), contextSensitiveProperties);
 
+    diff = std::max(diff, minimalStyleDifference);
+
     diff = adjustStyleDifference(diff, contextSensitiveProperties);
 
     styleWillChange(diff, style.get());
index 3ed9c6e..5f6c73b 100644 (file)
@@ -44,14 +44,18 @@ public:
 
     void initializeStyle();
 
-    void setStyle(Ref<RenderStyle>&&);
+    // Calling with minimalStyleDifference > StyleDifferenceEqual indicates that
+    // out-of-band state (e.g. animations) requires that styleDidChange processing
+    // continue even if the style isn't different from the current style.
+    void setStyle(Ref<RenderStyle>&&, StyleDifference minimalStyleDifference = StyleDifferenceEqual);
+
     // Called to update a style that is allowed to trigger animations.
-    void setAnimatableStyle(Ref<RenderStyle>&&);
+    void setAnimatableStyle(Ref<RenderStyle>&&, StyleDifference minimalStyleDifference);
 
     // The pseudo element style can be cached or uncached.  Use the cached method if the pseudo element doesn't respect
     // any pseudo classes (and therefore has no concept of changing state).
-    RenderStyle* getCachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0) const;
-    PassRefPtr<RenderStyle> getUncachedPseudoStyle(const PseudoStyleRequest&, RenderStyle* parentStyle = 0, RenderStyle* ownStyle = 0) const;
+    RenderStyle* getCachedPseudoStyle(PseudoId, RenderStyle* parentStyle = nullptr) const;
+    PassRefPtr<RenderStyle> getUncachedPseudoStyle(const PseudoStyleRequest&, RenderStyle* parentStyle = nullptr, RenderStyle* ownStyle = nullptr) const;
 
     // This is null for anonymous renderers.
     Element* element() const { return downcast<Element>(RenderObject::node()); }
@@ -88,8 +92,8 @@ public:
     bool isRenderNamedFlowFragmentContainer() const;
 
     virtual bool isChildAllowed(const RenderObject&, const RenderStyle&) const { return true; }
-    virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0);
-    virtual void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild = 0) { return addChild(newChild, beforeChild); }
+    virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = nullptr);
+    virtual void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild = nullptr) { return addChild(newChild, beforeChild); }
     virtual void removeChild(RenderObject&);
 
     // The following functions are used when the render tree hierarchy changes to make sure layers get
@@ -322,9 +326,13 @@ private:
     static bool s_noLongerAffectsParentBlock;
 };
 
-inline void RenderElement::setAnimatableStyle(Ref<RenderStyle>&& style)
+inline void RenderElement::setAnimatableStyle(Ref<RenderStyle>&& style, StyleDifference minimalStyleDifference)
 {
-    setStyle(animation().updateAnimations(*this, WTF::move(style)));
+    Ref<RenderStyle> animatedStyle = WTF::move(style);
+    if (animation().updateAnimations(*this, animatedStyle, animatedStyle))
+        minimalStyleDifference = std::max(minimalStyleDifference, StyleDifferenceRecompositeLayer);
+    
+    setStyle(WTF::move(animatedStyle), minimalStyleDifference);
 }
 
 inline RenderStyle& RenderElement::firstLineStyle() const
index fcc31e7..26ab956 100644 (file)
@@ -6693,7 +6693,7 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle
 
     updateNeedsCompositedScrolling();
 
-    compositor().layerStyleChanged(*this, oldStyle);
+    compositor().layerStyleChanged(diff, *this, oldStyle);
 
     updateOrRemoveFilterEffectRenderer();
 
index e7a0164..8566ad6 100644 (file)
@@ -915,8 +915,11 @@ static bool styleChangeRequiresLayerRebuild(const RenderLayer& layer, const Rend
     return false;
 }
 
-void RenderLayerCompositor::layerStyleChanged(RenderLayer& layer, const RenderStyle* oldStyle)
+void RenderLayerCompositor::layerStyleChanged(StyleDifference diff, RenderLayer& layer, const RenderStyle* oldStyle)
 {
+    if (diff == StyleDifferenceEqual)
+        return;
+
     const RenderStyle& newStyle = layer.renderer().style();
     if (updateLayerCompositingState(layer) || (oldStyle && styleChangeRequiresLayerRebuild(layer, *oldStyle, newStyle)))
         setCompositingLayersNeedRebuild();
index cde5534..bda4cd1 100644 (file)
@@ -171,7 +171,7 @@ public:
     void layerWasAdded(RenderLayer& parent, RenderLayer& child);
     void layerWillBeRemoved(RenderLayer& parent, RenderLayer& child);
 
-    void layerStyleChanged(RenderLayer&, const RenderStyle* oldStyle);
+    void layerStyleChanged(StyleDifference, RenderLayer&, const RenderStyle* oldStyle);
 
     static bool canCompositeClipPath(const RenderLayer&);
 
index 7c6e575..8058a28 100644 (file)
@@ -1063,6 +1063,8 @@ public:
 
     AnimationList* animations() { return rareNonInheritedData->m_animations.get(); }
     AnimationList* transitions() { return rareNonInheritedData->m_transitions.get(); }
+    
+    bool hasAnimationsOrTransitions() const { return rareNonInheritedData->hasAnimationsOrTransitions(); }
 
     AnimationList& ensureAnimations();
     AnimationList& ensureTransitions();
index 1b3f5f6..ae86551 100644 (file)
@@ -55,7 +55,8 @@ enum StyleDifference {
     StyleDifferenceLayoutPositionedMovementOnly,
     StyleDifferenceSimplifiedLayout,
     StyleDifferenceSimplifiedLayoutAndPositionedMovement,
-    StyleDifferenceLayout
+    StyleDifferenceLayout,
+    StyleDifferenceNewStyle
 };
 
 // When some style properties change, different amounts of work have to be done depending on
index ca6ea07..2012092 100644 (file)
@@ -99,6 +99,8 @@ public:
 #endif
     bool hasOpacity() const { return opacity < 1; }
 
+    bool hasAnimationsOrTransitions() const { return m_animations || m_transitions; }
+
     float opacity;
 
     float m_aspectRatioDenominator;
index 324cfba..d814092 100644 (file)
@@ -201,7 +201,9 @@ static void createRendererIfNeeded(Element& element, RenderStyle& inheritedStyle
 
     // FIXME: There's probably a better way to factor this.
     // This just does what setAnimatedStyle() does, except with setStyleInternal() instead of setStyle().
-    newRenderer->setStyleInternal(newRenderer->animation().updateAnimations(*newRenderer, newRenderer->style()));
+    Ref<RenderStyle> animatedStyle = newRenderer->style();
+    newRenderer->animation().updateAnimations(*newRenderer, animatedStyle, animatedStyle);
+    newRenderer->setStyleInternal(WTF::move(animatedStyle));
 
     newRenderer->initializeStyle();
 
@@ -630,7 +632,7 @@ static Change resolveLocal(Element& current, RenderStyle& inheritedStyle, Render
 
     if (RenderElement* renderer = current.renderer()) {
         if (localChange != NoChange || pseudoStyleCacheIsInvalid(renderer, newStyle.get()) || (inheritedChange == Force && renderer->requiresForcedStyleRecalcPropagation()) || current.styleChangeType() == SyntheticStyleChange)
-            renderer->setAnimatableStyle(*newStyle);
+            renderer->setAnimatableStyle(*newStyle, current.styleChangeType() == SyntheticStyleChange ? StyleDifferenceRecompositeLayer : StyleDifferenceEqual);
         else if (current.needsStyleRecalc()) {
             // Although no change occurred, we use the new style so that the cousin style sharing code won't get
             // fooled into believing this style is the same.