Bug 914847. Mini-flush for animations. r=dbaron, a=1.3+
authorNicholas Cameron <ncameron@mozilla.com>
Tue, 22 Oct 2013 14:14:41 +0200
changeset 175898 b7a571142fa5b209da747304b62156cf003bcf4a
parent 175897 306c55f885543c5dee0ced34af40d67241ab5ae7
child 175899 c6e3ddc8d831928eea5fedc0c0d4bd33d7c8a12f
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, 1
bugs914847
milestone28.0a2
Bug 914847. Mini-flush for animations. r=dbaron, a=1.3+
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/style/AnimationCommon.cpp
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -983,17 +983,18 @@ nsPresContext::Init(nsDeviceContext* aDe
 
     if (!mRefreshDriver) {
       mRefreshDriver = new nsRefreshDriver(this);
     }
   }
 
   // Initialise refresh tick counters for OMTA
   mLastStyleUpdateForAllAnimations =
-    mLastUpdateThrottledStyle = mRefreshDriver->MostRecentRefresh();
+    mLastUpdateThrottledAnimationStyle =
+    mLastUpdateThrottledTransitionStyle = mRefreshDriver->MostRecentRefresh();
 
   mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
 
   // Register callbacks so we're notified when the preferences change
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "font.",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
@@ -1498,25 +1499,39 @@ nsPresContext::GetContainerInternal() co
 
 already_AddRefed<nsISupports>
 nsPresContext::GetContainerExternal() const
 {
   return GetContainerInternal();
 }
 
 bool
-nsPresContext::ThrottledStyleIsUpToDate() const
+nsPresContext::ThrottledTransitionStyleIsUpToDate() const
 {
-  return mLastUpdateThrottledStyle == mRefreshDriver->MostRecentRefresh();
+  return
+    mLastUpdateThrottledTransitionStyle == mRefreshDriver->MostRecentRefresh();
 }
 
 void
-nsPresContext::TickLastUpdateThrottledStyle()
+nsPresContext::TickLastUpdateThrottledTransitionStyle()
+{
+  mLastUpdateThrottledTransitionStyle = mRefreshDriver->MostRecentRefresh();
+}
+
+bool
+nsPresContext::ThrottledAnimationStyleIsUpToDate() const
 {
-  mLastUpdateThrottledStyle = mRefreshDriver->MostRecentRefresh();
+  return
+    mLastUpdateThrottledAnimationStyle == mRefreshDriver->MostRecentRefresh();
+}
+
+void
+nsPresContext::TickLastUpdateThrottledAnimationStyle()
+{
+  mLastUpdateThrottledAnimationStyle = mRefreshDriver->MostRecentRefresh();
 }
 
 bool
 nsPresContext::StyleUpdateForAllAnimationsIsUpToDate()
 {
   return mLastStyleUpdateForAllAnimations == mRefreshDriver->MostRecentRefresh();
 }
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -652,18 +652,20 @@ public:
   void   SetBackgroundColorDraw(bool aCanDraw)
   {
     mDrawColorBackground = aCanDraw;
   }
   
   /**
    * Getter and setter for OMTA time counters
    */
-  bool ThrottledStyleIsUpToDate() const;
-  void TickLastUpdateThrottledStyle();
+  bool ThrottledTransitionStyleIsUpToDate() const;
+  void TickLastUpdateThrottledTransitionStyle();
+  bool ThrottledAnimationStyleIsUpToDate() const;
+  void TickLastUpdateThrottledAnimationStyle();
   bool StyleUpdateForAllAnimationsIsUpToDate();
   void TickLastStyleUpdateForAllAnimations();
 
 #ifdef IBMBIDI
   /**
    *  Check if bidi enabled (set depending on the presence of RTL
    *  characters or when default directionality is RTL).
    *  If enabled, we should apply the Unicode Bidi Algorithm
@@ -1228,18 +1230,20 @@ protected:
   LangGroupFontPrefs    mLangGroupFontPrefs;
 
   nscoord               mBorderWidthTable[3];
 
   uint32_t              mInterruptChecksToSkip;
 
   mozilla::TimeStamp    mReflowStartTime;
 
-  // last time animations/transition styles were flushed to their primary frames
-  mozilla::TimeStamp    mLastUpdateThrottledStyle;
+  // last time animations styles were flushed to their primary frames
+  mozilla::TimeStamp    mLastUpdateThrottledAnimationStyle;
+  // last time transition styles were flushed to their primary frames
+  mozilla::TimeStamp    mLastUpdateThrottledTransitionStyle;
   // last time we did a full style flush
   mozilla::TimeStamp    mLastStyleUpdateForAllAnimations;
 
   unsigned              mHasPendingInterrupt : 1;
   unsigned              mInterruptsEnabled : 1;
   unsigned              mUseDocumentFonts : 1;
   unsigned              mUseDocumentColors : 1;
   unsigned              mUnderlineLinks : 1;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6194,16 +6194,17 @@ nsIFrame* GetNearestFrameContainingPresS
 static bool
 FlushThrottledStyles(nsIDocument *aDocument, void *aData)
 {
   nsIPresShell* shell = aDocument->GetShell();
   if (shell && shell->IsVisible()) {
     nsPresContext* presContext = shell->GetPresContext();
     if (presContext) {
       presContext->TransitionManager()->UpdateAllThrottledStyles();
+      presContext->AnimationManager()->UpdateAllThrottledStyles();
     }
   }
 
   return true;
 }
 
 nsresult
 PresShell::HandleEvent(nsIFrame* aFrame,
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -1,26 +1,32 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "AnimationCommon.h"
+#include "nsTransitionManager.h"
+#include "nsAnimationManager.h"
+
 #include "gfxPlatform.h"
-#include "AnimationCommon.h"
 #include "nsRuleData.h"
 #include "nsCSSValue.h"
 #include "nsStyleContext.h"
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsDisplayList.h"
 #include "mozilla/MemoryReporting.h"
 #include "RestyleManager.h"
+#include "nsStyleSet.h"
+#include "nsStyleChangeList.h"
+
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace css {
 
 /* static */ bool
 IsGeometricProperty(nsCSSProperty aProperty)
@@ -137,16 +143,167 @@ CommonAnimationManager::ExtractComputedV
                         nsStyleAnimation::eUnit_Enumerated,
                       "unexpected unit");
     aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
                                nsStyleAnimation::eUnit_Visibility);
   }
   return result;
 }
 
+already_AddRefed<nsStyleContext>
+CommonAnimationManager::ReparentContent(nsIContent* aContent,
+                                        nsStyleContext* aParentStyle)
+{
+  nsStyleSet* styleSet = mPresContext->PresShell()->StyleSet();
+  nsIFrame* primaryFrame = nsLayoutUtils::GetStyleFrame(aContent);
+  if (!primaryFrame) {
+    return nullptr;
+  }
+
+  dom::Element* element = aContent->IsElement()
+                          ? aContent->AsElement()
+                          : nullptr;
+
+  nsRefPtr<nsStyleContext> newStyle =
+    styleSet->ReparentStyleContext(primaryFrame->StyleContext(),
+                                   aParentStyle, element);
+  primaryFrame->SetStyleContext(newStyle);
+  ReparentBeforeAndAfter(element, primaryFrame, newStyle, styleSet);
+
+  return newStyle.forget();
+}
+
+/* static */ void
+CommonAnimationManager::ReparentBeforeAndAfter(dom::Element* aElement,
+                                               nsIFrame* aPrimaryFrame,
+                                               nsStyleContext* aNewStyle,
+                                               nsStyleSet* aStyleSet)
+{
+  if (nsIFrame* before = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) {
+    nsRefPtr<nsStyleContext> beforeStyle =
+      aStyleSet->ReparentStyleContext(before->StyleContext(),
+                                     aNewStyle, aElement);
+    before->SetStyleContext(beforeStyle);
+  }
+  if (nsIFrame* after = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) {
+    nsRefPtr<nsStyleContext> afterStyle =
+      aStyleSet->ReparentStyleContext(after->StyleContext(),
+                                     aNewStyle, aElement);
+    after->SetStyleContext(afterStyle);
+  }
+}
+
+// Ensure that the next repaint rebuilds the layer tree for aFrame. That
+// means that changes to animations on aFrame's layer are propagated to
+// the compositor, which is needed for correct behaviour of new
+// transitions.
+static void
+ForceLayerRerendering(nsIFrame* aFrame, CommonElementAnimationData* aData)
+{
+  if (aData->HasAnimationOfProperty(eCSSProperty_opacity)) {
+    if (Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
+          aFrame, nsDisplayItem::TYPE_OPACITY)) {
+      layer->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
+    }
+  }
+
+  if (aData->HasAnimationOfProperty(eCSSProperty_transform)) {
+    if (Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
+          aFrame, nsDisplayItem::TYPE_TRANSFORM)) {
+      layer->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
+    }
+  }
+}
+
+nsStyleContext*
+CommonAnimationManager::UpdateThrottledStyle(dom::Element* aElement,
+                                             nsStyleContext* aParentStyle,
+                                             nsStyleChangeList& aChangeList)
+{
+  NS_ASSERTION(mPresContext->TransitionManager()->GetElementTransitions(
+                 aElement,
+                 nsCSSPseudoElements::ePseudo_NotPseudoElement,
+                 false) ||
+               mPresContext->AnimationManager()->GetElementAnimations(
+                 aElement,
+                 nsCSSPseudoElements::ePseudo_NotPseudoElement,
+                 false), "element not animated");
+
+  nsIFrame* primaryFrame = nsLayoutUtils::GetStyleFrame(aElement);
+  if (!primaryFrame) {
+    return nullptr;
+  }
+
+  nsStyleContext* oldStyle = primaryFrame->StyleContext();
+  nsRuleNode* ruleNode = oldStyle->RuleNode();
+  nsTArray<nsStyleSet::RuleAndLevel> rules;
+  do {
+    if (ruleNode->IsRoot()) {
+      break;
+    }
+
+    nsStyleSet::RuleAndLevel curRule;
+    curRule.mLevel = ruleNode->GetLevel();
+
+    if (curRule.mLevel == nsStyleSet::eAnimationSheet) {
+      ElementAnimations* ea =
+        mPresContext->AnimationManager()->GetElementAnimations(
+          aElement,
+          oldStyle->GetPseudoType(),
+          false);
+      NS_ASSERTION(ea,
+        "Rule has level eAnimationSheet without animation on manager");
+
+      mPresContext->AnimationManager()->EnsureStyleRuleFor(ea);
+      curRule.mRule = ea->mStyleRule;
+
+      // FIXME: maybe not needed anymore:
+      ForceLayerRerendering(primaryFrame, ea);
+    } else if (curRule.mLevel == nsStyleSet::eTransitionSheet) {
+      ElementTransitions *et =
+        mPresContext->TransitionManager()->GetElementTransitions(
+          aElement,
+          oldStyle->GetPseudoType(),
+          false);
+      NS_ASSERTION(et,
+        "Rule has level eTransitionSheet without transition on manager");
+
+      et->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh());
+      curRule.mRule = et->mStyleRule;
+
+      // FIXME: maybe not needed anymore:
+      ForceLayerRerendering(primaryFrame, et);
+    } else {
+      curRule.mRule = ruleNode->GetRule();
+    }
+
+    if (curRule.mRule) {
+      rules.AppendElement(curRule);
+    }
+  } while ((ruleNode = ruleNode->GetParent()));
+
+  nsRefPtr<nsStyleContext> newStyle = mPresContext->PresShell()->StyleSet()->
+    ResolveStyleForRules(aParentStyle, oldStyle, rules);
+
+  // We absolutely must call CalcStyleDifference in order to ensure the
+  // new context has all the structs cached that the old context had.
+  // We also need it for processing of the changes.
+  nsChangeHint styleChange =
+    oldStyle->CalcStyleDifference(newStyle, nsChangeHint(0));
+  aChangeList.AppendChange(primaryFrame, primaryFrame->GetContent(),
+                           styleChange);
+
+  primaryFrame->SetStyleContext(newStyle);
+
+  ReparentBeforeAndAfter(aElement, primaryFrame, newStyle,
+                         mPresContext->PresShell()->StyleSet());
+
+  return newStyle;
+}
+
 NS_IMPL_ISUPPORTS1(AnimValuesStyleRule, nsIStyleRule)
 
 /* virtual */ void
 AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
 {
   nsStyleContext *contextParent = aRuleData->mStyleContext->GetParent();
   if (contextParent && contextParent->HasPseudoElementData()) {
     // Don't apply transitions or animations to things inside of
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -12,16 +12,17 @@
 #include "prclist.h"
 #include "nsStyleAnimation.h"
 #include "nsCSSProperty.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h"
 #include "mozilla/Attributes.h"
+#include "nsCSSPseudoElements.h"
 
 class nsPresContext;
 class nsIFrame;
 
 
 namespace mozilla {
 namespace css {
 
@@ -66,20 +67,92 @@ public:
                   nsStyleAnimation::Value& aComputedValue);
 protected:
   friend struct CommonElementAnimationData; // for ElementDataRemoved
 
   virtual void AddElementData(CommonElementAnimationData* aData) = 0;
   virtual void ElementDataRemoved() = 0;
   void RemoveAllElementData();
 
+  // Update the style on aElement from the transition stored in this manager and
+  // the new parent style - aParentStyle. aElement must be transitioning or
+  // animated. Returns the updated style.
+  nsStyleContext* UpdateThrottledStyle(mozilla::dom::Element* aElement,
+                                       nsStyleContext* aParentStyle,
+                                       nsStyleChangeList &aChangeList);
+  // Reparent the style of aContent and any :before and :after pseudo-elements.
+  already_AddRefed<nsStyleContext> ReparentContent(nsIContent* aContent,
+                                                  nsStyleContext* aParentStyle);
+  // reparent :before and :after pseudo elements of aElement
+  static void ReparentBeforeAndAfter(dom::Element* aElement,
+                                     nsIFrame* aPrimaryFrame,
+                                     nsStyleContext* aNewStyle,
+                                     nsStyleSet* aStyleSet);
+
   PRCList mElementData;
   nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
 };
 
+// The internals of UpdateAllThrottledStyles, used by nsAnimationManager and
+// nsTransitionManager, see the comments in the declaration of the latter.
+#define IMPL_UPDATE_ALL_THROTTLED_STYLES_INTERNAL(class_, animations_getter_)  \
+void                                                                           \
+class_::UpdateAllThrottledStylesInternal()                                     \
+{                                                                              \
+  TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();          \
+                                                                               \
+  nsStyleChangeList changeList;                                                \
+                                                                               \
+  /* update each transitioning element by finding its root-most ancestor
+     with a transition, and flushing the style on that ancestor and all
+     its descendants*/                                                         \
+  PRCList *next = PR_LIST_HEAD(&mElementData);                                 \
+  while (next != &mElementData) {                                              \
+    CommonElementAnimationData* ea =                                           \
+      static_cast<CommonElementAnimationData*>(next);                          \
+    next = PR_NEXT_LINK(next);                                                 \
+                                                                               \
+    if (ea->mFlushGeneration == now) {                                         \
+      /* this element has been ticked already */                               \
+      continue;                                                                \
+    }                                                                          \
+                                                                               \
+    /* element is initialised to the starting element (i.e., one we know has
+       an animation) and ends up with the root-most animated ancestor,
+       that is, the element where we begin updates. */                         \
+    dom::Element* element = ea->mElement;                                      \
+    /* make a list of ancestors */                                             \
+    nsTArray<dom::Element*> ancestors;                                         \
+    do {                                                                       \
+      ancestors.AppendElement(element);                                        \
+    } while ((element = element->GetParentElement()));                         \
+                                                                               \
+    /* walk down the ancestors until we find one with a throttled transition */\
+    for (int32_t i = ancestors.Length() - 1; i >= 0; --i) {                    \
+      if (animations_getter_(ancestors[i],                                     \
+                            nsCSSPseudoElements::ePseudo_NotPseudoElement,     \
+                            false)) {                                          \
+        element = ancestors[i];                                                \
+        break;                                                                 \
+      }                                                                        \
+    }                                                                          \
+                                                                               \
+    nsIFrame* primaryFrame;                                                    \
+    if (element &&                                                             \
+        (primaryFrame = nsLayoutUtils::GetStyleFrame(element))) {              \
+      UpdateThrottledStylesForSubtree(element,                                 \
+        primaryFrame->StyleContext()->GetParent(), changeList);                \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  RestyleManager* restyleManager = mPresContext->RestyleManager();             \
+  restyleManager->ProcessRestyledFrames(changeList);                           \
+  restyleManager->FlushOverflowChangedTracker();                               \
+}
+
 /**
  * A style rule that maps property-nsStyleAnimation::Value pairs.
  */
 class AnimValuesStyleRule MOZ_FINAL : public nsIStyleRule
 {
 public:
   // nsISupports implementation
   NS_DECL_ISUPPORTS
@@ -128,21 +201,22 @@ private:
   Type mType;
   nsSMILKeySpline mTimingFunction;
   uint32_t mSteps;
 };
 
 struct CommonElementAnimationData : public PRCList
 {
   CommonElementAnimationData(dom::Element *aElement, nsIAtom *aElementProperty,
-                             CommonAnimationManager *aManager)
+                             CommonAnimationManager *aManager, TimeStamp aNow)
     : mElement(aElement)
     , mElementProperty(aElementProperty)
     , mManager(aManager)
     , mAnimationGeneration(0)
+    , mFlushGeneration(aNow)
 #ifdef DEBUG
     , mCalledPropertyDtor(false)
 #endif
   {
     MOZ_COUNT_CTOR(CommonElementAnimationData);
     PR_INIT_CLIST(this);
   }
   ~CommonElementAnimationData()
@@ -212,16 +286,21 @@ struct CommonElementAnimationData : publ
   // date with the animation manager.
   uint64_t mAnimationGeneration;
   // Update mFlushCount to nsCSSFrameConstructor's count
   void UpdateAnimationGeneration(nsPresContext* aPresContext);
 
   // The refresh time associated with mStyleRule.
   TimeStamp mStyleRuleRefreshTime;
 
+  // Generation counter for flushes of throttled animations.
+  // Used to prevent updating the styles twice for a given element during
+  // UpdateAllThrottledStyles.
+  TimeStamp mFlushGeneration;
+
 #ifdef DEBUG
   bool mCalledPropertyDtor;
 #endif
 };
 
 }
 }
 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -1,36 +1,41 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAnimationManager.h"
+#include "nsTransitionManager.h"
 
 #include "mozilla/MemoryReporting.h"
 
 #include "nsPresContext.h"
 #include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
+#include "nsStyleChangeList.h"
 #include "nsCSSRules.h"
+#include "RestyleManager.h"
 #include "nsStyleAnimation.h"
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "ActiveLayerTracker.h"
 #include <math.h>
 
 using namespace mozilla;
 using namespace mozilla::css;
 
-ElementAnimations::ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty,
-                                     nsAnimationManager *aAnimationManager)
+ElementAnimations::ElementAnimations(mozilla::dom::Element *aElement,
+                                     nsIAtom *aElementProperty,
+                                     nsAnimationManager *aAnimationManager,
+                                     TimeStamp aNow)
   : CommonElementAnimationData(aElement, aElementProperty,
-                               aAnimationManager),
+                               aAnimationManager, aNow),
     mNeedsRefreshes(true)
 {
 }
 
 static void
 ElementAnimationsPropertyDtor(void           *aObject,
                               nsIAtom        *aPropertyName,
                               void           *aPropertyValue,
@@ -447,17 +452,18 @@ nsAnimationManager::GetElementAnimations
                  "should never try to create transitions for pseudo "
                  "other than :before or :after");
     return nullptr;
   }
   ElementAnimations *ea = static_cast<ElementAnimations*>(
                              aElement->GetProperty(propName));
   if (!ea && aCreateIfNeeded) {
     // FIXME: Consider arena-allocating?
-    ea = new ElementAnimations(aElement, propName, this);
+    ea = new ElementAnimations(aElement, propName, this,
+           mPresContext->RefreshDriver()->MostRecentRefresh());
     nsresult rv = aElement->SetProperty(propName, ea,
                                         ElementAnimationsPropertyDtor, false);
     if (NS_FAILED(rv)) {
       NS_WARNING("SetProperty failed");
       delete ea;
       return nullptr;
     }
     if (propName == nsGkAtoms::animationsProperty) {
@@ -1084,8 +1090,67 @@ nsAnimationManager::DoDispatchEvents()
     AnimationEventInfo &info = events[i];
     nsEventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
 
     if (!mPresContext) {
       break;
     }
   }
 }
