Bug 1209405 - Part 3: Save updating style. r=birtles draft
authorDaisuke Akatsuka <daisuke@mozilla-japan.org>
Mon, 23 May 2016 10:47:14 +0900
changeset 369545 f8f855e57be7f304321a029c139077d462db197a
parent 369544 0adb7ffe42319f0c008c322ee0fffa6553025c87
child 369546 6de4b17886ddf2f63df96714a4e8cd20faf72d89
push id18843
push userbmo:daisuke@mozilla-japan.org
push dateMon, 23 May 2016 01:50:40 +0000
reviewersbirtles
bugs1209405
milestone49.0a1
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);