Back out bug 996796 patch 18 through patch 25 (changesets fbe97c2db729 through 9719c08c3144) to fix 50%-of-the-time Android 4.0 debug orange from single assertion ("Why did this not get handled while processing mRestyleRoots?", layout/base/RestyleTracker.cpp, line 87) in gfx/tests/crashtests/815489.html .

This commit is contained in:
L. David Baron 2014-08-03 01:22:07 -07:00
parent fe9339c972
commit a39da5881c
11 changed files with 345 additions and 243 deletions

View File

@ -890,7 +890,7 @@ RestyleManager::RestyleElement(Element* aElement,
newContext->StyleFont()->mFont.size) {
// The basis for 'rem' units has changed.
newContext = nullptr;
DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0), aRestyleHint);
DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0));
if (aMinHint == 0) {
return;
}
@ -906,15 +906,8 @@ RestyleManager::RestyleElement(Element* aElement,
ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
aRestyleTracker, aRestyleHint);
ProcessRestyledFrames(changeList);
} else if (aRestyleHint & ~eRestyle_LaterSiblings) {
// We're restyling an element with no frame, so we should try to
// make one if its new style says it should have one. But in order
// to try to honor the restyle hint (which we'd like to do so that,
// for example, an animation-only style flush doesn't flush other
// buffered style changes), we only do this if the restyle hint says
// we have *some* restyling for this frame. This means we'll
// potentially get ahead of ourselves in that case, but not as much
// as we would if we didn't check the restyle hint.
} else {
// no frames, reconstruct for content
FrameConstructor()->MaybeRecreateFramesForElement(aElement);
}
}
@ -1413,13 +1406,7 @@ RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
mPresContext->SetProcessingRestyles(true);
// FIXME (bug 1047928): Many of the callers probably don't need
// eRestyle_Subtree because they're changing things that affect data
// computation rather than selector matching; we could have a restyle
// hint passed in, and substantially improve the performance of things
// like pref changes and the restyling that we do for downloadable
// font loads.
DoRebuildAllStyleData(mPendingRestyles, aExtraHint, eRestyle_Subtree);
DoRebuildAllStyleData(mPendingRestyles, aExtraHint);
mPresContext->SetProcessingRestyles(false);
@ -1432,8 +1419,7 @@ RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
void
RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint)
nsChangeHint aExtraHint)
{
// Tell the style set to get the old rule tree out of the way
// so we can recalculate while maintaining rule tree immutability
@ -1442,19 +1428,6 @@ RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
return;
}
if (aRestyleHint & ~eRestyle_Subtree) {
// We want this hint to apply to the root node's primary frame
// rather than the root frame, since it's the primary frame that has
// the styles for the root element (rather than the ancestors of the
// primary frame whose mContent is the root node but which have
// different styles). If we use up the hint for one of the
// ancestors that we hit first, then we'll fail to do the restyling
// we need to do.
aRestyleTracker.AddPendingRestyle(mPresContext->Document()->GetRootElement(),
aRestyleHint, nsChangeHint(0));
aRestyleHint = nsRestyleHint(0);
}
// Recalculate all of the style contexts for the document
// Note that we can ignore the return value of ComputeStyleChangeFor
// because we never need to reframe the root frame
@ -1463,12 +1436,11 @@ RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
// on us re-running rule matching here
nsStyleChangeList changeList;
// XXX Does it matter that we're passing aExtraHint to the real root
// frame and not the root node's primary frame? (We could do
// roughly what we do for aRestyleHint above.)
// frame and not the root node's primary frame?
// Note: The restyle tracker we pass in here doesn't matter.
ComputeStyleChangeFor(mPresContext->PresShell()->GetRootFrame(),
&changeList, aExtraHint,
aRestyleTracker, aRestyleHint);
aRestyleTracker, eRestyle_Subtree);
// Process the required changes
ProcessRestyledFrames(changeList);
FlushOverflowChangedTracker();
@ -1574,25 +1546,8 @@ RestyleManager::UpdateOnlyAnimationStyles()
}
mLastUpdateForThrottledAnimations = now;
nsTransitionManager* transitionManager = mPresContext->TransitionManager();
nsAnimationManager* animationManager = mPresContext->AnimationManager();
transitionManager->SetInAnimationOnlyStyleUpdate(true);
RestyleTracker tracker(ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT);
tracker.Init(this);
// FIXME: We should have the transition manager and animation manager
// add only the elements for which animations are currently throttled
// (i.e., animating on the compositor with main-thread style updates
// suppressed).
transitionManager->AddStyleUpdatesTo(tracker);
animationManager->AddStyleUpdatesTo(tracker);
tracker.ProcessRestyles();
transitionManager->SetInAnimationOnlyStyleUpdate(false);
mPresContext->TransitionManager()->UpdateAllThrottledStyles();
mPresContext->AnimationManager()->UpdateAllThrottledStyles();
}
void
@ -1988,8 +1943,7 @@ GetPrevContinuationWithSameStyle(nsIFrame* aFrame)
*/
static nsIFrame*
GetNextContinuationWithSameStyle(nsIFrame* aFrame,
nsStyleContext* aOldStyleContext,
bool* aHaveMoreContinuations = nullptr)
nsStyleContext* aOldStyleContext)
{
// See GetPrevContinuationWithSameStyle about {ib} splits.
@ -2019,9 +1973,6 @@ GetNextContinuationWithSameStyle(nsIFrame* aFrame,
aOldStyleContext->GetParent() != nextStyle->GetParent(),
"continuations should have the same style context");
nextContinuation = nullptr;
if (aHaveMoreContinuations) {
*aHaveMoreContinuations = true;
}
}
return nextContinuation;
}
@ -2384,24 +2335,13 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
"eRestyle_LaterSiblings must not be part of aRestyleHint");
nsRestyleHint hintToRestore = nsRestyleHint(0);
if (mContent && mContent->IsElement() &&
// If we're we're resolving from the root of the frame tree (which
// we do in DoRebuildAllStyleData), we need to avoid getting the
// root's restyle data until we get to its primary frame, since
// it's the primary frame that has the styles for the root element
// (rather than the ancestors of the primary frame whose mContent
// is the root node but which have different styles). If we use
// up the hint for one of the ancestors that we hit first, then
// we'll fail to do the restyling we need to do.
(mContent->GetParent() || mContent->GetPrimaryFrame() == mFrame)) {
if (mContent && mContent->IsElement()) {
mContent->OwnerDoc()->FlushPendingLinkUpdates();
RestyleTracker::RestyleData restyleData;
if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) {
if (NS_UpdateHint(mHintsHandled, restyleData.mChangeHint)) {
mChangeList->AppendChange(mFrame, mContent, restyleData.mChangeHint);
}
hintToRestore = restyleData.mRestyleHint;
aRestyleHint = nsRestyleHint(aRestyleHint | restyleData.mRestyleHint);
}
}
@ -2414,20 +2354,10 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
// TEMPORARY (until bug 918064): Call RestyleSelf for each
// continuation or block-in-inline sibling.
bool haveMoreContinuations = false;
for (nsIFrame* f = mFrame; f;
f = GetNextContinuationWithSameStyle(f, oldContext,
&haveMoreContinuations)) {
f = GetNextContinuationWithSameStyle(f, oldContext)) {
RestyleSelf(f, aRestyleHint);
}
if (haveMoreContinuations && hintToRestore) {
// If we have more continuations with different style (e.g., because
// we're inside a ::first-letter or ::first-line), put the restyle
// hint back.
mRestyleTracker.AddPendingRestyleToTable(mContent->AsElement(),
hintToRestore, nsChangeHint(0));
}
}
RestyleChildren(childRestyleHint);
@ -2525,18 +2455,22 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
"non pseudo-element frame without content node");
newContext = styleSet->ResolveStyleForNonElement(parentContext);
}
else if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
else if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree)) &&
!prevContinuation) {
// Unfortunately, if prevContinuation is non-null then we may have
// already stolen the restyle tracker entry for this element while
// processing prevContinuation. So we don't know whether aRestyleHint
// should really be 0 here or whether it should be eRestyle_Self. Be
// pessimistic and force an actual reresolve in that situation. The good
// news is that in the common case when prevContinuation is non-null we
// just used prevContinuationContext anyway and aren't reaching this code
// to start with.
Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
if (aRestyleHint == nsRestyleHint(0) &&
!styleSet->IsInRuleTreeReconstruct()) {
if (aRestyleHint == nsRestyleHint(0)) {
newContext =
styleSet->ReparentStyleContext(oldContext, parentContext, element);
} else {
// Use ResolveStyleWithReplacement either for actual replacements
// or, with no replacements, as a substitute for
// ReparentStyleContext that rebuilds the path in the rule tree
// rather than reusing the rule node, as we need to do during a
// rule tree reconstruct.
newContext =
styleSet->ResolveStyleWithReplacement(element, parentContext, oldContext,
aRestyleHint);
@ -2643,26 +2577,11 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
NS_ASSERTION(extraPseudoTag &&
extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
"extra style context is not pseudo element");
if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
Element* element = extraPseudoType != nsCSSPseudoElements::ePseudo_AnonBox
? mContent->AsElement() : nullptr;
if (styleSet->IsInRuleTreeReconstruct()) {
// Use ResolveStyleWithReplacement as a substitute for
// ReparentStyleContext that rebuilds the path in the rule tree
// rather than reusing the rule node, as we need to do during a
// rule tree reconstruct.
newExtraContext =
styleSet->ResolveStyleWithReplacement(element, newContext,
oldExtraContext,
nsRestyleHint(0));
} else {
newExtraContext =
styleSet->ReparentStyleContext(oldExtraContext, newContext, element);
}
} else if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
newContext);
} else {
}
else {
// Don't expect XUL tree stuff here, since it needs a comparator and
// all.
NS_ASSERTION(extraPseudoType <
@ -2688,20 +2607,6 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
void
ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
{
// We'd like style resolution to be exact in the sense that an
// animation-only style flush flushes only the styles it requests
// flushing and doesn't update any other styles. This means avoiding
// constructing new frames during such a flush.
//
// For a ::before or ::after, we'll do an eRestyle_Subtree due to
// RestyleHintForOp in nsCSSRuleProcessor.cpp (via its
// HasAttributeDependentStyle or HasStateDependentStyle), given that
// we store pseudo-elements in selectors like they were children.
//
// Also, it's faster to skip the work we do on undisplayed children
// and pseudo-elements when we can skip it.
bool mightReframePseudos = aChildRestyleHint & eRestyle_Subtree;
RestyleUndisplayedChildren(aChildRestyleHint);
// Check whether we might need to create a new ::before frame.
@ -2712,7 +2617,7 @@ ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
// ReconstructFrame hint. Using an out of date style context could
// trigger assertions about mismatched rule trees.
if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
mightReframePseudos) {
aChildRestyleHint) {
RestyleBeforePseudo();
}
@ -2741,7 +2646,7 @@ ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
// Check whether we might need to create a new ::after frame.
// See comments above regarding :before.
if (!(mHintsHandled & nsChangeHint_ReconstructFrame) &&
mightReframePseudos) {
aChildRestyleHint) {
RestyleAfterPseudo(lastContinuation);
}
}
@ -2796,8 +2701,7 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
nsRestyleHint thisChildHint = aChildRestyleHint;
RestyleTracker::RestyleData undisplayedRestyleData;
Element* element = undisplayed->mContent->AsElement();
if (mRestyleTracker.GetRestyleData(element,
if (mRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
&undisplayedRestyleData)) {
thisChildHint =
nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
@ -2806,18 +2710,12 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
nsStyleSet* styleSet = mPresContext->StyleSet();
if (thisChildHint & (eRestyle_Self | eRestyle_Subtree)) {
undisplayedContext =
styleSet->ResolveStyleFor(element,
styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(),
mFrame->StyleContext(),
mTreeMatchContext);
} else if (thisChildHint ||
styleSet->IsInRuleTreeReconstruct()) {
// Use ResolveStyleWithReplacement either for actual
// replacements, or as a substitute for ReparentStyleContext
// that rebuilds the path in the rule tree rather than reusing
// the rule node, as we need to do during a rule tree
// reconstruct.
} else if (thisChildHint) {
undisplayedContext =
styleSet->ResolveStyleWithReplacement(element,
styleSet->ResolveStyleWithReplacement(undisplayed->mContent->AsElement(),
mFrame->StyleContext(),
undisplayed->mStyle,
thisChildHint);
@ -2825,7 +2723,7 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
undisplayedContext =
styleSet->ReparentStyleContext(undisplayed->mStyle,
mFrame->StyleContext(),
element);
undisplayed->mContent->AsElement());
}
const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {

View File

@ -164,26 +164,6 @@ public:
// whose updating is suppressed on the main thread (to save
// unnecessary work), while leaving all other aspects of style
// out-of-date.
//
// Performs an animation-only style flush to make styles from
// throttled transitions up-to-date prior to processing an unrelated
// style change, so that any transitions triggered by that style
// change produce correct results.
//
// In more detail: when we're able to run animations on the
// compositor, we sometimes "throttle" these animations by skipping
// updating style data on the main thread. However, whenever we
// process a normal (non-animation) style change, any changes in
// computed style on elements that have transition-* properties set
// may need to trigger new transitions; this process requires knowing
// both the old and new values of the property. To do this correctly,
// we need to have an up-to-date *old* value of the property on the
// 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 UpdateOnlyAnimationStyles();
bool ThrottledAnimationStyleIsUpToDate() const {
@ -199,8 +179,7 @@ public:
// Helper that does part of the work of RebuildAllStyleData, shared by
// RestyleElement for 'rem' handling.
void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint);
nsChangeHint aExtraHint);
// See PostRestyleEventCommon below.
void PostRestyleEvent(Element* aElement,

View File

@ -19,7 +19,6 @@
namespace mozilla {
class RestyleManager;
class ElementRestyler;
/**
* Helper class that collects a list of frames that need
@ -231,8 +230,6 @@ class RestyleTracker {
public:
typedef mozilla::dom::Element Element;
friend class ElementRestyler; // for AddPendingRestyleToTable
RestyleTracker(Element::FlagsType aRestyleBits) :
mRestyleBits(aRestyleBits),
mHaveLaterSiblingRestyles(false)
@ -264,7 +261,7 @@ public:
* if the element already had eRestyle_LaterSiblings set on it.
*/
bool AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint);
nsChangeHint aMinChangeHint);
/**
* Process the restyles we've been tracking.
@ -315,9 +312,6 @@ public:
};
private:
bool AddPendingRestyleToTable(Element* aElement, nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint);
/**
* Handle a single mPendingRestyles entry. aRestyleHint must not
* include eRestyle_LaterSiblings; that needs to be dealt with
@ -358,10 +352,9 @@ private:
bool mHaveLaterSiblingRestyles;
};
inline bool
RestyleTracker::AddPendingRestyleToTable(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
inline bool RestyleTracker::AddPendingRestyle(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
RestyleData existingData;
existingData.mRestyleHint = nsRestyleHint(0);
@ -384,17 +377,6 @@ RestyleTracker::AddPendingRestyleToTable(Element* aElement,
mPendingRestyles.Put(aElement, existingData);
return hadRestyleLaterSiblings;
}
inline bool
RestyleTracker::AddPendingRestyle(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
bool hadRestyleLaterSiblings =
AddPendingRestyleToTable(aElement, aRestyleHint, aMinChangeHint);
// We can only treat this element as a restyle root if we would
// actually restyle its descendants (so either call
// ReResolveStyleContext on it or just reframe it).

View File

@ -166,28 +166,6 @@ CommonAnimationManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
void
CommonAnimationManager::AddStyleUpdatesTo(RestyleTracker& aTracker)
{
PRCList* next = PR_LIST_HEAD(&mElementCollections);
while (next != &mElementCollections) {
ElementAnimationCollection* collection = static_cast<ElementAnimationCollection*>(next);
next = PR_NEXT_LINK(next);
if (!collection->IsForElement()) {
// We don't support compositor-driven animation of :before/:after
// transitions or animations, so at least skip those.
// FIXME: We'll need to handle this before using this for the
// transitions redesign.
continue;
}
nsRestyleHint rshint = collection->IsForTransitions()
? eRestyle_CSSTransitions : eRestyle_CSSAnimations;
aTracker.AddPendingRestyle(collection->mElement, rshint, nsChangeHint(0));
}
}
/* static */ bool
CommonAnimationManager::ExtractComputedValueForTransition(
nsCSSProperty aProperty,
@ -207,6 +185,90 @@ CommonAnimationManager::ExtractComputedValueForTransition(
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);
}
}
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();
nsStyleSet* styleSet = mPresContext->StyleSet();
nsRefPtr<nsStyleContext> newStyle =
styleSet->ResolveStyleWithReplacement(aElement, aParentStyle, oldStyle,
nsRestyleHint(eRestyle_CSSTransitions | eRestyle_CSSAnimations));
// 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, styleSet);
return newStyle;
}
NS_IMPL_ISUPPORTS(AnimValuesStyleRule, nsIStyleRule)
/* virtual */ void

View File

@ -34,7 +34,6 @@ class nsStyleChangeList;
namespace mozilla {
class RestyleTracker;
class StyleAnimationValue;
struct ElementPropertyTransition;
struct ElementAnimationCollection;
@ -68,11 +67,6 @@ public:
*/
void Disconnect();
// Tell the restyle tracker about all the styles that we're currently
// animating, so that it can update the animation rule for these
// elements.
void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker);
enum FlushFlags {
Can_Throttle,
Cannot_Throttle
@ -100,10 +94,82 @@ protected:
nsIAtom* aElementProperty,
nsCSSProperty aProperty);
// 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 mElementCollections;
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(&mElementCollections); \
while (next != &mElementCollections) { \
ElementAnimationCollection* collection = \
static_cast<ElementAnimationCollection*>(next); \
next = PR_NEXT_LINK(next); \
\
if (collection->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 = collection->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-StyleAnimationValue pairs.
*/
@ -390,6 +456,7 @@ struct ElementAnimationCollection : public PRCList
, mElementProperty(aElementProperty)
, mManager(aManager)
, mAnimationGeneration(0)
, mFlushGeneration(aNow)
, mNeedsRefreshes(true)
#ifdef DEBUG
, mCalledPropertyDtor(false)
@ -462,18 +529,6 @@ struct ElementAnimationCollection : public PRCList
mElementProperty == nsGkAtoms::transitionsProperty;
}
bool IsForTransitions() const {
return mElementProperty == nsGkAtoms::transitionsProperty ||
mElementProperty == nsGkAtoms::transitionsOfBeforeProperty ||
mElementProperty == nsGkAtoms::transitionsOfAfterProperty;
}
bool IsForAnimations() const {
return mElementProperty == nsGkAtoms::animationsProperty ||
mElementProperty == nsGkAtoms::animationsOfBeforeProperty ||
mElementProperty == nsGkAtoms::animationsOfAfterProperty;
}
nsString PseudoElement()
{
if (IsForElement()) {
@ -530,6 +585,11 @@ struct ElementAnimationCollection : public PRCList
// 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;
// False when we know that our current style rule is valid
// indefinitely into the future (because all of our animations are
// either completed or paused). May be invalidated by a style change.

View File

@ -775,3 +775,56 @@ nsAnimationManager::DoDispatchEvents()
}
}
}
void
nsAnimationManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
nsStyleContext* aParentStyle,
nsStyleChangeList& aChangeList)
{
dom::Element* element;
if (aContent->IsElement()) {
element = aContent->AsElement();
} else {
element = nullptr;
}
nsRefPtr<nsStyleContext> newStyle;
ElementAnimationCollection* collection;
if (element &&
(collection =
GetElementAnimations(element,
nsCSSPseudoElements::ePseudo_NotPseudoElement,
false))) {
// re-resolve our style
newStyle = UpdateThrottledStyle(element, aParentStyle, aChangeList);
// remove the current transition from the working set
collection->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(&mElementCollections)) {
// no throttled animations, leave early
return;
}
UpdateAllThrottledStylesInternal();
}

