Bug 528096: unexpected-token error recovery paths in the CSS parser need to UngetToken() before calling SkipUntil(), for correct behavior when the unexpected token is ( [ { or FUNCTION. r=dbaron

This commit is contained in:
Zack Weinberg 2010-01-27 16:20:05 -08:00
parent 3ad3950083
commit 1d78bfa0d0
8 changed files with 200 additions and 72 deletions

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<!-- Test for some pieces of bug 528096; other pieces are tested in
the mochitests test_font_face_parser.html,
test_media_queries.html, and test_selectors.html -->
<html><head>
<title>Test for bug 528096 (counters and image rects)</title>
<style>
div { display: inline-block; vertical-align: bottom;
margin-right: 6px; width: 16px; height: 16px;
background-color: lime; }
div::before { display: inline-block; height: 16px;
content: url("checkmark.gif"); }
</style>
</head><body>
<table><tr>
<td>-moz-image-rect()</td>
<td><div id="i1"></td>
</tr><tr>
<td>counter()</td>
<td><div id="c1"></div><div id="c2"></div></td>
</tr><tr>
<td>counters()</td>
<td><div id="cs1"></div><div id="cs2"></div><div id="cs3"></div></td>
</tr></table>
<p>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=528096">Mozilla
bug 528096</a>. There should be no red or orange, and each square
should have a black checkmark in it.
</p>
</body></html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE HTML>
<!-- Test for some pieces of bug 528096; other pieces are tested in
the mochitests test_font_face_parser.html,
test_media_queries.html, and test_selectors.html -->
<html><head>
<title>Test for bug 528096 (counters and image rects)</title>
<style>
div { display: inline-block; vertical-align: bottom;
margin-right: 6px; width: 16px; height: 16px;
background-color: orange; }
div::before { display: inline-block; height: 16px; }
/* -moz-image-rect() */
#i1::before {
background-color: lime;
background-image: -moz-image-rect((); background-color: red;);
content: url("checkmark.gif");
}
/* counter() */
#c1::before {
background-color: lime;
content: counter((); background-color: red;);
content: url("checkmark.gif");
}
#c2::before {
background-color: lime;
content: counter(foo,(); background-color: red;);
content: url("checkmark.gif");
}
/* counters() */
#cs1::before {
background-color: lime;
content: counters((); background-color: red;);
content: url("checkmark.gif");
}
#cs2::before {
background-color: lime;
content: counters(foo,(); background-color: red;);
content: url("checkmark.gif");
}
#cs3::before {
background-color: lime;
content: counters(foo,"s",(); background-color: red;);
content: url("checkmark.gif");
}
</style>
</head><body>
<table><tr>
<td>-moz-image-rect()</td>
<td><div id="i1"></td>
</tr><tr>
<td>counter()</td>
<td><div id="c1"></div><div id="c2"></div></td>
</tr><tr>
<td>counters()</td>
<td><div id="cs1"></div><div id="cs2"></div><div id="cs3"></div></td>
</tr></table>
<p>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=528096">Mozilla
bug 528096</a>. There should be no red or orange, and each square
should have a black checkmark in it.
</p>
</body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

View File

@ -1369,6 +1369,7 @@ fails HTTP(..) == 518172-2b.html 518172-b-ref.html # bug 518172
== 528038-1e.html 528038-1-ref.html
== 528038-1f.html 528038-1-ref.html
== 528038-2.html 528038-2-ref.html
== 528096-1.html 528096-1-ref.html
== 530686-1.html 530686-1-ref.html
== 531098-1.html 531098-1-ref.html
== 531371-1.html 531371-1-ref.html

View File

