Bug 1334036 - Part 11: Trigger animation-only restyle when we handle an event with coordinates. r=birtles,heycam

We need to request an animation-only restyle to force flush all throttled
animations on main thread when we handle an event with coordinates
(e.g. mouse event).

MozReview-Commit-ID: KkjeQVsLgTl

--HG--
extra : rebase_source : 314408062e719e9f52df9a6726e2f3dad817bbef
This commit is contained in:
Boris Chiou 2017-05-19 16:16:41 +08:00
parent 731ab38439
commit 38215cedea
10 changed files with 125 additions and 32 deletions

View File

@ -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<bool> 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<PseudoElementHashEntry, bool>::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();

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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; }

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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<Element*>(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)
{

View File

@ -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();

View File

@ -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.