Bug 1228229 part 8 - Add EffectCompositor::(Maybe)UpdateCascadeResults; r=dbaron
authorBrian Birtles <birtles@gmail.com>
Wed, 06 Jan 2016 11:04:05 +0900
changeset 319234 6c8576a04c467bcc823d5c68f76a6ff597a7f8cd
parent 319233 4974afc6e1a5e578b9a82e03f3ae6efe71d79c77
child 319235 5efd7a041b2da52d2bfb07d9766e3c12a382171f
push id8999
push userbenj@benj.me
push dateWed, 06 Jan 2016 10:10:00 +0000
reviewersdbaron
bugs1228229
milestone46.0a1
Bug 1228229 part 8 - Add EffectCompositor::(Maybe)UpdateCascadeResults; r=dbaron
dom/animation/EffectCompositor.cpp
dom/animation/EffectCompositor.h
dom/animation/KeyframeEffect.h
dom/animation/moz.build
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -7,20 +7,25 @@
 #include "EffectCompositor.h"
 
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/KeyframeEffect.h" // For KeyframeEffectReadOnly
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/LayerAnimationInfo.h"
+#include "AnimationCommon.h" // For AnimationCollection
+#include "nsAnimationManager.h"
+#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
 #include "nsCSSPropertySet.h"
+#include "nsIPresShell.h"
 #include "nsLayoutUtils.h"
 #include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation
 #include "nsTArray.h"
+#include "nsTransitionManager.h"
 
 using mozilla::dom::Animation;
 using mozilla::dom::Element;
 using mozilla::dom::KeyframeEffectReadOnly;
 
 namespace mozilla {
 
 // Helper function to factor out the common logic from
@@ -105,16 +110,67 @@ EffectCompositor::GetAnimationsForCompos
 #endif
     FindAnimationsForCompositor(aFrame, aProperty, &result);
   MOZ_ASSERT(!foundSome || !result.IsEmpty(),
              "If return value is true, matches array should be non-empty");
 
   return result;
 }
 
+/* static */ void
+EffectCompositor::MaybeUpdateCascadeResults(Element* aElement,
+                                            nsCSSPseudoElements::Type
+                                              aPseudoType,
+                                            nsStyleContext* aStyleContext)
+{
+  EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
+  if (!effects || !effects->CascadeNeedsUpdate()) {
+    return;
+  }
+
+  UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
+
+  MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
+}
+
+namespace {
+  class EffectCompositeOrderComparator {
+  public:
+    bool Equals(const KeyframeEffectReadOnly* a,
+                const KeyframeEffectReadOnly* b) const
+    {
+      return a == b;
+    }
+
+    bool LessThan(const KeyframeEffectReadOnly* a,
+                  const KeyframeEffectReadOnly* b) const
+    {
+      MOZ_ASSERT(a->GetAnimation() && b->GetAnimation());
+      MOZ_ASSERT(
+        Equals(a, b) ||
+        a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation()) !=
+          b->GetAnimation()->HasLowerCompositeOrderThan(*a->GetAnimation()));
+      return a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation());
+    }
+  };
+}
+
+/* static */ void
+EffectCompositor::UpdateCascadeResults(Element* aElement,
+                                       nsCSSPseudoElements::Type aPseudoType,
+                                       nsStyleContext* aStyleContext)
+{
+  EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
+  if (!effects) {
+    return;
+  }
+
+  UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
+}
+
 /* static */ Maybe<Pair<Element*, nsCSSPseudoElements::Type>>
 EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
 {
   // Always return the same object to benefit from return-value optimization.
   Maybe<Pair<Element*, nsCSSPseudoElements::Type>> result;
 
   nsIContent* content = aFrame->GetContent();
   if (!content) {
@@ -162,9 +218,121 @@ EffectCompositor::GetOverriddenPropertie
          LayerAnimationInfo::sRecords) {
     propertiesToTrack.AppendElement(record.mProperty);
   }
   nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
                                                    aStyleContext,
                                                    aPropertiesOverridden);
 }
 