+
+void
+nsAnimationManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
+                                                nsStyleContext* aParentStyle,
+                                                nsStyleChangeList& aChangeList)
+{
+  dom::Element* element;
+  if (aContent->IsElement()) {
+    element = aContent->AsElement();
+  } else {
+    element = nullptr;
+  }
+
+  nsRefPtr<nsStyleContext> newStyle;
+
+  ElementAnimations* ea;
+  if (element &&
+      (ea = GetElementAnimations(element,
+                                 nsCSSPseudoElements::ePseudo_NotPseudoElement,
+                                 false))) {
+    // re-resolve our style
+    newStyle = UpdateThrottledStyle(element, aParentStyle, aChangeList);
+    // remove the current transition from the working set
+    ea->mFlushGeneration = mPresContext->RefreshDriver()->MostRecentRefresh();
+  } else {
+    newStyle = ReparentContent(aContent, aParentStyle);
+  }
+
+  // walk the children
+  if (newStyle) {
+    for (nsIContent *child = aContent->GetFirstChild(); child;
+         child = child->GetNextSibling()) {
+      UpdateThrottledStylesForSubtree(child, newStyle, aChangeList);
+    }
+  }
+}
+
+IMPL_UPDATE_ALL_THROTTLED_STYLES_INTERNAL(nsAnimationManager,
+                                          GetElementAnimations)
+
+void
+nsAnimationManager::UpdateAllThrottledStyles()
+{
+  if (PR_CLIST_IS_EMPTY(&mElementData)) {
+    // no throttled animations, leave early
+    mPresContext->TickLastUpdateThrottledAnimationStyle();
+    return;
+  }
+
+  if (mPresContext->ThrottledAnimationStyleIsUpToDate()) {
+    // throttled transitions are up to date, leave early
+    return;
+  }
+
+  mPresContext->TickLastUpdateThrottledAnimationStyle();
+
+  UpdateAllThrottledStylesInternal();
+}
+
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -123,17 +123,17 @@ struct ElementAnimation
  */
 struct ElementAnimations MOZ_FINAL
   : public mozilla::css::CommonElementAnimationData
 {
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
 
   ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty,
-                    nsAnimationManager *aAnimationManager);
+                    nsAnimationManager *aAnimationManager, TimeStamp aNow);
 
   // This function takes as input the start time, duration, and direction of an
   // animation and returns the position in the current iteration.  Note that
   // this only works when we know that the animation is currently running.
   // This way of calling the function can be used from the compositor.  Note
   // that if the animation has not started yet, has already ended, or is paused,
   // it should not be run from the compositor.  When this function is called 
   // from the main thread, we need the actual ElementAnimation* in order to 
