Bug 1317209 - Part 5: Trigger composeStyle if there is an animation. r=heycam
authorBoris Chiou <boris.chiou@gmail.com>
Tue, 24 Jan 2017 15:27:56 +0800
changeset 377700 e40482064f56fe969aac406146c3f95383916338
parent 377699 280a3456f532ae876e60c93162b6051c50194da1
child 377701 030d4996715a0b031126a4c0cbd2398e739ebf51
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1317209
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1317209 - Part 5: Trigger composeStyle if there is an animation. r=heycam 1. Add one new FFI, Gecko_GetAnimationRule, which will try to update the animation rule and retrieve a ServoAnimationRule from EffectSet. 2. Add GetServoAnimationRule, which calls Animation::ComposeStyle and updates mElementsToRestyle. 3. There is no eRestyle_{CSSAnimations|CSSTransitions}, so we use eRestyle_Self and eRestyle_Subtree for stylo. MozReview-Commit-ID: 9RpJurPSFMk
dom/animation/EffectCompositor.cpp
dom/animation/EffectCompositor.h
layout/base/ServoRestyleManager.cpp
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -268,16 +268,18 @@ EffectCompositor::RequestRestyle(dom::El
     bool hasPendingRestyle = elementsToRestyle.Get(key);
     if (!hasPendingRestyle) {
       PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel);
     }
     elementsToRestyle.Put(key, true);
   }
 
   if (aRestyleType == RestyleType::Layer) {
+    // FIXME: we call RequestRestyle for both stylo and gecko, so we
+    // should fix this after supporting animations on the compositor.
     // Prompt layers to re-sync their animations.
     if (mPresContext->RestyleManager()->IsServo()) {
       NS_ERROR("stylo: Servo-backed style system should not be using "
                "EffectCompositor");
       return;
     }
     mPresContext->RestyleManager()->AsGecko()->IncrementAnimationGeneration();
     EffectSet* effectSet =
@@ -300,16 +302,22 @@ EffectCompositor::PostRestyleForAnimatio
   dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
   if (!element) {
     return;
   }
 
   nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions ?
                                         eRestyle_CSSTransitions :
                                         eRestyle_CSSAnimations;
+
+  // FIXME: stylo only supports Self and Subtree hints now, so we override it
+  // for stylo.
+  if (mPresContext->StyleSet()->IsServo()) {
+    hint = eRestyle_Self | eRestyle_Subtree;
+  }
   mPresContext->PresShell()->RestyleForAnimation(element, hint);
 }
 
 void
 EffectCompositor::PostRestyleForThrottledAnimations()
 {
   for (size_t i = 0; i < kCascadeLevelCount; i++) {
     CascadeLevel cascadeLevel = CascadeLevel(i);
@@ -418,16 +426,91 @@ EffectCompositor::GetAnimationRule(dom::
   EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
   if (!effectSet) {
     return nullptr;
   }
 
   return effectSet->AnimationRule(aCascadeLevel).mGecko;
 }
 
+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());
+    }
+  };
+}
+
+ServoAnimationRule*
+EffectCompositor::GetServoAnimationRule(const dom::Element* aElement,
+                                        CSSPseudoElementType aPseudoType,
+                                        CascadeLevel aCascadeLevel)
+{
+  if (!mPresContext || !mPresContext->IsDynamic()) {
+    // For print or print preview, ignore animations.
+    return nullptr;
+  }
+
+  EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
+  if (!effectSet) {
+    return nullptr;
+  }
+
+  // Get a list of effects sorted by composite order.
+  nsTArray<KeyframeEffectReadOnly*> sortedEffectList(effectSet->Count());
+  for (KeyframeEffectReadOnly* effect : *effectSet) {
+    sortedEffectList.AppendElement(effect);
+  }
+  sortedEffectList.Sort(EffectCompositeOrderComparator());
+
+  AnimationRule& animRule = effectSet->AnimationRule(aCascadeLevel);
+  animRule.mServo = nullptr;
+
+  // If multiple animations affect the same property, animations with higher
+  // composite order (priority) override or add or animations with lower
+  // priority.
+  // stylo: we don't support animations on compositor now, so propertiesToSkip
+  // is an empty set.
+  const nsCSSPropertyIDSet propertiesToSkip;
+  for (KeyframeEffectReadOnly* effect : sortedEffectList) {
+    effect->GetAnimation()->ComposeStyle(animRule, propertiesToSkip);
+  }
+
+  MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
+             "EffectSet should not change while composing style");
+
+  effectSet->UpdateAnimationRuleRefreshTime(
+    aCascadeLevel, mPresContext->RefreshDriver()->MostRecentRefresh());
+  return animRule.mServo;
+}
+
+void
+EffectCompositor::ClearElementsToRestyle()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  const auto iterEnd = mElementsToRestyle.end();
+  for (auto iter = mElementsToRestyle.begin(); iter != iterEnd; ++iter) {
+    iter->Clear();
+  }
+}
+
 /* static */ dom::Element*
 EffectCompositor::GetElementToRestyle(dom::Element* aElement,
                                       CSSPseudoElementType aPseudoType)
 {
   if (aPseudoType == CSSPseudoElementType::NotPseudo) {
     return aElement;
   }
 
@@ -586,38 +669,16 @@ EffectCompositor::MaybeUpdateCascadeResu
       }
     }
   }
   UpdateCascadeResults(*effects, aElement, aPseudoType, styleContext);
 
   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,
                                        CSSPseudoElementType aPseudoType,
                                        nsStyleContext* aStyleContext)
 {
   EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
   if (!effects) {
     return;
--- a/dom/animation/EffectCompositor.h
+++ b/dom/animation/EffectCompositor.h
@@ -24,16 +24,17 @@ class nsIStyleRule;
 class nsPresContext;
 class nsStyleContext;
 
 namespace mozilla {
 
 class EffectSet;
 class RestyleTracker;
 class StyleAnimationValue;
+class ServoAnimationRule;
 struct AnimationPerformanceWarning;
 struct AnimationProperty;
 struct NonOwningAnimationTarget;
 
 namespace dom {
 class Animation;
 class Element;
 }
@@ -145,16 +146,31 @@ public:
   // When we are not resolving style context, |aStyleContext| can be nullptr, we
   // will use a style context associated with the primary frame of the specified
   // (pseudo-)element.
   nsIStyleRule* GetAnimationRule(dom::Element* aElement,
                                  CSSPseudoElementType aPseudoType,
                                  CascadeLevel aCascadeLevel,
                                  nsStyleContext* aStyleContext);
 
+  // Get animation rule for stylo. This is an equivalent of GetAnimationRule
+  // and will be called from servo side. We need to be careful while doing any
+  // modification because it may case some thread-safe issues.
+  ServoAnimationRule* GetServoAnimationRule(const dom::Element* aElement,
+                                            CSSPseudoElementType aPseudoType,
+                                            CascadeLevel aCascadeLevel);
+
+  // Clear mElementsToRestyle hashtable. Unlike GetAnimationRule,
+  // in GetServoAnimationRule, we don't remove the entry of the composed
+  // animation, so we can prevent the thread-safe issues of dom::Element.
+  // Therefore, we need to call Clear mElementsToRestyle until we go back to
+  // Gecko side.
+  // FIXME: we shouldn't clear the animations on the compositor.
+  void ClearElementsToRestyle();
+
   bool HasPendingStyleUpdates() const;
   bool HasThrottledStyleUpdates() const;
 
   // Tell the restyle tracker about all the animated styles that have
   // pending updates so that it can update the animation rule for these
   // elements.
   void AddStyleUpdatesTo(RestyleTracker& aTracker);
 
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -311,16 +311,17 @@ ServoRestyleManager::ProcessPendingResty
 
   ServoStyleSet* styleSet = StyleSet();
   nsIDocument* doc = PresContext()->Document();
 
   // XXXbholley: Should this be while() per bug 1316247?
   if (HasPendingRestyles()) {
     mInStyleRefresh = true;
     styleSet->StyleDocument();
+    PresContext()->EffectCompositor()->ClearElementsToRestyle();
 
     // First do any queued-up frame creation. (see bugs 827239 and 997506).
     //
     // XXXEmilio I'm calling this to avoid random behavior changes, since we
     // delay frame construction after styling we should re-check once our
     // model is more stable whether we can skip this call.
     //
     // Note this has to be *after* restyling, because otherwise frame
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/ServoBindings.h"
 
 #include "ChildIterator.h"
 #include "gfxFontFamilyList.h"
 #include "nsAttrValueInlines.h"
 #include "nsCSSProps.h"
 #include "nsCSSParser.h"
+#include "nsCSSPseudoElements.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsIContentInlines.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsINode.h"
@@ -24,17 +25,19 @@
 #include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsRuleNode.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
 #include "nsStyleUtil.h"
 #include "nsTArray.h"
 
+#include "mozilla/EffectCompositor.h"
 #include "mozilla/EventStates.h"
+#include "mozilla/ServoAnimationRule.h"
 #include "mozilla/ServoElementSnapshot.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
 
 using namespace mozilla;
@@ -319,16 +322,56 @@ Gecko_GetServoDeclarationBlock(RawGeckoE
     //     document into a Servo-style-backend document.  See bug 1330051.
     NS_WARNING("stylo: requesting a Gecko declaration block?");
     return nullptr;
   }
   return reinterpret_cast<const RawServoDeclarationBlockStrong*>
     (decl->AsServo()->RefRaw());
 }
 
+RawServoDeclarationBlockStrong
+Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
+                       nsIAtom* aPseudoTag)
+{
+  MOZ_ASSERT(aElement, "Invalid GeckoElement");
+
+  const RawServoDeclarationBlockStrong emptyDeclarationBlock{ nullptr };
+  nsIDocument* doc = aElement->GetComposedDoc();
+  if (!doc || !doc->GetShell()) {
+    return emptyDeclarationBlock;
+  }
+  nsPresContext* presContext = doc->GetShell()->GetPresContext();
+  if (!presContext) {
+    return emptyDeclarationBlock;
+  }
+
+  // FIXME: support different cascading levels in the later patch
+  CSSPseudoElementType pseudoType =
+    aPseudoTag
+    ? nsCSSPseudoElements::GetPseudoType(
+        aPseudoTag,
+        nsCSSProps::EnabledState::eIgnoreEnabledState)
+    : CSSPseudoElementType::NotPseudo;
+  if (pseudoType != CSSPseudoElementType::NotPseudo &&
+      pseudoType != CSSPseudoElementType::before &&
+      pseudoType != CSSPseudoElementType::after) {
+    return emptyDeclarationBlock;
+  }
+
+  ServoAnimationRule* rule =
+    presContext->EffectCompositor()->GetServoAnimationRule(
+      aElement,
+      pseudoType,
+      EffectCompositor::CascadeLevel::Animations);
+  if (!rule) {
+    return emptyDeclarationBlock;
+  }
+  return rule->GetValues();
+}
+
 void
 Gecko_FillAllBackgroundLists(nsStyleImageLayers* aLayers, uint32_t aMaxLen)
 {
   nsRuleNode::FillAllBackgroundLists(*aLayers, aMaxLen);
 }
 
 void
 Gecko_FillAllMaskLists(nsStyleImageLayers* aLayers, uint32_t aMaxLen)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -153,16 +153,21 @@ SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNC
                                               const ServoElementSnapshot*)
 
 #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
 
 // Style attributes.
 RawServoDeclarationBlockStrongBorrowedOrNull
 Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element);
 
+// Animations
+RawServoDeclarationBlockStrong
+Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
+                       nsIAtom* aPseudoTag);
+
 // Atoms.
 nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength);
 void Gecko_AddRefAtom(nsIAtom* aAtom);
 void Gecko_ReleaseAtom(nsIAtom* aAtom);
 const uint16_t* Gecko_GetAtomAsUTF16(nsIAtom* aAtom, uint32_t* aLength);
 bool Gecko_AtomEqualsUTF8(nsIAtom* aAtom, const char* aString, uint32_t aLength);
 bool Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* aAtom, const char* aString, uint32_t aLength);