From a39da5881cbbf9421d46a39019bb216f4ced44dd Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 3 Aug 2014 01:22:07 -0700 Subject: [PATCH] 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 . --- layout/base/RestyleManager.cpp | 170 ++++++--------------------- layout/base/RestyleManager.h | 23 +--- layout/base/RestyleTracker.h | 26 +--- layout/style/AnimationCommon.cpp | 106 +++++++++++++---- layout/style/AnimationCommon.h | 96 ++++++++++++--- layout/style/nsAnimationManager.cpp | 53 +++++++++ layout/style/nsAnimationManager.h | 11 ++ layout/style/nsStyleSet.cpp | 3 - layout/style/nsStyleSet.h | 4 - layout/style/nsTransitionManager.cpp | 61 ++++++++-- layout/style/nsTransitionManager.h | 35 ++++-- 11 files changed, 345 insertions(+), 243 deletions(-) diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 1eb2cc9883b6..71d9c3c71cd2 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -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) { diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index 101ee53fba08..f11adbad938b 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -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, diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index 04f204513198..eb2735e4da62 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -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). diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp index dac45c809aaf..cb88bc63d3be 100644 --- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -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(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 +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 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 beforeStyle = + aStyleSet->ReparentStyleContext(before->StyleContext(), + aNewStyle, aElement); + before->SetStyleContext(beforeStyle); + } + if (nsIFrame* after = nsLayoutUtils::GetBeforeFrame(aPrimaryFrame)) { + nsRefPtr 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 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 diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h index 6df2bac2786f..08f4fe596d73 100644 --- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -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 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(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 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. diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index a1912d23c681..09b68026bfb4 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -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 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(); +} + diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index 28fc23559c53..5a21b9095a2f 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -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(); diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 733c74f69c6f..4f9409077766 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -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 rules; for (nsRuleNode* ruleNode = aOldRuleNode; !ruleNode->IsRoot(); diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index 33561970975b..44cc98a725b6 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -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. diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 15e40fe305a0..7eabc311b826 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -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 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; diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h index 4f8b3bb68e88..d66d4c9aa7d7 100644 --- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -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_) */