Commit aa5a95a8 authored by Rune Lillesveen's avatar Rune Lillesveen Committed by Commit Bot

Make CSSSelector::SelectorText() non-recursive.

Should fix the stack overflow issue for selectors with an excessive
amount of compound selectors.

R=meade@chromium.org
BUG=719374

Change-Id: I2a1cfb8cb2d00d96f8d46a6e7317c5871020c6cd
Reviewed-on: https://chromium-review.googlesource.com/506020Reviewed-by: default avatarEddy Mead <meade@chromium.org>
Commit-Queue: Rune Lillesveen <rune@opera.com>
Cr-Commit-Position: refs/heads/master@{#473972}
parent edcbdf5a
...@@ -645,57 +645,57 @@ static void SerializeNamespacePrefixIfNeeded(const AtomicString& prefix, ...@@ -645,57 +645,57 @@ static void SerializeNamespacePrefixIfNeeded(const AtomicString& prefix,
builder.Append('|'); builder.Append('|');
} }
String CSSSelector::SelectorText(const String& right_side) const { const CSSSelector* CSSSelector::SerializeCompound(
StringBuilder str; StringBuilder& builder) const {
if (match_ == kTag && !tag_is_implicit_) { if (match_ == kTag && !tag_is_implicit_) {
SerializeNamespacePrefixIfNeeded(TagQName().Prefix(), str); SerializeNamespacePrefixIfNeeded(TagQName().Prefix(), builder);
SerializeIdentifierOrAny(TagQName().LocalName(), str); SerializeIdentifierOrAny(TagQName().LocalName(), builder);
} }
const CSSSelector* cs = this; for (const CSSSelector* simple_selector = this; simple_selector;
while (true) { simple_selector = simple_selector->TagHistory()) {
if (cs->match_ == kId) { if (simple_selector->match_ == kId) {
str.Append('#'); builder.Append('#');
SerializeIdentifier(cs->SerializingValue(), str); SerializeIdentifier(simple_selector->SerializingValue(), builder);
} else if (cs->match_ == kClass) { } else if (simple_selector->match_ == kClass) {
str.Append('.'); builder.Append('.');
SerializeIdentifier(cs->SerializingValue(), str); SerializeIdentifier(simple_selector->SerializingValue(), builder);
} else if (cs->match_ == kPseudoClass || cs->match_ == kPagePseudoClass) { } else if (simple_selector->match_ == kPseudoClass ||
str.Append(':'); simple_selector->match_ == kPagePseudoClass) {
str.Append(cs->SerializingValue()); builder.Append(':');
builder.Append(simple_selector->SerializingValue());
switch (cs->GetPseudoType()) {
switch (simple_selector->GetPseudoType()) {
case kPseudoNthChild: case kPseudoNthChild:
case kPseudoNthLastChild: case kPseudoNthLastChild:
case kPseudoNthOfType: case kPseudoNthOfType:
case kPseudoNthLastOfType: { case kPseudoNthLastOfType: {
str.Append('('); builder.Append('(');
// http://dev.w3.org/csswg/css-syntax/#serializing-anb // http://dev.w3.org/csswg/css-syntax/#serializing-anb
int a = cs->data_.rare_data_->NthAValue(); int a = simple_selector->data_.rare_data_->NthAValue();
int b = cs->data_.rare_data_->NthBValue(); int b = simple_selector->data_.rare_data_->NthBValue();
if (a == 0 && b == 0) if (a == 0 && b == 0)
str.Append('0'); builder.Append('0');
else if (a == 0) else if (a == 0)
str.Append(String::Number(b)); builder.Append(String::Number(b));
else if (b == 0) else if (b == 0)
str.Append(String::Format("%dn", a)); builder.Append(String::Format("%dn", a));
else if (b < 0) else if (b < 0)
str.Append(String::Format("%dn%d", a, b)); builder.Append(String::Format("%dn%d", a, b));
else else
str.Append(String::Format("%dn+%d", a, b)); builder.Append(String::Format("%dn+%d", a, b));
str.Append(')'); builder.Append(')');
break; break;
} }
case kPseudoLang: case kPseudoLang:
str.Append('('); builder.Append('(');
str.Append(cs->Argument()); builder.Append(simple_selector->Argument());
str.Append(')'); builder.Append(')');
break; break;
case kPseudoNot: case kPseudoNot:
DCHECK(cs->SelectorList()); DCHECK(simple_selector->SelectorList());
break; break;
case kPseudoHost: case kPseudoHost:
case kPseudoHostContext: case kPseudoHostContext:
...@@ -704,87 +704,106 @@ String CSSSelector::SelectorText(const String& right_side) const { ...@@ -704,87 +704,106 @@ String CSSSelector::SelectorText(const String& right_side) const {
default: default:
break; break;
} }
} else if (cs->match_ == kPseudoElement) { } else if (simple_selector->match_ == kPseudoElement) {
str.Append("::"); builder.Append("::");
str.Append(cs->SerializingValue()); builder.Append(simple_selector->SerializingValue());
} else if (cs->IsAttributeSelector()) { } else if (simple_selector->IsAttributeSelector()) {
str.Append('['); builder.Append('[');
SerializeNamespacePrefixIfNeeded(cs->Attribute().Prefix(), str); SerializeNamespacePrefixIfNeeded(simple_selector->Attribute().Prefix(),
SerializeIdentifier(cs->Attribute().LocalName(), str); builder);
switch (cs->match_) { SerializeIdentifier(simple_selector->Attribute().LocalName(), builder);
switch (simple_selector->match_) {
case kAttributeExact: case kAttributeExact:
str.Append('='); builder.Append('=');
break; break;
case kAttributeSet: case kAttributeSet:
// set has no operator or value, just the attrName // set has no operator or value, just the attrName
str.Append(']'); builder.Append(']');
break; break;
case kAttributeList: case kAttributeList:
str.Append("~="); builder.Append("~=");
break; break;
case kAttributeHyphen: case kAttributeHyphen:
str.Append("|="); builder.Append("|=");
break; break;
case kAttributeBegin: case kAttributeBegin:
str.Append("^="); builder.Append("^=");
break; break;
case kAttributeEnd: case kAttributeEnd:
str.Append("$="); builder.Append("$=");
break; break;
case kAttributeContain: case kAttributeContain:
str.Append("*="); builder.Append("*=");
break; break;
default: default:
break; break;
} }
if (cs->match_ != kAttributeSet) { if (simple_selector->match_ != kAttributeSet) {
SerializeString(cs->SerializingValue(), str); SerializeString(simple_selector->SerializingValue(), builder);
if (cs->AttributeMatch() == kCaseInsensitive) if (simple_selector->AttributeMatch() == kCaseInsensitive)
str.Append(" i"); builder.Append(" i");
str.Append(']'); builder.Append(']');
} }
} }
if (cs->SelectorList()) { if (simple_selector->SelectorList()) {
str.Append('('); builder.Append('(');
const CSSSelector* first_sub_selector = cs->SelectorList()->First(); const CSSSelector* first_sub_selector =
simple_selector->SelectorList()->First();
for (const CSSSelector* sub_selector = first_sub_selector; sub_selector; for (const CSSSelector* sub_selector = first_sub_selector; sub_selector;
sub_selector = CSSSelectorList::Next(*sub_selector)) { sub_selector = CSSSelectorList::Next(*sub_selector)) {
if (sub_selector != first_sub_selector) if (sub_selector != first_sub_selector)
str.Append(','); builder.Append(',');
str.Append(sub_selector->SelectorText()); builder.Append(sub_selector->SelectorText());
} }
str.Append(')'); builder.Append(')');
} }
if (cs->Relation() != kSubSelector || !cs->TagHistory()) if (simple_selector->Relation() != kSubSelector)
break; return simple_selector;
cs = cs->TagHistory();
} }
return nullptr;
}
if (const CSSSelector* tag_history = cs->TagHistory()) { String CSSSelector::SelectorText() const {
switch (cs->Relation()) { String result;
for (const CSSSelector* compound = this; compound;
compound = compound->TagHistory()) {
StringBuilder builder;
compound = compound->SerializeCompound(builder);
if (!compound)
return builder.ToString() + result;
DCHECK(compound->Relation() != kSubSelector);
switch (compound->Relation()) {
case kDescendant: case kDescendant:
return tag_history->SelectorText(" " + str.ToString() + right_side); result = " " + builder.ToString() + result;
break;
case kChild: case kChild:
return tag_history->SelectorText(" > " + str.ToString() + right_side); result = " > " + builder.ToString() + result;
break;
case kShadowDeep: case kShadowDeep:
return tag_history->SelectorText(" /deep/ " + str.ToString() + result = " /deep/ " + builder.ToString() + result;
right_side); break;
case kShadowPiercingDescendant: case kShadowPiercingDescendant:
return tag_history->SelectorText(" >>> " + str.ToString() + right_side); result = " >>> " + builder.ToString() + result;
break;
case kDirectAdjacent: case kDirectAdjacent:
return tag_history->SelectorText(" + " + str.ToString() + right_side); result = " + " + builder.ToString() + result;
break;
case kIndirectAdjacent: case kIndirectAdjacent:
return tag_history->SelectorText(" ~ " + str.ToString() + right_side); result = " ~ " + builder.ToString() + result;
break;
case kSubSelector: case kSubSelector:
NOTREACHED(); NOTREACHED();
case kShadowPseudo: case kShadowPseudo:
case kShadowSlot: case kShadowSlot:
return tag_history->SelectorText(str.ToString() + right_side); result = builder.ToString() + result;
break;
} }
} }
return str.ToString() + right_side; NOTREACHED();
return String();
} }
void CSSSelector::SetAttribute(const QualifiedName& value, void CSSSelector::SetAttribute(const QualifiedName& value,
......
...@@ -96,7 +96,7 @@ class CORE_EXPORT CSSSelector { ...@@ -96,7 +96,7 @@ class CORE_EXPORT CSSSelector {
~CSSSelector(); ~CSSSelector();
String SelectorText(const String& right_side = "") const; String SelectorText() const;
bool operator==(const CSSSelector&) const; bool operator==(const CSSSelector&) const;
...@@ -368,6 +368,7 @@ class CORE_EXPORT CSSSelector { ...@@ -368,6 +368,7 @@ class CORE_EXPORT CSSSelector {
unsigned SpecificityForOneSelector() const; unsigned SpecificityForOneSelector() const;
unsigned SpecificityForPage() const; unsigned SpecificityForPage() const;
const CSSSelector* SerializeCompound(StringBuilder&) const;
// Hide. // Hide.
CSSSelector& operator=(const CSSSelector&); CSSSelector& operator=(const CSSSelector&);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment