Bug 1209405 - Part 3: Save updating style. r=birtles
authorDaisuke Akatsuka <daisuke@mozilla-japan.org>
Mon, 23 May 2016 10:47:14 +0900
changeset 337458 16f55c99f2cf776e5db21f0545a94f87923718ec
parent 337457 b373151148d63f2365436dc676cf741ebd51fc36
child 337459 e83d507253028152705c0782cbc5a78c2b3d17cc
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles
bugs1209405
milestone49.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 1209405 - Part 3: Save updating style. r=birtles MozReview-Commit-ID: FAWfIeXrLki
dom/smil/nsSMILAnimationController.cpp
dom/smil/nsSMILAnimationController.h
dom/smil/nsSMILAnimationFunction.h
dom/smil/nsSMILCompositor.cpp
dom/smil/nsSMILCompositor.h
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -314,22 +314,23 @@ nsSMILAnimationController::DoSample(bool
     NS_ERROR("Shouldn't be sampling after document has disconnected");
     return;
   }
   if (mRunningSample) {
     NS_ERROR("Shouldn't be recursively sampling");
     return;
   }
 
+  bool isStyleFlushNeeded = mResampleNeeded;
   mResampleNeeded = false;
   // Set running sample flag -- do this before flushing styles so that when we
   // flush styles we don't end up requesting extra samples
   AutoRestore<bool> autoRestoreRunningSample(mRunningSample);
   mRunningSample = true;
-  
+
   // STEP 1: Bring model up to date
   // (i)  Rewind elements where necessary
   // (ii) Run milestone samples
   RewindElements();
   DoMilestoneSamples();
 
   // STEP 2: Sample the child time containers
   //