View File

@ -132,6 +132,9 @@ public:
nsCSSPseudoElements::Type aPseudoType,
bool aCreateIfNeeded);
// Updates styles on throttled animations. See note on nsTransitionManager
void UpdateAllThrottledStyles();
protected:
virtual void ElementCollectionRemoved() MOZ_OVERRIDE
{
@ -159,6 +162,14 @@ private:
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();

View File

@ -1345,9 +1345,6 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
// only the path from the last change in the rule tree, like
// ReplaceAnimationRule in nsStyleSet.cpp does. (That could then
// perhaps share this code, too?)
// But if we do that, we'll need to pass whether we are rebuilding the
// rule tree from ElementRestyler::RestyleSelf to avoid taking that
// path when we're rebuilding the rule tree.
nsTArray<RuleNodeInfo> rules;
for (nsRuleNode* ruleNode = aOldRuleNode; !ruleNode->IsRoot();

View File

@ -318,10 +318,6 @@ class nsStyleSet
// Note: EndReconstruct should not be called if BeginReconstruct fails
void EndReconstruct();
bool IsInRuleTreeReconstruct() const {
return mInReconstruct;
}
// Let the style set know that a particular sheet is the quirks sheet. This
// sheet must already have been added to the UA sheets. The pointer must not
// be null. This should only be called once for a given style set.

View File

@ -72,6 +72,58 @@ ElementPropertyTransition::CurrentValuePortion() const
* nsTransitionManager *
*****************************************************************************/
void
nsTransitionManager::UpdateThrottledStylesForSubtree(nsIContent* aContent,
nsStyleContext* aParentStyle,
nsStyleChangeList& aChangeList)
{
dom::Element* element;
if (aContent->IsElement()) {
element = aContent->AsElement();
} else {
element = nullptr;
}
nsRefPtr<nsStyleContext> newStyle;
ElementAnimationCollection* collection;
if (element &&
(collection =
GetElementTransitions(element,
nsCSSPseudoElements::ePseudo_NotPseudoElement,
false))) {
// re-resolve our style
newStyle = UpdateThrottledStyle(element, aParentStyle, aChangeList);
// remove the current transition from the working set
collection->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(nsTransitionManager,
GetElementTransitions)
void
nsTransitionManager::UpdateAllThrottledStyles()
{
if (PR_CLIST_IS_EMPTY(&mElementCollections)) {
// no throttled transitions, leave early
return;
}
UpdateAllThrottledStylesInternal();
}
void
nsTransitionManager::ElementCollectionRemoved()
{
@ -111,15 +163,6 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement,
aNewStyleContext->HasPseudoElementData(),
"pseudo type mismatch");
if (mInAnimationOnlyStyleUpdate) {
// If we're doing an animation-only style update, return, since the
// purpose of an animation-only style update is to update only the
// animation styles so that we don't consider style changes
// resulting from changes in the animation time for starting a
// transition.
return nullptr;
}
if (!mPresContext->IsDynamic()) {
// For print or print preview, ignore transitions.
return nullptr;

View File

@ -67,7 +67,6 @@ class nsTransitionManager MOZ_FINAL
public:
nsTransitionManager(nsPresContext *aPresContext)
: mozilla::css::CommonAnimationManager(aPresContext)
, mInAnimationOnlyStyleUpdate(false)
{
}
@ -118,10 +117,6 @@ public:
nsStyleContext *aOldStyleContext,
nsStyleContext *aNewStyleContext);
void SetInAnimationOnlyStyleUpdate(bool aInAnimationOnlyUpdate) {
mInAnimationOnlyStyleUpdate = aInAnimationOnlyUpdate;
}
// nsIStyleRuleProcessor (parts)
virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
@ -139,6 +134,27 @@ public:
void FlushTransitions(FlushFlags aFlags);
// Performs a 'mini-flush' to make styles from throttled transitions
// up-to-date prior to processing an unrelated style change, so that
// any transitions triggered by that style change produce correct
// results.
//
// In more detail: when we're able to run animations on the
// compositor, we sometimes "throttle" these animations by skipping
// updating style data on the main thread. However, whenever we
// process a normal (non-animation) style change, any changes in
// computed style on elements that have transition-* properties set
// may need to trigger new transitions; this process requires knowing
// both the old and new values of the property. To do this correctly,
// we need to have an up-to-date *old* value of the property on the
// 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();
ElementAnimationCollection* GetElementTransitions(
mozilla::dom::Element *aElement,
nsCSSPseudoElements::Type aPseudoType,
@ -161,8 +177,13 @@ private:
nsCSSPropertySet* aWhichStarted);
void WalkTransitionRule(ElementDependentRuleProcessorData* aData,
nsCSSPseudoElements::Type aPseudoType);
bool mInAnimationOnlyStyleUpdate;
// 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);
void UpdateAllThrottledStylesInternal();
};
#endif /* !defined(nsTransitionManager_h_) */