Bug 1209405 - Part 3: Save updating style. r=birtles
MozReview-Commit-ID: FAWfIeXrLki
--- 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);