@@ -373,17 +374,19 @@ nsSMILAnimationController::DoSample(bool
 
   // Create the compositor table
   nsAutoPtr<nsSMILCompositorTable>
     currentCompositorTable(new nsSMILCompositorTable(0));
 
   for (auto iter = mAnimationElementTable.Iter(); !iter.Done(); iter.Next()) {
     SVGAnimationElement* animElem = iter.Get()->GetKey();
     SampleTimedElement(animElem, &activeContainers);
-    AddAnimationToCompositorTable(animElem, currentCompositorTable);
+    AddAnimationToCompositorTable(animElem,
+                                  currentCompositorTable,
+                                  isStyleFlushNeeded);
   }
   activeContainers.Clear();
 
   // STEP 4: Compare previous sample's compositors against this sample's.
   // (Transfer cached base values across, & remove animation effects from
   // no-longer-animated targets.)
   if (mLastCompositorTable) {
     // * Transfer over cached base values, from last sample's compositors
@@ -417,35 +420,38 @@ nsSMILAnimationController::DoSample(bool
 
   // return early if there are no active animations to avoid a style flush
   if (currentCompositorTable->Count() == 0) {
     mLastCompositorTable = nullptr;
     return;
   }
 
   nsCOMPtr<nsIDocument> kungFuDeathGrip(mDocument);  // keeps 'this' alive too
-  mDocument->FlushPendingNotifications(Flush_Style);
+  if (isStyleFlushNeeded) {
+    mDocument->FlushPendingNotifications(Flush_Style);
+  }
 
-  // WARNING: 
+  // WARNING:
   // WARNING: the above flush may have destroyed the pres shell and/or
   // WARNING: frames and other layout related objects.
   // WARNING:
 
   // STEP 5: Compose currently-animated attributes.
   // XXXdholbert: This step traverses our animation targets in an effectively
   // random order. For animation from/to 'inherit' values to work correctly
   // when the inherited value is *also* being animated, we really should be
   // traversing our animated nodes in an ancestors-first order (bug 501183)
+  bool mightHavePendingStyleUpdates = false;
   for (auto iter = currentCompositorTable->Iter(); !iter.Done(); iter.Next()) {
-    iter.Get()->ComposeAttribute();
+    iter.Get()->ComposeAttribute(mightHavePendingStyleUpdates);
   }
 
   // Update last compositor table
   mLastCompositorTable = currentCompositorTable.forget();
-  mMightHavePendingStyleUpdates = true;
+  mMightHavePendingStyleUpdates = mightHavePendingStyleUpdates;
 
   NS_ASSERTION(!mResampleNeeded, "Resample dirty flag set during sample!");
 }
 
 void
 nsSMILAnimationController::RewindElements()
 {
   bool rewindNeeded = false;
@@ -591,17 +597,19 @@ nsSMILAnimationController::SampleTimedEl
 
   MOZ_ASSERT(!timeContainer->IsSeeking(),
              "Doing a regular sample but the time container is still seeking");
   aElement->TimedElement().SampleAt(containerTime);
 }
 
 /*static*/ void
 nsSMILAnimationController::AddAnimationToCompositorTable(
-  SVGAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable)
+  SVGAnimationElement* aElement,
+  nsSMILCompositorTable* aCompositorTable,
+  bool& aStyleFlushNeeded)
 {
   // Add a compositor to the hash table if there's not already one there
   nsSMILTargetIdentifier key;
   if (!GetTargetIdentifierForAnimation(aElement, key))
     // Something's wrong/missing about animation's target; skip this animation
     return;
 
   nsSMILAnimationFunction& func = aElement->AnimationFunction();
@@ -624,16 +632,17 @@ nsSMILAnimationController::AddAnimationT
     nsSMILCompositor* result = aCompositorTable->PutEntry(key);
     result->ToggleForceCompositing();
 
     // We've now made sure that |func|'s inactivity will be reflected as of
     // this sample. We need to clear its HasChanged() flag so that it won't
     // trigger this same clause in future samples (until it changes again).
     func.ClearHasChanged();
   }
+  aStyleFlushNeeded |= func.ValueNeedsReparsingEverySample();
 }
 
 static inline bool
 IsTransformAttribute(int32_t aNamespaceID, nsIAtom *aAttributeName)
 {
   return aNamespaceID == kNameSpaceID_None &&
          (aAttributeName == nsGkAtoms::transform ||
           aAttributeName == nsGkAtoms::patternTransform ||
--- a/dom/smil/nsSMILAnimationController.h
+++ b/dom/smil/nsSMILAnimationController.h
@@ -137,18 +137,22 @@ protected:
   void DoSample(bool aSkipUnchangedContainers);
 
   void RewindElements();
 
   void DoMilestoneSamples();
 
   static void SampleTimedElement(mozilla::dom::SVGAnimationElement* aElement,
                                  TimeContainerHashtable* aActiveContainers);
+
   static void AddAnimationToCompositorTable(
-    mozilla::dom::SVGAnimationElement* aElement, nsSMILCompositorTable* aCompositorTable);
+      mozilla::dom::SVGAnimationElement* aElement,
+      nsSMILCompositorTable* aCompositorTable,
+      bool& aStyleFlushNeeded);
+
   static bool GetTargetIdentifierForAnimation(
       mozilla::dom::SVGAnimationElement* aAnimElem, nsSMILTargetIdentifier& aResult);
 
   // Methods for adding/removing time containers
   virtual nsresult AddChild(nsSMILTimeContainer& aChild) override;
   virtual void     RemoveChild(nsSMILTimeContainer& aChild) override;
 
   void FlagDocumentNeedsFlush();
--- a/dom/smil/nsSMILAnimationFunction.h
+++ b/dom/smil/nsSMILAnimationFunction.h
@@ -241,16 +241,24 @@ public:
    * Mark this animation function as having been skipped. By marking the
    * function as skipped, if it is used in a subsequent sample we'll know to
    * recomposite the sandwich.
    */
   void SetWasSkipped() {
     mWasSkippedInPrevSample = true;
   }
 
+  /**
+   * Returns true if we need to recalculate the animation value on every sample.
+   * (e.g. because it depends on context like the font-size)
+   */
+  bool ValueNeedsReparsingEverySample() const {
+    return mValueNeedsReparsingEverySample;
+  }
+
   // Comparator utility class, used for sorting nsSMILAnimationFunctions
   class Comparator {
     public:
       bool Equals(const nsSMILAnimationFunction* aElem1,
                     const nsSMILAnimationFunction* aElem2) const {
         return (aElem1->CompareTo(aElem2) == 0);
       }
       bool LessThan(const nsSMILAnimationFunction* aElem1,
--- a/dom/smil/nsSMILCompositor.cpp
+++ b/dom/smil/nsSMILCompositor.cpp
@@ -44,32 +44,34 @@ void
 nsSMILCompositor::AddAnimationFunction(nsSMILAnimationFunction* aFunc)
 {
   if (aFunc) {
     mAnimationFunctions.AppendElement(aFunc);
   }
 }
 
 void
-nsSMILCompositor::ComposeAttribute()
+nsSMILCompositor::ComposeAttribute(bool& aMightHavePendingStyleUpdates)
 {
   if (!mKey.mElement)
     return;
 
   // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually
   // give animated value to)
   nsAutoPtr<nsISMILAttr> smilAttr(CreateSMILAttr());
   if (!smilAttr) {
     // Target attribute not found (or, out of memory)
     return;
   }
   if (mAnimationFunctions.IsEmpty()) {
     // No active animation functions. (We can still have a nsSMILCompositor in
     // that case if an animation function has *just* become inactive)
     smilAttr->ClearAnimValue();
+    // Removing the animation effect may require a style update.
+    aMightHavePendingStyleUpdates = true;
     return;
   }
 
   // SECOND: Sort the animationFunctions, to prepare for compositing.
   nsSMILAnimationFunction::Comparator comparator;
   mAnimationFunctions.Sort(comparator);
 
   // THIRD: Step backwards through animation functions to find out
@@ -83,16 +85,17 @@ nsSMILCompositor::ComposeAttribute()
   }
   UpdateCachedBaseValue(sandwichResultValue);
 
   if (!mForceCompositing) {
     return;
   }
 
   // FIFTH: Compose animation functions
+  aMightHavePendingStyleUpdates = true;
   uint32_t length = mAnimationFunctions.Length();
   for (uint32_t i = firstFuncToCompose; i < length; ++i) {
     mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
   }
   if (sandwichResultValue.IsNull()) {
     smilAttr->ClearAnimValue();
     return;
   }
--- a/dom/smil/nsSMILCompositor.h
+++ b/dom/smil/nsSMILCompositor.h
@@ -47,18 +47,19 @@ public:
   static PLDHashNumber HashKey(KeyTypePointer aKey);
   enum { ALLOW_MEMMOVE = false };
 
   // Adds the given animation function to this Compositor's list of functions
   void AddAnimationFunction(nsSMILAnimationFunction* aFunc);
 
   // Composes the attribute's current value with the list of animation
   // functions, and assigns the resulting value to this compositor's target
-  // attribute
-  void ComposeAttribute();
+  // attribute. If a change is made that might produce style updates,
+  // aMightHavePendingStyleUpdates is set to true. Otherwise it is not modified.
+  void ComposeAttribute(bool& aMightHavePendingStyleUpdates);
 
   // Clears animation effects on my target attribute
   void ClearAnimationEffects();
 
   // Cycle-collection support
   void Traverse(nsCycleCollectionTraversalCallback* aCallback);
 
   // Toggles a bit that will force us to composite (bypassing early-return
@@ -69,17 +70,17 @@ public:
   void StealCachedBaseValue(nsSMILCompositor* aOther) {
     mCachedBaseValue = mozilla::Move(aOther->mCachedBaseValue);
   }
 
  private:
   // Create a nsISMILAttr for my target, on the heap.  Caller is responsible
   // for deallocating the returned object.
   nsISMILAttr* CreateSMILAttr();
-  
+
   // Finds the index of the first function that will affect our animation
   // sandwich. Also toggles the 'mForceCompositing' flag if it finds that any
   // (used) functions have changed.
   uint32_t GetFirstFuncToAffectSandwich();
 
   // If the passed-in base value differs from our cached base value, this
   // method updates the cached value (and toggles the 'mForceCompositing' flag)
   void UpdateCachedBaseValue(const nsSMILValue& aBaseValue);