Stop using potentially-deep recursion to serialize selectors. (Bug 475215) r+sr=bzbarsky

This commit is contained in:
L. David Baron 2009-02-19 07:29:28 -08:00
parent c584b934a5
commit 18d6af2e5e
2 changed files with 53 additions and 41 deletions

View File

@ -75,6 +75,7 @@
#include "nsIPrincipal.h"
#include "nsComponentManagerUtils.h"
#include "nsCSSPseudoClasses.h"
#include "nsTArray.h"
#include "nsContentUtils.h"
#include "nsContentErrors.h"
@ -468,11 +469,6 @@ static PRBool IsPseudoElement(nsIAtom* aAtom)
return PR_FALSE;
}
void nsCSSSelector::AppendNegationToString(nsAString& aString)
{
aString.AppendLiteral(":not(");
}
//
// Builds the textual representation of a selector. Called by DOM 2 CSS
// StyleRule:selectorText
@ -483,28 +479,58 @@ nsCSSSelector::ToString(nsAString& aString, nsICSSStyleSheet* aSheet,
{
if (!aAppend)
aString.Truncate();
// selectors are linked from right-to-left, so the next selector in
// the linked list actually precedes this one in the resulting string
nsAutoTArray<const nsCSSSelector*, 8> stack;
for (const nsCSSSelector *s = this; s; s = s->mNext) {
stack.AppendElement(s);
}
ToStringInternal(aString, aSheet, IsPseudoElement(mTag), PR_FALSE);
while (!stack.IsEmpty()) {
PRUint32 index = stack.Length() - 1;
const nsCSSSelector *s = stack.ElementAt(index);
stack.RemoveElementAt(index);
s->AppendToStringWithoutCombinators(aString, aSheet);
// Append the combinator, if needed.
if (!stack.IsEmpty()) {
const nsCSSSelector *next = stack.ElementAt(index - 1);
if (!IsPseudoElement(next->mTag)) {
aString.Append(PRUnichar(' '));
PRUnichar oper = s->mOperator;
if (oper != PRUnichar(0)) {
aString.Append(oper);
aString.Append(PRUnichar(' '));
}
}
}
}
}
void nsCSSSelector::ToStringInternal(nsAString& aString,
nsICSSStyleSheet* aSheet,
PRBool aIsPseudoElem,
PRBool aIsNegated) const
void
nsCSSSelector::AppendToStringWithoutCombinators
(nsAString& aString, nsICSSStyleSheet* aSheet) const
{
AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, PR_FALSE);
for (const nsCSSSelector* negation = mNegations; negation;
negation = negation->mNegations) {
aString.AppendLiteral(":not(");
negation->AppendToStringWithoutCombinatorsOrNegations(aString, aSheet,
PR_TRUE);
aString.Append(PRUnichar(')'));
}
}
void
nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
(nsAString& aString, nsICSSStyleSheet* aSheet,
PRBool aIsNegated) const
{
nsAutoString temp;
PRBool isPseudoElement = IsPseudoElement(mTag);
// selectors are linked from right-to-left, so the next selector in the linked list
// actually precedes this one in the resulting string
if (mNext) {
mNext->ToStringInternal(aString, aSheet, IsPseudoElement(mTag), 0);
if (!aIsNegated && !isPseudoElement) {
// don't add a leading whitespace if we have a pseudo-element
// or a negated simple selector
aString.Append(PRUnichar(' '));
}
}
// For non-pseudo-element selectors or for lone pseudo-elements, deal with
// namespace prefixes.
@ -697,22 +723,6 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
list = list->mNext;
}
}
if (!aIsNegated) {
for (nsCSSSelector* negation = mNegations; negation;
negation = negation->mNegations) {
aString.AppendLiteral(":not(");
negation->ToStringInternal(aString, aSheet, PR_FALSE, PR_TRUE);
aString.Append(PRUnichar(')'));
}
}
// Append the operator only if the selector is not negated and is not
// a pseudo-element
if (!aIsNegated && mOperator && !aIsPseudoElem) {
aString.Append(PRUnichar(' '));
aString.Append(mOperator);
}
}
PRBool

View File

@ -173,10 +173,12 @@ private:
void AddPseudoClassInternal(nsPseudoClassList *aPseudoClass);
nsCSSSelector* Clone(PRBool aDeepNext, PRBool aDeepNegations) const;
void AppendNegationToString(nsAString& aString);
void ToStringInternal(nsAString& aString, nsICSSStyleSheet* aSheet,
PRBool aIsPseudoElem,
PRBool aIsNegated) const;
void AppendToStringWithoutCombinators(nsAString& aString,
nsICSSStyleSheet* aSheet) const;
void AppendToStringWithoutCombinatorsOrNegations(nsAString& aString,
nsICSSStyleSheet* aSheet,
PRBool aIsNegated)
const;
// Returns true if this selector can have a namespace specified (which
// happens if and only if the default namespace would apply to this
// selector).