+/* static */ void
+EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
+                                       Element* aElement,
+                                       nsCSSPseudoElements::Type aPseudoType,
+                                       nsStyleContext* aStyleContext)
+{
+  MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet,
+             "Effect set should correspond to the specified (pseudo-)element");
+  if (aEffectSet.IsEmpty()) {
+    aEffectSet.MarkCascadeUpdated();
+    return;
+  }
+
+  // Get a list of effects sorted by composite order.
+  nsTArray<KeyframeEffectReadOnly*> sortedEffectList;
+  for (KeyframeEffectReadOnly* effect : aEffectSet) {
+    sortedEffectList.AppendElement(effect);
+  }
+  sortedEffectList.Sort(EffectCompositeOrderComparator());
+
+  // Get properties that override the *animations* level of the cascade.
+  //
+  // We only do this for properties that we can animate on the compositor
+  // since we will apply other properties on the main thread where the usual
+  // cascade applies.
+  nsCSSPropertySet overriddenProperties;
+  if (aStyleContext) {
+    GetOverriddenProperties(aStyleContext, overriddenProperties);
+  }
+
+  bool changed = false;
+  nsCSSPropertySet animatedProperties;
+
+  // Iterate from highest to lowest composite order.
+  for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) {
+    MOZ_ASSERT(effect->GetAnimation(),
+               "Effects on a target element should have an Animation");
+    bool inEffect = effect->IsInEffect();
+    for (AnimationProperty& prop : effect->Properties()) {
+
+      bool winsInCascade = !animatedProperties.HasProperty(prop.mProperty) &&
+                           inEffect;
+
+      // If this property wins in the cascade, add it to the set of animated
+      // properties. We need to do this even if the property is overridden
+      // (in which case we set winsInCascade to false below) since we don't
+      // want to fire transitions on these properties.
+      if (winsInCascade) {
+        animatedProperties.AddProperty(prop.mProperty);
+      }
+
+      // For effects that will be applied to the animations level of the
+      // cascade, we need to check that the property isn't being set by
+      // something with higher priority in the cascade.
+      //
+      // We only do this, however, for properties that can be animated on
+      // the compositor. For properties animated on the main thread the usual
+      // cascade ensures these animations will be correctly overridden.
+      if (winsInCascade &&
+          !effect->GetAnimation()->AppliesToTransitionsLevel() &&
+          overriddenProperties.HasProperty(prop.mProperty)) {
+        winsInCascade = false;
+      }
+
+      if (winsInCascade != prop.mWinsInCascade) {
+        changed = true;
+      }
+      prop.mWinsInCascade = winsInCascade;
+    }
+  }
+
+  aEffectSet.MarkCascadeUpdated();
+
+  // If there is any change in the cascade result, update animations on
+  // layers with the winning animations.
+  nsPresContext* presContext = GetPresContext(aElement);
+  if (changed && presContext) {
+    // We currently unconditionally update both animations and transitions
+    // even if we could, for example, get away with only updating animations.
+    // This is a temporary measure until we unify all animation style updating
+    // under EffectCompositor.
+    AnimationCollection* animations =
+      presContext->AnimationManager()->GetAnimationCollection(aElement,
+                                                              aPseudoType,
+                                                              false);
+                                                             /* don't create */
+    if (animations) {
+      animations->RequestRestyle(AnimationCollection::RestyleType::Layer);
+    }
+
+    AnimationCollection* transitions =
+      presContext->TransitionManager()->GetAnimationCollection(aElement,
+                                                               aPseudoType,
+                                                               false);
+                                                             /* don't create */
+    if (transitions) {
+      transitions->RequestRestyle(AnimationCollection::RestyleType::Layer);
+    }
+  }
+}
+
+/* static */ nsPresContext*
+EffectCompositor::GetPresContext(Element* aElement)
+{
+  MOZ_ASSERT(aElement);
+  nsIPresShell* shell = nsComputedDOMStyle::GetPresShellForContent(aElement);
+  if (!shell) {
+    return nullptr;
+  }
+  return shell->GetPresContext();
+}
+
 } // namespace mozilla
