Make the 'noappend' selector optimizations only restyle later siblings, which is what they need to do. (Bug 534804) r=bzbarsky

This commit is contained in:
L. David Baron 2010-05-14 22:01:46 -07:00
parent 76df418748
commit 86bba93f79
3 changed files with 47 additions and 19 deletions

View File

@ -139,18 +139,19 @@ enum {
NODE_HAS_EDGE_CHILD_SELECTOR = 0x00008000U,
// A child of the node has a selector such that any insertion or
// removal of children requires restyling the parent (but append is
// OK). Additionally (in this manner it is stronger than
// NODE_HAS_SLOW_SELECTOR), if a child's style changes in any way
// (e.g., the child changes to or from matching :empty due to a
// grandchild changes), the node must also be restyled.
NODE_HAS_SLOW_SELECTOR_NOAPPEND
// removal of children requires restyling later siblings of that
// element. Additionally (in this manner it is stronger than
// NODE_HAS_SLOW_SELECTOR), if a child's style changes due to any
// other content tree changes (e.g., the child changes to or from
// matching :empty due to a grandchild insertion or removal), the
// child's later siblings must also be restyled.
NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS
= 0x00010000U,
NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR |
NODE_HAS_SLOW_SELECTOR |
NODE_HAS_EDGE_CHILD_SELECTOR |
NODE_HAS_SLOW_SELECTOR_NOAPPEND,
NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS,
NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR
= 0x00020000U,

View File

@ -11320,16 +11320,16 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame)
void
nsCSSFrameConstructor::RestyleForEmptyChange(Element* aContainer)
{
Element* toRestyle = aContainer;
// In some cases (:empty + E, :empty ~ E), a change if the content of
// an element requires restyling its grandparent, because it changes
// its parent's :empty state.
nsRestyleHint hint = eRestyle_Self;
nsIContent* grandparent = aContainer->GetParent();
if (grandparent &&
(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_NOAPPEND)) {
toRestyle = grandparent->AsElement();
(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
}
PostRestyleEvent(toRestyle, eRestyle_Self, NS_STYLE_HINT_NONE);
PostRestyleEvent(aContainer, hint, NS_STYLE_HINT_NONE);
}
void
@ -11347,7 +11347,7 @@ nsCSSFrameConstructor::RestyleForAppend(Element* aContainer,
#endif
PRUint32 selectorFlags =
aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
~NODE_HAS_SLOW_SELECTOR_NOAPPEND);
~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
if (selectorFlags == 0)
return;
@ -11391,6 +11391,25 @@ nsCSSFrameConstructor::RestyleForAppend(Element* aContainer,
}
}
// Needed since we can't use PostRestyleEvent on non-elements (with
// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Self |
// eRestyle_LaterSiblings) as appropriate).
static void
RestyleSiblingsStartingWith(nsCSSFrameConstructor *aFrameConstructor,
nsIContent *aStartingSibling /* may be null */)
{
for (nsIContent *sibling = aStartingSibling; sibling;
sibling = sibling->GetNextSibling()) {
if (sibling->IsElement()) {
aFrameConstructor->
PostRestyleEvent(sibling->AsElement(),
nsRestyleHint(eRestyle_Self | eRestyle_LaterSiblings),
NS_STYLE_HINT_NONE);
break;
}
}
}
// Restyling for a ContentInserted or CharacterDataChanged notification.
// This could be used for ContentRemoved as well if we got the
// notification before the removal happened (and sometimes
@ -11431,13 +11450,17 @@ nsCSSFrameConstructor::RestyleForInsertOrChange(Element* aContainer,
}
}
if (selectorFlags & (NODE_HAS_SLOW_SELECTOR |
NODE_HAS_SLOW_SELECTOR_NOAPPEND)) {
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
PostRestyleEvent(aContainer, eRestyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
// Restyle all later siblings.
RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
}
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
// restyle the previously-first element child if it is after this node
PRBool passedChild = PR_FALSE;
@ -11509,13 +11532,17 @@ nsCSSFrameConstructor::RestyleForRemove(Element* aContainer,
}
}
if (selectorFlags & (NODE_HAS_SLOW_SELECTOR |
NODE_HAS_SLOW_SELECTOR_NOAPPEND)) {
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
PostRestyleEvent(aContainer, eRestyle_Self, NS_STYLE_HINT_NONE);
// Restyling the container is the most we can do here, so we're done.
return;
}
if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
// Restyle all later siblings.
RestyleSiblingsStartingWith(this, aFollowingSibling);
}
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
// restyle the now-first element child if it was after aOldChild
PRBool reachedFollowingSibling = PR_FALSE;

View File

@ -1444,7 +1444,7 @@ nthChildGenericMatches(RuleProcessorData& data,
if (isFromEnd)
parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
else
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND);
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
}
const PRInt32 index = data.GetNthIndex(isOfType, isFromEnd, PR_FALSE);
@ -1481,7 +1481,7 @@ edgeOfTypeMatches(RuleProcessorData& data, TreeMatchContext& aTreeMatchContext,
if (checkLast)
parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
else
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND);
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
}
return (!checkFirst ||
@ -2124,7 +2124,7 @@ static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
nsIContent* parent = prevdata->mParentContent;
if (parent) {
if (aTreeMatchContext.mForStyling)
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND);
parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
PRInt32 index = parent->IndexOf(prevdata->mElement);
while (0 <= --index) {