@@ -270,16 +270,19 @@ public:
       DoDispatchEvents();
     }
   }
 
   ElementAnimations* GetElementAnimations(mozilla::dom::Element *aElement,
                                           nsCSSPseudoElements::Type aPseudoType,
                                           bool aCreateIfNeeded);
 
+  // Updates styles on throttled animations. See note on nsTransitionManager
+  void UpdateAllThrottledStyles();
+
 protected:
   virtual void ElementDataRemoved() MOZ_OVERRIDE
   {
     CheckNeedsRefresh();
   }
   virtual void AddElementData(mozilla::css::CommonElementAnimationData* aData) MOZ_OVERRIDE;
 
   /**
@@ -293,16 +296,24 @@ private:
   bool BuildSegment(InfallibleTArray<AnimationPropertySegment>& aSegments,
                     nsCSSProperty aProperty, const nsAnimation& aAnimation,
                     float aFromKey, nsStyleContext* aFromContext,
                     mozilla::css::Declaration* aFromDeclaration,
                     float aToKey, nsStyleContext* aToContext);
   nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
                                  nsCSSPseudoElements::Type aPseudoType);
 
+  // Update the animated styles of an element and its descendants.
+  // If the element has an animation, it is flushed back to its primary frame.
+  // If the element does not have an animation, then its style is reparented.
+  void UpdateThrottledStylesForSubtree(nsIContent* aContent,
+                                       nsStyleContext* aParentStyle,
+                                       nsStyleChangeList &aChangeList);
+  void UpdateAllThrottledStylesInternal();
+
   // The guts of DispatchEvents
   void DoDispatchEvents();
 
   EventArray mPendingEvents;
 
   bool mObservingRefreshDriver;
 };
 
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -36,18 +36,18 @@ using mozilla::TimeDuration;
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::css;
 
 ElementTransitions::ElementTransitions(mozilla::dom::Element *aElement,
                                        nsIAtom *aElementProperty,
                                        nsTransitionManager *aTransitionManager,
                                        TimeStamp aNow)
-  : CommonElementAnimationData(aElement, aElementProperty, aTransitionManager)
-  , mFlushGeneration(aNow)
+  : CommonElementAnimationData(aElement, aElementProperty,
+                               aTransitionManager, aNow)
 {
 }
 
 double
 ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const
 {
   // Set |timePortion| to the portion of the way we are through the time
   // input to the transition's timing function (always within the range
@@ -205,132 +205,16 @@ ElementTransitions::CanPerformOnComposit
   }
   return true;
 }
 
 /*****************************************************************************
  * nsTransitionManager                                                       *
  *****************************************************************************/
 
