Bug 1192302 - Part 2: Traverse the frame tree when processing eRestyle_SomeDescendants. r=bzbarsky

This commit is contained in:
Cameron McCormack 2015-09-04 10:00:14 +10:00
Родитель 5a7fd0fade
Коммит 9c2b79104e
2 изменённых файлов: 235 добавлений и 54 удалений

Просмотреть файл

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

Просмотреть файл

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