Bug 1362991: Compute at most one text style context per element. r?heycam
MozReview-Commit-ID: IDTRk47CsRS
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -135,30 +135,48 @@ ServoRestyleManager::ClearRestyleStateFr
aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
}
/**
* This struct takes care of encapsulating some common state that text nodes may
* need to track during the post-traversal.
*
* This is currently used to properly compute change hints when the parent
- * element of this node is a display: contents node.
+ * element of this node is a display: contents node, and also to avoid computing
+ * the style for text children more than once per element.
*/
struct ServoRestyleManager::TextPostTraversalState
{
+ nsStyleContext& mParentContext;
+ ServoStyleSet& mStyleSet;
+ RefPtr<nsStyleContext> mStyle;
bool mShouldPostHints;
bool mShouldComputeHints;
nsChangeHint mComputedHint;
- explicit TextPostTraversalState(bool aDisplayContentsParentStyleChanged)
- : mShouldPostHints(aDisplayContentsParentStyleChanged)
+ TextPostTraversalState(nsStyleContext& aParentContext,
+ ServoStyleSet& aStyleSet,
+ bool aDisplayContentsParentStyleChanged)
+ : mParentContext(aParentContext)
+ , mStyleSet(aStyleSet)
+ , mStyle(nullptr)
+ , mShouldPostHints(aDisplayContentsParentStyleChanged)
, mShouldComputeHints(aDisplayContentsParentStyleChanged)
, mComputedHint(nsChangeHint_Empty)
{}
+ nsStyleContext& ComputeStyle(nsIContent* aTextNode)
+ {
+ if (!mStyle) {
+ mStyle = mStyleSet.ResolveStyleForText(aTextNode, &mParentContext);
+ }
+ MOZ_ASSERT(mStyle);
+ return *mStyle;
+ }
+
void ComputeHintIfNeeded(nsIContent* aContent,
nsIFrame* aTextFrame,
nsStyleContext& aNewContext,
nsStyleChangeList& aChangeList)
{
MOZ_ASSERT(aTextFrame);
MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
@@ -169,18 +187,16 @@ struct ServoRestyleManager::TextPostTrav
nsStyleContext* oldContext = aTextFrame->StyleContext();
MOZ_ASSERT(oldContext->GetPseudo() == nsCSSAnonBoxes::mozText);
// We rely on the fact that all the text children for the same element share
// style to avoid recomputing style differences for all of them.
//
// TODO(emilio): The above may not be true for ::first-{line,letter}, but
// we'll cross that bridge when we support those in stylo.
- //
- // TODO(emilio): We could also use the same style context itself, can't we?
if (mShouldComputeHints) {
mShouldComputeHints = false;
uint32_t equalStructs, samePointerStructs;
mComputedHint =
oldContext->CalcStyleDifference(&aNewContext,
&equalStructs,
&samePointerStructs);
}
@@ -310,59 +326,55 @@ ServoRestyleManager::ProcessPostTraversa
const bool traverseElementChildren =
aElement->HasDirtyDescendantsForServo() || descendantsNeedFrames;
const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
if (traverseElementChildren || traverseTextChildren) {
nsStyleContext* upToDateContext =
recreateContext ? newContext : oldStyleContext;
StyleChildrenIterator it(aElement);
- TextPostTraversalState textState(displayContentsNode && recreateContext);
+ TextPostTraversalState textState(
+ *upToDateContext, *aStyleSet, displayContentsNode && recreateContext);
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
if (traverseElementChildren && n->IsElement()) {
ProcessPostTraversal(n->AsElement(), upToDateContext,
aStyleSet, aChangeList);
} else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
- ProcessPostTraversalForText(
- n, upToDateContext, aStyleSet, aChangeList, textState);
+ ProcessPostTraversalForText(n, aChangeList, textState);
}
}
}
aElement->UnsetHasDirtyDescendantsForServo();
aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
}
void
ServoRestyleManager::ProcessPostTraversalForText(
nsIContent* aTextNode,
- nsStyleContext* aParentContext,
- ServoStyleSet* aStyleSet,
nsStyleChangeList& aChangeList,
TextPostTraversalState& aPostTraversalState)
{
// Handle lazy frame construction.
if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
aChangeList.AppendChange(nullptr, aTextNode, nsChangeHint_ReconstructFrame);
return;
}
// Handle restyle.
nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
if (primaryFrame) {
RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
- RefPtr<nsStyleContext> newContext =
- aStyleSet->ResolveStyleForText(aTextNode, aParentContext);
-
+ nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
aPostTraversalState.ComputeHintIfNeeded(
- aTextNode, primaryFrame, *newContext, aChangeList);
+ aTextNode, primaryFrame, newContext, aChangeList);
for (nsIFrame* f = primaryFrame; f;
f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
- f->SetStyleContext(newContext);
+ f->SetStyleContext(&newContext);
}
}
}
void
ServoRestyleManager::ClearSnapshots()
{
for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -118,18 +118,16 @@ private:
*/
void ProcessPostTraversal(Element* aElement,
nsStyleContext* aParentContext,
ServoStyleSet* aStyleSet,
nsStyleChangeList& aChangeList);
struct TextPostTraversalState;
void ProcessPostTraversalForText(nsIContent* aTextNode,
- nsStyleContext* aParentContext,
- ServoStyleSet* aStyleSet,
nsStyleChangeList& aChangeList,
TextPostTraversalState& aState);
inline ServoStyleSet* StyleSet() const
{
MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
"ServoRestyleManager should only be used with a Servo-flavored "
"style backend");