-// reparent :before and :after pseudo elements of aElement
-static void ReparentBeforeAndAfter(dom::Element* aElement,
-                                   nsIFrame* aPrimaryFrame,
-                                   nsStyleContext* aNewStyle,
-                                   nsStyleSet* aStyleSet)
-{
-  if (nsIFrame* before = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) {
-    nsRefPtr<nsStyleContext> beforeStyle =
-      aStyleSet->ReparentStyleContext(before->StyleContext(),
-                                     aNewStyle, aElement);
-    before->SetStyleContext(beforeStyle);
-  }
-  if (nsIFrame* after = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) {
-    nsRefPtr<nsStyleContext> afterStyle =
-      aStyleSet->ReparentStyleContext(after->StyleContext(),
-                                     aNewStyle, aElement);
-    after->SetStyleContext(afterStyle);
-  }
-}
-
-// Ensure that the next repaint rebuilds the layer tree for aFrame. That
-// means that changes to animations on aFrame's layer are propagated to
-// the compositor, which is needed for correct behaviour of new
-// transitions.
-static void
-ForceLayerRerendering(nsIFrame* aFrame, CommonElementAnimationData* aData)
-{
-  if (aData->HasAnimationOfProperty(eCSSProperty_opacity)) {
-    if (Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
-          aFrame, nsDisplayItem::TYPE_OPACITY)) {
-      layer->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
-    }
-  } 
-  
-  if (aData->HasAnimationOfProperty(eCSSProperty_transform)) {
-    if (Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
-          aFrame, nsDisplayItem::TYPE_TRANSFORM)) {
-      layer->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
-    }
-  }
-}
-
-nsStyleContext*
-nsTransitionManager::UpdateThrottledStyle(dom::Element* aElement,
-                                          nsStyleContext* aParentStyle,
-                                          nsStyleChangeList& aChangeList)
-{
-  NS_ASSERTION(GetElementTransitions(aElement,
-                                     nsCSSPseudoElements::ePseudo_NotPseudoElement,
-                                     false), "element not transitioning");
-
-  nsIFrame* primaryFrame = nsLayoutUtils::GetStyleFrame(aElement);
-  if (!primaryFrame) {
-    return nullptr;
-  }
-
-  nsStyleContext* oldStyle = primaryFrame->StyleContext();
-  nsRuleNode* ruleNode = oldStyle->RuleNode();
-  nsTArray<nsStyleSet::RuleAndLevel> rules;
-  do {
-    if (ruleNode->IsRoot()) {
-      break;
-    }
-
-    nsStyleSet::RuleAndLevel curRule;
-    curRule.mLevel = ruleNode->GetLevel();
-
-    if (curRule.mLevel == nsStyleSet::eAnimationSheet) {
-      ElementAnimations* ea = 
-        mPresContext->AnimationManager()->GetElementAnimations(aElement,
-                                                               oldStyle->GetPseudoType(),
-                                                               false);
-      NS_ASSERTION(ea, "Rule has level eAnimationSheet without animation on manager");
-
-      mPresContext->AnimationManager()->EnsureStyleRuleFor(ea);
-      curRule.mRule = ea->mStyleRule;
-
-      // FIXME: maybe not needed anymore:
-      ForceLayerRerendering(primaryFrame, ea);
-    } else if (curRule.mLevel == nsStyleSet::eTransitionSheet) {
-      ElementTransitions *et =
-        GetElementTransitions(aElement, oldStyle->GetPseudoType(), false);
-      NS_ASSERTION(et, "Rule has level eTransitionSheet without transition on manager");
-      
-      et->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh());
-      curRule.mRule = et->mStyleRule;
-
-      // FIXME: maybe not needed anymore:
-      ForceLayerRerendering(primaryFrame, et);
-    } else {
-      curRule.mRule = ruleNode->GetRule();
-    }
-
-    if (curRule.mRule) {
-      rules.AppendElement(curRule);
-    }
-  } while ((ruleNode = ruleNode->GetParent()));
-
-  nsRefPtr<nsStyleContext> newStyle = mPresContext->PresShell()->StyleSet()->
-    ResolveStyleForRules(aParentStyle, oldStyle, rules);
-
-  // We absolutely must call CalcStyleDifference in order to ensure the
-  // new context has all the structs cached that the old context had.
-  // We also need it for processing of the changes.
-  nsChangeHint styleChange =
-    oldStyle->CalcStyleDifference(newStyle, nsChangeHint(0));
-  aChangeList.AppendChange(primaryFrame, primaryFrame->GetContent(),
-                           styleChange);
-
-  primaryFrame->SetStyleContext(newStyle);
-
-  ReparentBeforeAndAfter(aElement, primaryFrame, newStyle, mPresContext->PresShell()->StyleSet());
-
-  return newStyle;
-}
-
 void
 nsTransitionManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
                                                      nsStyleContext* aParentStyle,
                                                      nsStyleChangeList& aChangeList)
 {
   dom::Element* element;
   if (aContent->IsElement()) {
     element = aContent->AsElement();
@@ -345,100 +229,47 @@ nsTransitionManager::UpdateThrottledStyl
       (et = GetElementTransitions(element,
                                   nsCSSPseudoElements::ePseudo_NotPseudoElement,
                                   false))) {
     // re-resolve our style
     newStyle = UpdateThrottledStyle(element, aParentStyle, aChangeList);
     // remove the current transition from the working set
     et->mFlushGeneration = mPresContext->RefreshDriver()->MostRecentRefresh();
   } else {
-    // reparent the element's style
-    nsStyleSet* styleSet = mPresContext->PresShell()->StyleSet();
-    nsIFrame* primaryFrame = nsLayoutUtils::GetStyleFrame(aContent);
-    if (!primaryFrame) {
-      return;
-    }
-
-    newStyle = styleSet->ReparentStyleContext(primaryFrame->StyleContext(),
-                                              aParentStyle, element);
-    primaryFrame->SetStyleContext(newStyle);
-    ReparentBeforeAndAfter(element, primaryFrame, newStyle, styleSet);
+    newStyle = ReparentContent(aContent, aParentStyle);
   }
 
   // walk the children
   if (newStyle) {
     for (nsIContent *child = aContent->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       UpdateThrottledStylesForSubtree(child, newStyle, aChangeList);
     }
   }
 }
 
