--- 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,
--- 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_) */