--- a/dom/animation/EffectCompositor.h
+++ b/dom/animation/EffectCompositor.h
@@ -9,34 +9,62 @@
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Pair.h"
 #include "mozilla/RefPtr.h"
 #include "nsCSSPseudoElements.h"
 #include "nsTArray.h"
 
 class nsCSSPropertySet;
+class nsPresContext;
+class nsStyleContext;
 
 namespace mozilla {
 
+class EffectSet;
+
 namespace dom {
 class Animation;
 class Element;
 }
 
 class EffectCompositor
 {
 public:
   static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
                                          nsCSSProperty aProperty);
 
   static nsTArray<RefPtr<dom::Animation>>
   GetAnimationsForCompositor(const nsIFrame* aFrame,
                              nsCSSProperty aProperty);
 
+
+  // Update animation cascade results for the specified (pseudo-)element
+  // but only if we have marked the cascade as needing an update due a
+  // the change in the set of effects or a change in one of the effects'
+  // "in effect" state.
+  //
+  // This method does NOT detect if other styles that apply above the
+  // animation level of the cascade have changed.
+  static void
+  MaybeUpdateCascadeResults(dom::Element* aElement,
+                            nsCSSPseudoElements::Type aPseudoType,
+                            nsStyleContext* aStyleContext);
+
+  // Update the mWinsInCascade member for each property in effects targetting
+  // the specified (pseudo-)element.
+  //
+  // This can be expensive so we should only call it if styles that apply
+  // above the animation level of the cascade might have changed. For all
+  // other cases we should call MaybeUpdateCascadeResults.
+  static void
+  UpdateCascadeResults(dom::Element* aElement,
+                       nsCSSPseudoElements::Type aPseudoType,
+                       nsStyleContext* aStyleContext);
+
   // Helper to fetch the corresponding element and pseudo-type from a frame.
   //
   // For frames corresponding to pseudo-elements, the returned element is the
   // element on which we store the animations (i.e. the EffectSet and/or
   // AnimationCollection), *not* the generated content.
   //
   // Returns an empty result when a suitable element cannot be found including
   // when the frame represents a pseudo-element on which we do not support
@@ -45,13 +73,22 @@ public:
   GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame);
 
   // Get the properties that we are able to animate on the compositor that
   // are specified at a higher level in the cascade than the animations
   // level in |aStyleContext|.
   static void
   GetOverriddenProperties(nsStyleContext* aStyleContext,
                           nsCSSPropertySet& aPropertiesOverridden);
+
+private:
+  static void
+  UpdateCascadeResults(EffectSet& aEffectSet,
+                       dom::Element* aElement,
+                       nsCSSPseudoElements::Type aPseudoType,
+                       nsStyleContext* aStyleContext);
+
+  static nsPresContext* GetPresContext(dom::Element* aElement);
 };
 
 } // namespace mozilla
 
 #endif // mozilla_EffectCompositor_h
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -126,20 +126,17 @@ struct AnimationProperty
   // same property and element is not running, in which case we set this
   // to false so that the animation (lower in the cascade) can win.  We
   // then use this to decide whether to apply the style both in the CSS
   // cascade and for OMTA.
   //
   // For CSS Animations, which are overridden by !important rules in the
   // cascade, we actually determine this from the CSS cascade
   // computations, and then use it for OMTA.
-  // **NOTE**: For CSS animations, we only bother setting mWinsInCascade
-  // accurately for properties that we can animate on the compositor.
-  // For other properties, we make it always be true.
-  // **NOTE 2**: This member is not included when comparing AnimationProperty
+  // **NOTE**: This member is not included when comparing AnimationProperty
   // objects for equality.
   bool mWinsInCascade = true;
 
   // If true, the propery is currently being animated on the compositor.
   //
   // Note that when the owning Animation requests a non-throttled restyle, in
   // between calling RequestRestyle on its AnimationCollection and when the
   // restyle is performed, this member may temporarily become false even if
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -34,14 +34,15 @@ UNIFIED_SOURCES += [
     'EffectCompositor.cpp',
     'EffectSet.cpp',
     'KeyframeEffect.cpp',
     'PendingAnimationTracker.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
+    '/layout/style',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']