@ -293,7 +293,6 @@ protected:
void SkipRuleSet(PRBool aInsideBraces);
PRBool SkipAtRule();
PRBool SkipDeclaration(PRBool aCheckForBraces);
PRBool GetNonCloseParenToken(PRBool aSkipWS);
PRBool PushGroup(nsICSSGroupRule* aRule);
void PopGroup(void);
@ -1737,6 +1736,7 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
}
if (eCSSToken_Ident != mToken.mType) {
REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
UngetToken();
SkipUntil(')');
return PR_FALSE;
}
@ -1791,6 +1791,7 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
if (!mToken.IsSymbol(':')) {
REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
UngetToken();
SkipUntil(')');
return PR_FALSE;
}
@ -1831,19 +1832,24 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
}
break;
case nsMediaFeature::eResolution:
rv = GetToken(PR_TRUE) && mToken.mType == eCSSToken_Dimension &&
rv = GetToken(PR_TRUE);
if (!rv)
break;
rv = mToken.mType == eCSSToken_Dimension &&
mToken.mIntegerValid && mToken.mNumber > 0.0f;
if (rv) {
// No worries about whether unitless zero is allowed, since the
// value must be positive (and we checked that above).
NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
} else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
} else {
rv = PR_FALSE;
}
if (!rv) {
UngetToken();
break;
}
// No worries about whether unitless zero is allowed, since the
// value must be positive (and we checked that above).
NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
} else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
} else {
rv = PR_FALSE;
}
break;
case nsMediaFeature::eEnumerated:
@ -2302,18 +2308,6 @@ CSSParserImpl::SkipUntilOneOf(const PRUnichar* aStopSymbolChars)
}
}
PRBool
CSSParserImpl::GetNonCloseParenToken(PRBool aSkipWS)
{
if (!GetToken(aSkipWS))
return PR_FALSE;
if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == ')') {
UngetToken();
return PR_FALSE;
}
return PR_TRUE;
}
PRBool
CSSParserImpl::SkipDeclaration(PRBool aCheckForBraces)
{
@ -3926,6 +3920,7 @@ CSSParserImpl::ParseTreePseudoElement(nsPseudoClassList **aPseudoElementArgs)
nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
}
else if (!mToken.IsSymbol(',')) {
UngetToken();
SkipUntil(')');
return PR_FALSE;
}
@ -4642,59 +4637,67 @@ CSSParserImpl::ParseCounter(nsCSSValue& aValue)
nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
eCSSUnit_Counter : eCSSUnit_Counters);
if (!GetNonCloseParenToken(PR_TRUE) ||
eCSSToken_Ident != mToken.mType) {
SkipUntil(')');
return PR_FALSE;
}
nsRefPtr<nsCSSValue::Array> val =
nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
if (!val) {
mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
return PR_FALSE;
}
val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
if (eCSSUnit_Counters == unit) {
// get mandatory separator string
if (!ExpectSymbol(',', PR_TRUE) ||
!(GetNonCloseParenToken(PR_TRUE) &&
eCSSToken_String == mToken.mType)) {
SkipUntil(')');
return PR_FALSE;
// A non-iterative for loop to break out when an error occurs.
for (;;) {
if (!GetToken(PR_TRUE)) {
break;
}
val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
}
// get optional type
PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
if (ExpectSymbol(',', PR_TRUE)) {
nsCSSKeyword keyword;
PRBool success = GetNonCloseParenToken(PR_TRUE) &&
eCSSToken_Ident == mToken.mType &&
(keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) !=
eCSSKeyword_UNKNOWN;
if (success) {
success = nsCSSProps::FindKeyword(keyword,
nsCSSProps::kListStyleKTable, type);
if (eCSSToken_Ident != mToken.mType) {
UngetToken();
break;
}
if (!success) {
SkipUntil(')');
return PR_FALSE;
nsRefPtr<nsCSSValue::Array> val =
nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
if (!val) {
mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
break;
}
}
PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
if (!ExpectSymbol(')', PR_TRUE)) {
SkipUntil(')');
return PR_FALSE;
val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
if (eCSSUnit_Counters == unit) {
// must have a comma and then a separator string
if (!ExpectSymbol(',', PR_TRUE) || !GetToken(PR_TRUE)) {
break;
}
if (eCSSToken_String != mToken.mType) {
UngetToken();
break;
}
val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
}
// get optional type
PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
if (ExpectSymbol(',', PR_TRUE)) {
if (!GetToken(PR_TRUE)) {
break;
}
nsCSSKeyword keyword;
if (eCSSToken_Ident != mToken.mType ||
(keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) ==
eCSSKeyword_UNKNOWN ||
!nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable,
type)) {
UngetToken();
break;
}
}
PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
if (!ExpectSymbol(')', PR_TRUE)) {
break;
}
aValue.SetArrayValue(val, unit);
return PR_TRUE;
}
aValue.SetArrayValue(val, unit);
return PR_TRUE;
SkipUntil(')');
return PR_FALSE;
}
PRBool
@ -4839,6 +4842,7 @@ CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
if (!ParseURL(url))
break;
} else {
UngetToken();
break;
}
if (!ExpectSymbol(',', PR_TRUE))

View File

@ -163,6 +163,10 @@
"src: url(\"/fonts/Cat\") format(\"Cat\"; src: local(Rat); )"),
d: { "src" : "url(\"/fonts/Mouse\")" },
noncanonical: true },
{ rule: _("src: url(\"/fonts/Mouse\"); " +
"src: url(\"/fonts/Cat\") format((); src: local(Rat); )"),
d: { "src" : "url(\"/fonts/Mouse\")" },
noncanonical: true },
// Correct unicode-range:
{ rule: _("unicode-range: U+00A5;"), d: { "unicode-range" : "U+00A5" } },

View File

@ -590,6 +590,11 @@ function run() {
should_apply("[badsyntax],all");
should_not_apply("badmedium,[badsyntax]");
should_not_apply("[badsyntax],badmedium");
// bug 528096
should_not_apply_unbalanced("((resolution),all");
should_not_apply_unbalanced("(resolution(),all");
should_not_apply_unbalanced("(resolution (),all");
should_not_apply_unbalanced("(resolution:(),all");
handle_posted_items();
}

View File

@ -754,6 +754,16 @@ function run() {
bodychildset([0, 1, 2, 3, 4, 11]),
bodychildset([5, 6, 7, 8, 9, 10]));
// bug 528096 (tree pseudos)
test_selector_in_html(":-moz-tree-column((){} a", single_a,
empty_set, set_single, html_default_ns);
test_selector_in_html(":-moz-tree-column(x(){} a", single_a,
empty_set, set_single, html_default_ns);
test_selector_in_html(":-moz-tree-column(a b (){} a", single_a,
empty_set, set_single, html_default_ns);
test_selector_in_html(":-moz-tree-column(a, b (){} a", single_a,
empty_set, set_single, html_default_ns);
run_deferred_tests();
}