Bug 996796 patch 20 - Make restyling exact - Avoid rerunning selector matching on everything when the basis of rem units changes. r=heycam

This commit is contained in:
L. David Baron 2014-08-02 19:37:47 -07:00
parent 065ed3abba
commit 995eeb66e6
4 changed files with 77 additions and 15 deletions

View File

@ -890,7 +890,7 @@ RestyleManager::RestyleElement(Element* aElement,
newContext->StyleFont()->mFont.size) { newContext->StyleFont()->mFont.size) {
// The basis for 'rem' units has changed. // The basis for 'rem' units has changed.
newContext = nullptr; newContext = nullptr;
DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0)); DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0), aRestyleHint);
if (aMinHint == 0) { if (aMinHint == 0) {
return; return;
} }
@ -1406,7 +1406,13 @@ RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
mPresContext->SetProcessingRestyles(true); mPresContext->SetProcessingRestyles(true);
DoRebuildAllStyleData(mPendingRestyles, aExtraHint); // 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);
mPresContext->SetProcessingRestyles(false); mPresContext->SetProcessingRestyles(false);
@ -1419,7 +1425,8 @@ RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint)
void void
RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker, RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
nsChangeHint aExtraHint) nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint)
{ {
// Tell the style set to get the old rule tree out of the way // Tell the style set to get the old rule tree out of the way
// so we can recalculate while maintaining rule tree immutability // so we can recalculate while maintaining rule tree immutability
@ -1428,6 +1435,19 @@ RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
return; 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 // Recalculate all of the style contexts for the document
// Note that we can ignore the return value of ComputeStyleChangeFor // Note that we can ignore the return value of ComputeStyleChangeFor
// because we never need to reframe the root frame // because we never need to reframe the root frame
@ -1436,11 +1456,12 @@ RestyleManager::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
// on us re-running rule matching here // on us re-running rule matching here
nsStyleChangeList changeList; nsStyleChangeList changeList;
// XXX Does it matter that we're passing aExtraHint to the real root // XXX Does it matter that we're passing aExtraHint to the real root
// frame and not the root node's primary frame? // frame and not the root node's primary frame? (We could do
// roughly what we do for aRestyleHint above.)
// Note: The restyle tracker we pass in here doesn't matter. // Note: The restyle tracker we pass in here doesn't matter.
ComputeStyleChangeFor(mPresContext->PresShell()->GetRootFrame(), ComputeStyleChangeFor(mPresContext->PresShell()->GetRootFrame(),
&changeList, aExtraHint, &changeList, aExtraHint,
aRestyleTracker, eRestyle_Subtree); aRestyleTracker, aRestyleHint);
// Process the required changes // Process the required changes
ProcessRestyledFrames(changeList); ProcessRestyledFrames(changeList);
FlushOverflowChangedTracker(); FlushOverflowChangedTracker();
@ -2340,7 +2361,16 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint)
"eRestyle_LaterSiblings must not be part of aRestyleHint"); "eRestyle_LaterSiblings must not be part of aRestyleHint");
nsRestyleHint hintToRestore = nsRestyleHint(0); nsRestyleHint hintToRestore = nsRestyleHint(0);
if (mContent && mContent->IsElement()) { 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)) {
mContent->OwnerDoc()->FlushPendingLinkUpdates(); mContent->OwnerDoc()->FlushPendingLinkUpdates();
RestyleTracker::RestyleData restyleData; RestyleTracker::RestyleData restyleData;
if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) { if (mRestyleTracker.GetRestyleData(mContent->AsElement(), &restyleData)) {
@ -2473,10 +2503,16 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
} }
else if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) { else if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType); Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
if (aRestyleHint == nsRestyleHint(0)) { if (aRestyleHint == nsRestyleHint(0) &&
!styleSet->IsInRuleTreeReconstruct()) {
newContext = newContext =
styleSet->ReparentStyleContext(oldContext, parentContext, element); styleSet->ReparentStyleContext(oldContext, parentContext, element);
} else { } 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 = newContext =
styleSet->ResolveStyleWithReplacement(element, parentContext, oldContext, styleSet->ResolveStyleWithReplacement(element, parentContext, oldContext,
aRestyleHint); aRestyleHint);
@ -2586,8 +2622,19 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint)
if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) { if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
Element* element = extraPseudoType != nsCSSPseudoElements::ePseudo_AnonBox Element* element = extraPseudoType != nsCSSPseudoElements::ePseudo_AnonBox
? mContent->AsElement() : nullptr; ? mContent->AsElement() : nullptr;
newExtraContext = if (styleSet->IsInRuleTreeReconstruct()) {
styleSet->ReparentStyleContext(oldExtraContext, newContext, element); // 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) { } else if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag, newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag,
newContext); newContext);
@ -2711,7 +2758,8 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
nsRestyleHint thisChildHint = aChildRestyleHint; nsRestyleHint thisChildHint = aChildRestyleHint;
RestyleTracker::RestyleData undisplayedRestyleData; RestyleTracker::RestyleData undisplayedRestyleData;
if (mRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(), Element* element = undisplayed->mContent->AsElement();
if (mRestyleTracker.GetRestyleData(element,
&undisplayedRestyleData)) { &undisplayedRestyleData)) {
thisChildHint = thisChildHint =
nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint); nsRestyleHint(thisChildHint | undisplayedRestyleData.mRestyleHint);
@ -2720,12 +2768,18 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
nsStyleSet* styleSet = mPresContext->StyleSet(); nsStyleSet* styleSet = mPresContext->StyleSet();
if (thisChildHint & (eRestyle_Self | eRestyle_Subtree)) { if (thisChildHint & (eRestyle_Self | eRestyle_Subtree)) {
undisplayedContext = undisplayedContext =
styleSet->ResolveStyleFor(undisplayed->mContent->AsElement(), styleSet->ResolveStyleFor(element,
mFrame->StyleContext(), mFrame->StyleContext(),
mTreeMatchContext); mTreeMatchContext);
} else if (thisChildHint) { } 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.
undisplayedContext = undisplayedContext =
styleSet->ResolveStyleWithReplacement(undisplayed->mContent->AsElement(), styleSet->ResolveStyleWithReplacement(element,
mFrame->StyleContext(), mFrame->StyleContext(),
undisplayed->mStyle, undisplayed->mStyle,
thisChildHint); thisChildHint);
@ -2733,7 +2787,7 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
undisplayedContext = undisplayedContext =
styleSet->ReparentStyleContext(undisplayed->mStyle, styleSet->ReparentStyleContext(undisplayed->mStyle,
mFrame->StyleContext(), mFrame->StyleContext(),
undisplayed->mContent->AsElement()); element);
} }
const nsStyleDisplay* display = undisplayedContext->StyleDisplay(); const nsStyleDisplay* display = undisplayedContext->StyleDisplay();
if (display->mDisplay != NS_STYLE_DISPLAY_NONE) { if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {

View File

@ -179,7 +179,8 @@ public:
// Helper that does part of the work of RebuildAllStyleData, shared by // Helper that does part of the work of RebuildAllStyleData, shared by
// RestyleElement for 'rem' handling. // RestyleElement for 'rem' handling.
void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker, void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
nsChangeHint aExtraHint); nsChangeHint aExtraHint,
nsRestyleHint aRestyleHint);
// See PostRestyleEventCommon below. // See PostRestyleEventCommon below.
void PostRestyleEvent(Element* aElement, void PostRestyleEvent(Element* aElement,

View File

@ -1345,6 +1345,9 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement,
// only the path from the last change in the rule tree, like // only the path from the last change in the rule tree, like
// ReplaceAnimationRule in nsStyleSet.cpp does. (That could then // ReplaceAnimationRule in nsStyleSet.cpp does. (That could then
// perhaps share this code, too?) // 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; nsTArray<RuleNodeInfo> rules;
for (nsRuleNode* ruleNode = aOldRuleNode; !ruleNode->IsRoot(); for (nsRuleNode* ruleNode = aOldRuleNode; !ruleNode->IsRoot();

View File

@ -318,6 +318,10 @@ class nsStyleSet
// Note: EndReconstruct should not be called if BeginReconstruct fails // Note: EndReconstruct should not be called if BeginReconstruct fails
void EndReconstruct(); void EndReconstruct();
bool IsInRuleTreeReconstruct() const {
return mInReconstruct;
}
// Let the style set know that a particular sheet is the quirks sheet. This // 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 // 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. // be null. This should only be called once for a given style set.