+IMPL_UPDATE_ALL_THROTTLED_STYLES_INTERNAL(nsTransitionManager,
+                                          GetElementTransitions)
+
 void
 nsTransitionManager::UpdateAllThrottledStyles()
 {
   if (PR_CLIST_IS_EMPTY(&mElementData)) {
     // no throttled transitions, leave early
-    mPresContext->TickLastUpdateThrottledStyle();
+    mPresContext->TickLastUpdateThrottledTransitionStyle();
     return;
   }
 
-  if (mPresContext->ThrottledStyleIsUpToDate()) {
+  if (mPresContext->ThrottledTransitionStyleIsUpToDate()) {
     // throttled transitions are up to date, leave early
     return;
   }
 
-  mPresContext->TickLastUpdateThrottledStyle();
-  TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
-
-  nsStyleChangeList changeList;
-
-  // update each transitioning element by finding its root-most ancestor with a
-  // transition, and flushing the style on that ancestor and all its descendants
-  PRCList *next = PR_LIST_HEAD(&mElementData);
-  while (next != &mElementData) {
-    ElementTransitions* et = static_cast<ElementTransitions*>(next);
-    next = PR_NEXT_LINK(next);
-
-    if (et->mFlushGeneration == now) {
-      // this element has been ticked already
-      continue;
-    }
-
-    // element is initialised to the starting element (i.e., one we know has
-    // a transition) and ends up with the root-most transitioning ancestor,
-    // that is, the element where we begin updates.
-    dom::Element* element = et->mElement;
-    // make a list of ancestors
-    nsTArray<dom::Element*> ancestors;
-    do {
-      ancestors.AppendElement(element);
-    } while ((element = element->GetParentElement()));
-
-    // walk down the ancestors until we find one with a throttled transition
-    for (int32_t i = ancestors.Length() - 1; i >= 0; --i) {
-      if (GetElementTransitions(ancestors[i],
-                                nsCSSPseudoElements::ePseudo_NotPseudoElement,
-                                false)) {
-        element = ancestors[i];
-        break;
-      }
-    }
-
-    nsIFrame* primaryFrame;
-    if (element &&
-        (primaryFrame = nsLayoutUtils::GetStyleFrame(element))) {
-      UpdateThrottledStylesForSubtree(element,
-        primaryFrame->StyleContext()->GetParent(), changeList);
-    }
-  }
-
-  RestyleManager* restyleManager = mPresContext->RestyleManager();
-  restyleManager->ProcessRestyledFrames(changeList);
-  restyleManager->FlushOverflowChangedTracker();
+  mPresContext->TickLastUpdateThrottledTransitionStyle();
+  UpdateAllThrottledStylesInternal();
 }
 
 void
 nsTransitionManager::ElementDataRemoved()
 {
   // If we have no transitions or animations left, remove ourselves from
   // the refresh driver.
   if (PR_CLIST_IS_EMPTY(&mElementData)) {
@@ -522,17 +353,17 @@ nsTransitionManager::StyleContextChanged
       aNewStyleContext->GetParent()->HasPseudoElementData()) {
     // Ignore transitions on things that inherit properties from
     // pseudo-elements.
     // FIXME (Bug 522599): Add tests for this.
     return nullptr;
   }
 
   NS_WARN_IF_FALSE(!nsLayoutUtils::AreAsyncAnimationsEnabled() ||
-                     mPresContext->ThrottledStyleIsUpToDate(),
+                     mPresContext->ThrottledTransitionStyleIsUpToDate(),
                    "throttled animations not up to date");
 
   // Per http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html
   // I'll consider only the transitions from the number of items in
   // 'transition-property' on down, and later ones will override earlier
   // ones (tracked using |whichStarted|).
   bool startedAny = false;
   nsCSSPropertySet whichStarted;
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -86,21 +86,16 @@ struct ElementTransitions MOZ_FINAL
 
   void EnsureStyleRuleFor(mozilla::TimeStamp aRefreshTime);
 
   virtual bool HasAnimationOfProperty(nsCSSProperty aProperty) const MOZ_OVERRIDE;
   virtual bool CanPerformOnCompositorThread(CanAnimateFlags aFlags) const MOZ_OVERRIDE;
 
   // Either zero or one for each CSS property:
   nsTArray<ElementPropertyTransition> mPropertyTransitions;
-
-  // Generation counter for flushes of throttled transitions.
-  // Used to prevent updating the styles twice for a given element during
-  // UpdateAllThrottledStyles.
-  mozilla::TimeStamp mFlushGeneration;
 };
 
 
 
 class nsTransitionManager MOZ_FINAL
   : public mozilla::css::CommonAnimationManager
 {
 public:
@@ -198,42 +193,37 @@ public:
   // primary frame.  So the purpose of the mini-flush is to update the
   // style for all throttled transitions and animations to the current
   // animation state without making any other updates, so that when we
   // process the queued style updates we'll have correct old data to
   // compare against.  When we do this, we don't bother touching frames
   // other than primary frames.
   void UpdateAllThrottledStyles();
 
+  ElementTransitions* GetElementTransitions(mozilla::dom::Element *aElement,
+                                          nsCSSPseudoElements::Type aPseudoType,
+                                          bool aCreateIfNeeded);
+
 protected:
   virtual void ElementDataRemoved() MOZ_OVERRIDE;
   virtual void AddElementData(mozilla::css::CommonElementAnimationData* aData) MOZ_OVERRIDE;
 
 private:
   void ConsiderStartingTransition(nsCSSProperty aProperty,
                                   const nsTransition& aTransition,
                                   mozilla::dom::Element *aElement,
                                   ElementTransitions *&aElementTransitions,
                                   nsStyleContext *aOldStyleContext,
                                   nsStyleContext *aNewStyleContext,
                                   bool *aStartedAny,
                                   nsCSSPropertySet *aWhichStarted);
-  ElementTransitions* GetElementTransitions(mozilla::dom::Element *aElement,
-                                            nsCSSPseudoElements::Type aPseudoType,
-                                            bool aCreateIfNeeded);
   void WalkTransitionRule(ElementDependentRuleProcessorData* aData,
                           nsCSSPseudoElements::Type aPseudoType);
-
   // Update the animated styles of an element and its descendants.
   // If the element has a transition, it is flushed back to its primary frame.
   // If the element does not have a transition, then its style is reparented.
   void UpdateThrottledStylesForSubtree(nsIContent* aContent,
                                        nsStyleContext* aParentStyle,
                                        nsStyleChangeList &aChangeList);
-  // Update the style on aElement from the transition stored in this manager and
-  // the new parent style - aParentStyle. aElement must be transitioning or
-  // animated. Returns the updated style.
-  nsStyleContext* UpdateThrottledStyle(mozilla::dom::Element* aElement,
-                                       nsStyleContext* aParentStyle,
-                                       nsStyleChangeList &aChangeList);
+  void UpdateAllThrottledStylesInternal();
 };
 
 #endif /* !defined(nsTransitionManager_h_) */