Bug 914847. Mini-flush for animations. r=dbaron
authorNicholas Cameron <ncameron@mozilla.com>
Tue, 22 Oct 2013 14:14:41 +0200
changeset 164119 8aacc8a6747279c7846a376678b93654e7fac008
parent 164118 1c63f67bbaf11247b00233f95270098458dc632b
child 164120 b07b7fca7f6c44d16d0b514160ff01f494023443
push id26026
push userphilringnalda@gmail.com
push dateSat, 18 Jan 2014 23:17:27 +0000
treeherdermozilla-central@61fd0f987cf2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs914847
milestone29.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 914847. Mini-flush for animations. r=dbaron
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
@@ -1050,17 +1050,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,
@@ -1574,25 +1575,39 @@ nsPresContext::GetContainerWeakExternal(
 
 nsIDocShell*
 nsPresContext::GetDocShell() const
 {
   return mContainer;
 }
 
 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
@@ -659,18 +659,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
@@ -1235,18 +1237,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
@@ -6206,16 +6206,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) {
@@ -1088,8 +1094,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_) */