From 9c2b79104e4b2526e930426aca0719d31d5b19de Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 4 Sep 2015 10:00:14 +1000 Subject: [PATCH] Bug 1192302 - Part 2: Traverse the frame tree when processing eRestyle_SomeDescendants. r=bzbarsky --- layout/base/RestyleManager.cpp | 265 ++++++++++++++++++++++++++------- layout/base/RestyleManager.h | 24 ++- 2 files changed, 235 insertions(+), 54 deletions(-) diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index b3b4bf5e67b2..126d7810f870 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -2770,22 +2770,213 @@ private: * mSelectorsForDescendants. If the element does match one of the selectors, * we cause it to be restyled with eRestyle_Self. * - * We traverse down the flattened tree unless we find an element that - * (a) already has a pending restyle, or (b) does not have a pending restyle - * but does match one of the selectors in mSelectorsForDescendants. For (a), - * we add the current mSelectorsForDescendants into the existing restyle data, - * and for (b) we add a new pending restyle with that array. So in both - * cases, when we come to restyling this element back up in - * ProcessPendingRestyles, we will again find the eRestyle_SomeDescendants - * hint and its selectors array. + * We traverse down the frame tree (and through the flattened content tree + * when we find undisplayed content) unless we find an element that (a) already + * has a pending restyle, or (b) does not have a pending restyle but does match + * one of the selectors in mSelectorsForDescendants. For (a), we add the + * current mSelectorsForDescendants into the existing restyle data, and for (b) + * we add a new pending restyle with that array. So in both cases, when we + * come to restyling this element back up in ProcessPendingRestyles, we will + * again find the eRestyle_SomeDescendants hint and its selectors array. * * This ensures that we don't visit descendant elements and check them * against mSelectorsForDescendants more than once. */ void -ElementRestyler::AddPendingRestylesForDescendantsMatchingSelectors( - Element* aElement, +ElementRestyler::ConditionallyRestyleChildren() +{ + MOZ_ASSERT(mContent == mFrame->GetContent()); + + if (!mContent->IsElement() || mSelectorsForDescendants.IsEmpty()) { + return; + } + + Element* element = mContent->AsElement(); + + LOG_RESTYLE("traversing descendants of frame %s (with element %s) to " + "propagate eRestyle_SomeDescendants for these %d selectors:", + FrameTagToString(mFrame).get(), + ElementTagToString(element).get(), + int(mSelectorsForDescendants.Length())); + LOG_RESTYLE_INDENT(); +#ifdef RESTYLE_LOGGING + for (nsCSSSelector* sel : mSelectorsForDescendants) { + LOG_RESTYLE("%s", sel->RestrictedSelectorToString().get()); + } +#endif + + Element* restyleRoot = mRestyleTracker.FindClosestRestyleRoot(element); + ConditionallyRestyleChildren(mFrame, restyleRoot); +} + +void +ElementRestyler::ConditionallyRestyleChildren(nsIFrame* aFrame, + Element* aRestyleRoot) +{ + MOZ_ASSERT(aFrame->GetContent()); + MOZ_ASSERT(aFrame->GetContent()->IsElement()); + + ConditionallyRestyleUndisplayedDescendants(aFrame, aRestyleRoot); + ConditionallyRestyleContentChildren(aFrame, aRestyleRoot); +} + +// The structure of this method parallels RestyleContentChildren. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::ConditionallyRestyleContentChildren(nsIFrame* aFrame, + Element* aRestyleRoot) +{ + MOZ_ASSERT(aFrame->GetContent()); + MOZ_ASSERT(aFrame->GetContent()->IsElement()); + + if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aFrame->GetContent()->AsElement(); + } + + for (nsIFrame* f = aFrame; f; + f = GetNextContinuationWithSameStyle(f, f->StyleContext())) { + nsIFrame::ChildListIterator lists(f); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + nsIFrame* child = childFrames.get(); + // Out-of-flows are reached through their placeholders. Continuations + // and block-in-inline splits are reached through those chains. + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && + !GetPrevContinuationWithSameStyle(child)) { + // only do frames that are in flow + if (child->GetType() == nsGkAtoms::placeholderFrame) { // placeholder + // get out of flow frame and recur there + nsIFrame* outOfFlowFrame = + nsPlaceholderFrame::GetRealFrameForPlaceholder(child); + + // |nsFrame::GetParentStyleContext| checks being out + // of flow so that this works correctly. + do { + if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) { + continue; + } + if (!ConditionallyRestyle(outOfFlowFrame, aRestyleRoot)) { + ConditionallyRestyleChildren(outOfFlowFrame, aRestyleRoot); + } + } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation())); + } else { // regular child frame + if (child != mResolvedChild) { + if (!ConditionallyRestyle(child, aRestyleRoot)) { + ConditionallyRestyleChildren(child, aRestyleRoot); + } + } + } + } + } + } + } +} + +// The structure of this method parallels RestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::ConditionallyRestyleUndisplayedDescendants( + nsIFrame* aFrame, Element* aRestyleRoot) +{ + nsIContent* undisplayedParent; + if (MustCheckUndisplayedContent(aFrame, undisplayedParent)) { + DoConditionallyRestyleUndisplayedDescendants(undisplayedParent, + aRestyleRoot); + } +} + +// The structure of this method parallels DoRestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::DoConditionallyRestyleUndisplayedDescendants( + nsIContent* aParent, + Element* aRestyleRoot) +{ + nsCSSFrameConstructor* fc = mPresContext->FrameConstructor(); + UndisplayedNode* nodes = fc->GetAllUndisplayedContentIn(aParent); + ConditionallyRestyleUndisplayedNodes(nodes, aParent, + NS_STYLE_DISPLAY_NONE, aRestyleRoot); + nodes = fc->GetAllDisplayContentsIn(aParent); + ConditionallyRestyleUndisplayedNodes(nodes, aParent, + NS_STYLE_DISPLAY_CONTENTS, aRestyleRoot); +} + +// The structure of this method parallels RestyleUndisplayedNodes. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::ConditionallyRestyleUndisplayedNodes( + UndisplayedNode* aUndisplayed, + nsIContent* aUndisplayedParent, + const uint8_t aDisplay, + Element* aRestyleRoot) +{ + MOZ_ASSERT(aDisplay == NS_STYLE_DISPLAY_NONE || + aDisplay == NS_STYLE_DISPLAY_CONTENTS); + + if (!aUndisplayed) { + return; + } + + if (aUndisplayedParent && + aUndisplayedParent->IsElement() && + aUndisplayedParent->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aUndisplayedParent->AsElement(); + } + + for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed; + undisplayed = undisplayed->mNext) { + + if (!undisplayed->mContent->IsElement()) { + continue; + } + + Element* element = undisplayed->mContent->AsElement(); + + if (!ConditionallyRestyle(element, aRestyleRoot)) { + if (aDisplay == NS_STYLE_DISPLAY_NONE) { + ConditionallyRestyleContentDescendants(element, aRestyleRoot); + } else { // NS_STYLE_DISPLAY_CONTENTS + DoConditionallyRestyleUndisplayedDescendants(element, aRestyleRoot); + } + } + } +} + +void +ElementRestyler::ConditionallyRestyleContentDescendants(Element* aElement, + Element* aRestyleRoot) +{ + if (aElement->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aElement; + } + + FlattenedChildIterator it(aElement); + for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { + if (n->IsElement()) { + Element* e = n->AsElement(); + if (!ConditionallyRestyle(e, aRestyleRoot)) { + ConditionallyRestyleContentDescendants(e, aRestyleRoot); + } + } + } +} + +bool +ElementRestyler::ConditionallyRestyle(nsIFrame* aFrame, Element* aRestyleRoot) +{ + MOZ_ASSERT(aFrame->GetContent()); + + if (!aFrame->GetContent()->IsElement()) { + return true; + } + + return ConditionallyRestyle(aFrame->GetContent()->AsElement(), aRestyleRoot); +} + +bool +ElementRestyler::ConditionallyRestyle(Element* aElement, Element* aRestyleRoot) { LOG_RESTYLE("considering element %s for eRestyle_SomeDescendants", ElementTagToString(aElement).get()); @@ -2807,7 +2998,7 @@ ElementRestyler::AddPendingRestylesForDescendantsMatchingSelectors( data.mSelectorsForDescendants = mSelectorsForDescendants; mRestyleTracker.AddPendingRestyle(aElement, rshint, nsChangeHint(0), &data, Some(aRestyleRoot)); - return; + return true; } if (SelectorMatchesForRestyle(aElement)) { @@ -2818,46 +3009,10 @@ ElementRestyler::AddPendingRestylesForDescendantsMatchingSelectors( eRestyle_Self | eRestyle_SomeDescendants, nsChangeHint(0), &data, Some(aRestyleRoot)); - return; + return true; } - FlattenedChildIterator it(aElement); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - if (n->IsElement()) { - AddPendingRestylesForDescendantsMatchingSelectors(n->AsElement(), - aRestyleRoot); - } - } -} - -void -ElementRestyler::AddPendingRestylesForDescendantsMatchingSelectors( - nsIContent* aContent) -{ - if (!mContent->IsElement() || mSelectorsForDescendants.IsEmpty()) { - return; - } - - Element* element = mContent->AsElement(); - - LOG_RESTYLE("traversing descendants of element %s to propagate " - "eRestyle_SomeDescendants for these %d selectors:", - ElementTagToString(element).get(), - int(mSelectorsForDescendants.Length())); - LOG_RESTYLE_INDENT(); -#ifdef RESTYLE_LOGGING - for (nsCSSSelector* sel : mSelectorsForDescendants) { - LOG_RESTYLE("%s", sel->RestrictedSelectorToString().get()); - } -#endif - Element* restyleRoot = mRestyleTracker.FindClosestRestyleRoot(element); - FlattenedChildIterator it(element); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - if (n->IsElement()) { - AddPendingRestylesForDescendantsMatchingSelectors(n->AsElement(), - restyleRoot); - } - } + return false; } bool @@ -3174,7 +3329,7 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants); if (aRestyleHint & eRestyle_SomeDescendants) { - AddPendingRestylesForDescendantsMatchingSelectors(mContent); + ConditionallyRestyleChildren(); } return; } @@ -3206,7 +3361,7 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants); if (aRestyleHint & eRestyle_SomeDescendants) { - AddPendingRestylesForDescendantsMatchingSelectors(mContent); + ConditionallyRestyleChildren(); } return; } @@ -4246,6 +4401,8 @@ ElementRestyler::ComputeStyleChangeFor(nsIFrame* aFrame, } } +// The structure of this method parallels ConditionallyRestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. void ElementRestyler::RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint) { @@ -4256,6 +4413,8 @@ ElementRestyler::RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint) } } +// The structure of this method parallels DoConditionallyRestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. void ElementRestyler::DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint, nsIContent* aParent, @@ -4270,6 +4429,8 @@ ElementRestyler::DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint aParentContext, NS_STYLE_DISPLAY_CONTENTS); } +// The structure of this method parallels ConditionallyRestyleUndisplayedNodes. +// If you update this method, you probably want to update that one too. void ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, UndisplayedNode* aUndisplayed, @@ -4498,6 +4659,8 @@ ElementRestyler::InitializeAccessibilityNotifications(nsStyleContext* aNewContex #endif } +// The structure of this method parallels ConditionallyRestyleContentChildren. +// If you update this method, you probably want to update that one too. void ElementRestyler::RestyleContentChildren(nsIFrame* aParent, nsRestyleHint aChildRestyleHint) diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index fc4b6c50a0ed..72ac08b0bd27 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -758,9 +758,27 @@ private: eNotifyHidden }; - void AddPendingRestylesForDescendantsMatchingSelectors(Element* aElement, - Element* aRestyleRoot); - void AddPendingRestylesForDescendantsMatchingSelectors(nsIContent* aContent); + // These methods handle the eRestyle_SomeDescendants hint by traversing + // down the frame tree (and then when reaching undisplayed content, + // the flattened content tree) find elements that match a selector + // in mSelectorsForDescendants and call AddPendingRestyle for them. + void ConditionallyRestyleChildren(); + void ConditionallyRestyleChildren(nsIFrame* aFrame, + Element* aRestyleRoot); + void ConditionallyRestyleContentChildren(nsIFrame* aFrame, + Element* aRestyleRoot); + void ConditionallyRestyleUndisplayedDescendants(nsIFrame* aFrame, + Element* aRestyleRoot); + void DoConditionallyRestyleUndisplayedDescendants(nsIContent* aParent, + Element* aRestyleRoot); + void ConditionallyRestyleUndisplayedNodes(UndisplayedNode* aUndisplayed, + nsIContent* aUndisplayedParent, + const uint8_t aDisplay, + Element* aRestyleRoot); + void ConditionallyRestyleContentDescendants(Element* aElement, + Element* aRestyleRoot); + bool ConditionallyRestyle(nsIFrame* aFrame, Element* aRestyleRoot); + bool ConditionallyRestyle(Element* aElement, Element* aRestyleRoot); #ifdef RESTYLE_LOGGING int32_t& LoggingDepth() { return mLoggingDepth; }