diff --git a/dom/animation/EffectCompositor.cpp b/dom/animation/EffectCompositor.cpp index 15c0363ab628..2e1b118dd5e3 100644 --- a/dom/animation/EffectCompositor.cpp +++ b/dom/animation/EffectCompositor.cpp @@ -957,13 +957,14 @@ EffectCompositor::SetPerformanceWarning( } bool -EffectCompositor::PreTraverse() +EffectCompositor::PreTraverse(AnimationRestyleType aRestyleType) { - return PreTraverseInSubtree(nullptr); + return PreTraverseInSubtree(nullptr, aRestyleType); } bool -EffectCompositor::PreTraverseInSubtree(Element* aRoot) +EffectCompositor::PreTraverseInSubtree(Element* aRoot, + AnimationRestyleType aRestyleType) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mPresContext->RestyleManager()->IsServo()); @@ -971,9 +972,13 @@ EffectCompositor::PreTraverseInSubtree(Element* aRoot) AutoRestore guard(mIsInPreTraverse); mIsInPreTraverse = true; - // We need to force flush all throttled animations if there are any - // non-animation restyles. - bool flushThrottledRestyles = aRoot && aRoot->HasDirtyDescendantsForServo(); + // We need to force flush all throttled animations if we also have + // non-animation restyles (since we'll want the up-to-date animation style + // when we go to process them so we can trigger transitions correctly), and + // if we are currently flushing all throttled animation restyles. + bool flushThrottledRestyles = + (aRoot && aRoot->HasDirtyDescendantsForServo()) || + aRestyleType == AnimationRestyleType::Full; using ElementsToRestyleIterType = nsDataHashtable::Iterator; @@ -1092,8 +1097,9 @@ EffectCompositor::PreTraverse(dom::Element* aElement, PseudoElementHashEntry::KeyType key = { aElement, aPseudoType }; - // We need to flush all throttled animation restyles too if there are - // any non-animation restyles. + // We need to flush all throttled animation restyles too if we also have + // non-animation restyles (since we'll want the up-to-date animation style + // when we go to process them so we can trigger transitions correctly). Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType); bool flushThrottledRestyles = elementToRestyle && elementToRestyle->HasDirtyDescendantsForServo(); diff --git a/dom/animation/EffectCompositor.h b/dom/animation/EffectCompositor.h index a26c795d0c6a..816b7c2fcb68 100644 --- a/dom/animation/EffectCompositor.h +++ b/dom/animation/EffectCompositor.h @@ -227,18 +227,27 @@ public: nsCSSPropertyID aProperty, const AnimationPerformanceWarning& aWarning); + // The type which represents what kind of animation restyle we want. + enum class AnimationRestyleType { + Throttled, // Restyle elements that have posted animation restyles. + Full // Restyle all elements with animations (i.e. even if the + // animations are throttled). + }; + // Do a bunch of stuff that we should avoid doing during the parallel // traversal (e.g. changing member variables) for all elements that we expect // to restyle on the next traversal. + // // Returns true if there are elements needing a restyle for animation. - bool PreTraverse(); + bool PreTraverse(AnimationRestyleType aRestyleType); // Similar to the above but only for the (pseudo-)element. bool PreTraverse(dom::Element* aElement, CSSPseudoElementType aPseudoType); // Similar to the above but for all elements in the subtree rooted // at aElement. - bool PreTraverseInSubtree(dom::Element* aElement); + bool PreTraverseInSubtree(dom::Element* aElement, + AnimationRestyleType aRestyleType); private: ~EffectCompositor() = default; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 351891689995..52f9a596f10c 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -6946,10 +6946,7 @@ FlushThrottledStyles(nsIDocument *aDocument, void *aData) if (shell && shell->IsVisible()) { nsPresContext* presContext = shell->GetPresContext(); if (presContext) { - if (presContext->RestyleManager()->IsGecko()) { - // XXX stylo: ServoRestyleManager doesn't support animations yet. - presContext->RestyleManager()->AsGecko()->UpdateOnlyAnimationStyles(); - } + presContext->RestyleManager()->UpdateOnlyAnimationStyles(); } } diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index d77ce267eef7..196c9451e86c 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -180,6 +180,8 @@ public: const nsAttrValue* aOldValue); inline nsresult ReparentStyleContext(nsIFrame* aFrame); + inline void UpdateOnlyAnimationStyles(); + // Get a counter that increments on every style change, that we use to // track whether off-main-thread animations are up-to-date. uint64_t GetAnimationGeneration() const { return mAnimationGeneration; } diff --git a/layout/base/RestyleManagerInlines.h b/layout/base/RestyleManagerInlines.h index 0c6aabe824c1..6e2e70e58469 100644 --- a/layout/base/RestyleManagerInlines.h +++ b/layout/base/RestyleManagerInlines.h @@ -79,6 +79,12 @@ RestyleManager::ReparentStyleContext(nsIFrame* aFrame) MOZ_STYLO_FORWARD(ReparentStyleContext, (aFrame)); } +void +RestyleManager::UpdateOnlyAnimationStyles() +{ + MOZ_STYLO_FORWARD(UpdateOnlyAnimationStyles, ()); +} + } // namespace mozilla #endif // mozilla_RestyleManagerInlines_h diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 0289b96e9167..8ff1dc181529 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -514,7 +514,8 @@ ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent, } void -ServoRestyleManager::ProcessPendingRestyles() +ServoRestyleManager::DoProcessPendingRestyles(TraversalRestyleBehavior + aRestyleBehavior) { MOZ_ASSERT(PresContext()->Document(), "No document? Pshaw!"); MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!"); @@ -536,6 +537,8 @@ ServoRestyleManager::ProcessPendingRestyles() ServoStyleSet* styleSet = StyleSet(); nsIDocument* doc = PresContext()->Document(); + bool animationOnly = aRestyleBehavior == + TraversalRestyleBehavior::ForAnimationOnly; // Ensure the refresh driver is active during traversal to avoid mutating // mActiveTimer and mMostRecentRefresh time. @@ -546,12 +549,15 @@ ServoRestyleManager::ProcessPendingRestyles() // in a loop because certain rare paths in the frame constructor (like // uninstalling XBL bindings) can trigger additional style validations. mInStyleRefresh = true; - if (mHaveNonAnimationRestyles) { + if (mHaveNonAnimationRestyles && !animationOnly) { ++mAnimationGeneration; } - while (styleSet->StyleDocument()) { - ClearSnapshots(); + while (animationOnly ? styleSet->StyleDocumentForAnimationOnly() + : styleSet->StyleDocument()) { + if (!animationOnly) { + ClearSnapshots(); + } // Recreate style contexts, and queue up change hints (which also handle // lazy frame construction). @@ -582,12 +588,14 @@ ServoRestyleManager::ProcessPendingRestyles() IncrementRestyleGeneration(); } - ClearSnapshots(); FlushOverflowChangedTracker(); - mHaveNonAnimationRestyles = false; + if (!animationOnly) { + ClearSnapshots(); + styleSet->AssertTreeIsClean(); + mHaveNonAnimationRestyles = false; + } mInStyleRefresh = false; - styleSet->AssertTreeIsClean(); // Note: We are in the scope of |animationsWithDestroyedFrame|, so // |mAnimationsWithDestroyedFrame| is still valid. @@ -595,6 +603,24 @@ ServoRestyleManager::ProcessPendingRestyles() mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames(); } +void +ServoRestyleManager::ProcessPendingRestyles() +{ + DoProcessPendingRestyles(TraversalRestyleBehavior::Normal); +} + +void +ServoRestyleManager::UpdateOnlyAnimationStyles() +{ + // Bug 1365855: We also need to implement this for SMIL. + bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates(); + if (!doCSS) { + return; + } + + DoProcessPendingRestyles(TraversalRestyleBehavior::ForAnimationOnly); +} + void ServoRestyleManager::RestyleForInsertOrChange(nsINode* aContainer, nsIContent* aChild) diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index f1dd644659e0..acd3fb02be0f 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -50,6 +50,8 @@ public: nsRestyleHint aRestyleHint); void ProcessPendingRestyles(); + void UpdateOnlyAnimationStyles(); + void ContentInserted(nsINode* aContainer, nsIContent* aChild); void ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent); @@ -138,6 +140,8 @@ private: void ClearSnapshots(); ServoElementSnapshot& SnapshotFor(mozilla::dom::Element* aElement); + void DoProcessPendingRestyles(TraversalRestyleBehavior aRestyleBehavior); + // We use a separate data structure from nsStyleChangeList because we need a // frame to create nsStyleChangeList entries, and the primary frame may not be // attached yet. diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 49db17121e81..62817ffbe6a7 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -270,7 +270,8 @@ ServoStyleSet::PreTraverseSync() } void -ServoStyleSet::PreTraverse(Element* aRoot) +ServoStyleSet::PreTraverse(Element* aRoot, + EffectCompositor::AnimationRestyleType aRestyleType) { PreTraverseSync(); @@ -279,12 +280,13 @@ ServoStyleSet::PreTraverse(Element* aRoot) nsSMILAnimationController* smilController = mPresContext->Document()->GetAnimationController(); if (aRoot) { - mPresContext->EffectCompositor()->PreTraverseInSubtree(aRoot); + mPresContext->EffectCompositor() + ->PreTraverseInSubtree(aRoot, aRestyleType); if (smilController) { smilController->PreTraverseInSubtree(aRoot); } } else { - mPresContext->EffectCompositor()->PreTraverse(); + mPresContext->EffectCompositor()->PreTraverse(aRestyleType); if (smilController) { smilController->PreTraverse(); } @@ -310,10 +312,18 @@ ServoStyleSet::PrepareAndTraverseSubtree( bool isInitial = !aRoot->HasServoData(); bool forReconstruct = aRestyleBehavior == TraversalRestyleBehavior::ForReconstruct; + bool forAnimationOnly = + aRestyleBehavior == TraversalRestyleBehavior::ForAnimationOnly; bool postTraversalRequired = Servo_TraverseSubtree( aRoot, mRawSet.get(), &snapshots, aRootBehavior, aRestyleBehavior); MOZ_ASSERT_IF(isInitial || forReconstruct, !postTraversalRequired); + // Don't need to trigger a second traversal if this restyle only needs + // animation-only restyle. + if (forAnimationOnly) { + return postTraversalRequired; + } + auto root = const_cast(aRoot); // If there are still animation restyles needed, trigger a second traversal to @@ -324,8 +334,10 @@ ServoStyleSet::PrepareAndTraverseSubtree( // traversal caused, for example, the font-size to change, the SMIL style // won't be updated until the next tick anyway. EffectCompositor* compositor = mPresContext->EffectCompositor(); - if (forReconstruct ? compositor->PreTraverseInSubtree(root) - : compositor->PreTraverse()) { + EffectCompositor::AnimationRestyleType restyleType = + EffectCompositor::AnimationRestyleType::Throttled; + if (forReconstruct ? compositor->PreTraverseInSubtree(root, restyleType) + : compositor->PreTraverse(restyleType)) { if (Servo_TraverseSubtree( aRoot, mRawSet.get(), &snapshots, aRootBehavior, aRestyleBehavior)) { MOZ_ASSERT(!forReconstruct); @@ -851,6 +863,23 @@ ServoStyleSet::StyleDocument() return postTraversalRequired; } +bool +ServoStyleSet::StyleDocumentForAnimationOnly() +{ + PreTraverse(nullptr, EffectCompositor::AnimationRestyleType::Full); + + bool postTraversalRequired = false; + DocumentStyleRootIterator iter(mPresContext->Document()); + while (Element* root = iter.GetNextStyleRoot()) { + if (PrepareAndTraverseSubtree(root, + TraversalRootBehavior::Normal, + TraversalRestyleBehavior::ForAnimationOnly)) { + postTraversalRequired = true; + } + } + return postTraversalRequired; +} + void ServoStyleSet::StyleNewSubtree(Element* aRoot) { diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index 047caf4d6d0f..f32b2648a09f 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -7,6 +7,7 @@ #ifndef mozilla_ServoStyleSet_h #define mozilla_ServoStyleSet_h +#include "mozilla/EffectCompositor.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/EventStates.h" #include "mozilla/PostTraversalTask.h" @@ -245,6 +246,15 @@ public: */ bool StyleDocument(); + /** + * Performs a Servo animation-only traversal to compute style for all nodes + * with the animation-only dirty bit in the document. + * + * This will traverse all of the document's style roots (that is, its document + * element, and the roots of the document-level native anonymous content). + */ + bool StyleDocumentForAnimationOnly(); + /** * Eagerly styles a subtree of unstyled nodes that was just appended to the * tree. This is used in situations where we need the style immediately and @@ -422,7 +432,9 @@ private: * When aRoot is null, the entire document is pre-traversed. Otherwise, * only the subtree rooted at aRoot is pre-traversed. */ - void PreTraverse(dom::Element* aRoot = nullptr); + void PreTraverse(dom::Element* aRoot = nullptr, + EffectCompositor::AnimationRestyleType = + EffectCompositor::AnimationRestyleType::Throttled); // Subset of the pre-traverse steps that involve syncing up data void PreTraverseSync(); diff --git a/layout/style/ServoTypes.h b/layout/style/ServoTypes.h index b09eadf2985f..48367e8115f2 100644 --- a/layout/style/ServoTypes.h +++ b/layout/style/ServoTypes.h @@ -53,14 +53,16 @@ enum class TraversalRootBehavior { UnstyledChildrenOnly, }; -// Indicates whether the Servo style system should perform normal processing or -// whether it should traverse in a mode that doesn't generate any change hints, -// which is what's required when handling frame reconstruction. The change -// hints in this case are unneeded, since the old frames have already been -// destroyed. +// Indicates whether the Servo style system should perform normal processing, +// animation-only processing (so we can flush any throttled animation styles), +// or whether it should traverse in a mode that doesn't generate any change +// hints, which is what's required when handling frame reconstruction. +// The change hints in this case are unneeded, since the old frames have +// already been destroyed. enum class TraversalRestyleBehavior { Normal, ForReconstruct, + ForAnimationOnly, }; // Represents which tasks are performed in a SequentialTask of UpdateAnimations.