Bug 1364009 - Don't allow comments/spaces between signs,numbers,and n in an+b syntax for nth-child; r=dbaron

In the an+b syntax, this continues to allow comments and spaces like so:
` an + b `. It does not allow `a n+b`, or `- an+b` or `+ an+b` (and the
same for the `an-b` form). Similarly, it does not allow `- b` or `+ b`.

Additionally, it *does* allow `+/*comment*/n+b` or `-/*comment*/n+b`,
but not `+ n+b` or `-n+b`. This is specced; in this one case we
parse two tokens but do not allow whitespace in between.

MozReview-Commit-ID: INzFGeMPeK7

--HG--
extra : rebase_source : ca5bcf4034759823f79b9a925dc72998d8f0218b
This commit is contained in:
Manish Goregaokar 2017-06-01 15:54:14 -07:00
parent b3c51a3e2d
commit 652a30c309
7 changed files with 93 additions and 112 deletions

View File

@ -4,24 +4,20 @@
<title>Tests :nth-child(An+B) matching</title>
<style type="text/css">
div :nth-child(+/**/3n-2) { color:white; }
div :nth-child(+3n/**/-2) { background-color:black; }
div :nth-child(+3n/**/-2) { font-size:12px; }
div :nth-child(+3n-/**/2) { text-decoration: underline; }
div :nth-child(+3n-2/**/) { border-left-width: 1px; }
div :nth-child(+3/**/n-2) { border-right-width: 1px; }
div :nth-child(+3n/**/-2) { border-top-width: 1px; }
div :nth-child(+3n/**/-2) { border-bottom-width: 1px; }
div :nth-child(+3n-/**/2) { border-style: solid; }
div :nth-child(+3n-2/**/) { border-color: blue; }
div :nth-child(+3n-/**/2) { border-right-width: 1px; }
div :nth-child(+3n-2/**/) { border-style: solid; border-color: blue;}
/* valid but will not match anything */
div :nth-child(-/**/n-2) { color:red; }
div :nth-child(-n/**/-2) { color:red; }
div :nth-child(-n/**/-2) { color:red; }
div :nth-child(-n-/**/2) { color:red; }
div :nth-child(-n-2/**/) { color:red; }
div :nth-child(-1/**/n-2) { color:red; }
div :nth-child(-1n/**/-2) { color:red; }
div :nth-child(-1n/**/-2) { color:red; }
div :nth-child(-1n-/**/2) { color:red; }
@ -32,6 +28,8 @@
div :nth-child(- /**/n-2) { color:red; }
div :nth-child(+/**/ n-2) { color:red; }
div :nth-child(+ /**/n-2) { color:red; }
div :nth-child(+3/**/n-2) { color:red; }
div :nth-child(-/**/n-2) {color: red;}
</style>
</head>

View File

@ -4,24 +4,20 @@
<title>Tests :nth-child(An+B) matching</title>
<style type="text/css">
div :nth-child(+/**/3N-2) { color:white; }
div :nth-child(+3N/**/-2) { background-color:black; }
div :nth-child(+3N/**/-2) { font-size:12px; }
div :nth-child(+3N-/**/2) { text-decoration: underline; }
div :nth-child(+3N-2/**/) { border-left-width: 1px; }
div :nth-child(+3/**/N-2) { border-right-width: 1px; }
div :nth-child(+3N/**/-2) { border-top-width: 1px; }
div :nth-child(+3N/**/-2) { border-bottom-width: 1px; }
div :nth-child(+3N-/**/2) { border-style: solid; }
div :nth-child(+3N-2/**/) { border-color: blue; }
div :nth-child(+3N-/**/2) { border-right-width: 1px; }
div :nth-child(+3N-2/**/) { border-style: solid; border-color: blue;}
/* valid but will not match anything */
div :nth-child(-/**/N-2) { color:red; }
div :nth-child(-N/**/-2) { color:red; }
div :nth-child(-N/**/-2) { color:red; }
div :nth-child(-N-/**/2) { color:red; }
div :nth-child(-N-2/**/) { color:red; }
div :nth-child(-1/**/N-2) { color:red; }
div :nth-child(-1N/**/-2) { color:red; }
div :nth-child(-1N/**/-2) { color:red; }
div :nth-child(-1N-/**/2) { color:red; }
@ -32,6 +28,8 @@
div :nth-child(- /**/N-2) { color:red; }
div :nth-child(+/**/ N-2) { color:red; }
div :nth-child(+ /**/N-2) { color:red; }
div :nth-child(+3/**/N-2) { color:red; }
div :nth-child(-/**/N-2) {color: red;}
</style>
</head>

View File

@ -4,7 +4,6 @@
<title>Tests :nth-child(An+B) matching</title>
<style type="text/css">
x { color:white; }
x { background-color:black; }
x { font-size:12px; }
x { text-decoration: underline; }
@ -12,8 +11,7 @@
x { border-right-width: 1px; }
x { border-top-width: 1px; }
x { border-bottom-width: 1px; }
x { border-style: solid; }
x { border-color: blue; }
x { border-style: solid; border-color: blue;}
</style>
</head>

View File

@ -2,5 +2,5 @@
== attr-case-insensitive-1.html attr-case-insensitive-1-ref.html
== sibling-combinators-on-anon-content-1.xhtml sibling-combinators-on-anon-content-ref.xhtml
== sibling-combinators-on-anon-content-2.xhtml sibling-combinators-on-anon-content-ref.xhtml
fails-if(styloVsGecko||stylo) == nth-child-1.html nth-child-ref.html
fails-if(styloVsGecko||stylo) == nth-child-2.html nth-child-ref.html
== nth-child-1.html nth-child-ref.html
== nth-child-2.html nth-child-ref.html

View File

@ -6331,9 +6331,10 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
CSSPseudoClassType aType)
{
int32_t numbers[2] = { 0, 0 };
int32_t sign[2] = { 1, 1 };
bool hasSign[2] = { false, false };
bool lookForB = true;
bool onlyN = false;
int hasSign = false;
int sign = 1;
// Follow the whitespace rules as proposed in
// http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
@ -6343,17 +6344,6 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
return eSelectorParsingStatus_Error;
}
if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
hasSign[0] = true;
if (mToken.IsSymbol('-')) {
sign[0] = -1;
}
if (! GetToken(false)) {
REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
return eSelectorParsingStatus_Error;
}
}
// A helper function that checks if the token starts with literal string
// |aStr| using a case-insensitive match.
auto TokenBeginsWith = [this] (const nsLiteralString& aStr) {
@ -6361,6 +6351,21 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
nsASCIICaseInsensitiveStringComparator());
};
if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
// This can only be +n or -n, since +an, -an, +a, -a will all
// parse a number as the first token.
numbers[0] = mToken.IsSymbol('+') ? 1 : -1;
onlyN = true;
// consume the `n`
// We do not allow whitespace here
// https://drafts.csswg.org/css-syntax-3/#the-anb-type
if (! GetToken(false)) {
REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
}
if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
// The CSS tokenization doesn't handle :nth-child() containing - well:
// 2n-1 is a dimension
@ -6370,7 +6375,7 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
uint32_t truncAt = 0;
if (TokenBeginsWith(NS_LITERAL_STRING("n-"))) {
truncAt = 1;
} else if (TokenBeginsWith(NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
} else if (TokenBeginsWith(NS_LITERAL_STRING("-n-"))) {
truncAt = 2;
}
if (truncAt != 0) {
@ -6379,76 +6384,58 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
}
}
if (eCSSToken_Ident == mToken.mType) {
if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
numbers[0] = 2;
numbers[1] = 1;
lookForB = false;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
numbers[0] = 2;
numbers[1] = 0;
lookForB = false;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
numbers[0] = sign[0];
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
numbers[0] = -1;
}
else {
if (onlyN) {
// If we parsed a + or -, check that the truncated
// token is an "n"
if (eCSSToken_Ident != mToken.mType || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
return eSelectorParsingStatus_Error;
}
}
else if (eCSSToken_Number == mToken.mType) {
if (!mToken.mIntegerValid) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
// for +-an case
if (mToken.mHasSign && hasSign[0]) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
int32_t intValue = mToken.mInteger * sign[0];
// for -a/**/n case
if (! GetToken(false)) {
numbers[1] = intValue;
lookForB = false;
}
else {
if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
numbers[0] = intValue;
}
else if (eCSSToken_Ident == mToken.mType && TokenBeginsWith(NS_LITERAL_STRING("n-"))) {
numbers[0] = intValue;
mScanner->Backup(mToken.mIdent.Length() - 1);
}
else {
UngetToken();
numbers[1] = intValue;
} else {
if (eCSSToken_Ident == mToken.mType) {
if (mToken.mIdent.LowerCaseEqualsLiteral("odd")) {
numbers[0] = 2;
numbers[1] = 1;
lookForB = false;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("even")) {
numbers[0] = 2;
numbers[1] = 0;
lookForB = false;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
numbers[0] = 1;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("-n")) {
numbers[0] = -1;
}
else {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
}
}
else if (eCSSToken_Dimension == mToken.mType) {
if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
else if (eCSSToken_Number == mToken.mType) {
if (!mToken.mIntegerValid) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
numbers[1] = mToken.mInteger;
lookForB = false;
}
else if (eCSSToken_Dimension == mToken.mType) {
if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
numbers[0] = mToken.mInteger;
}
// XXX If it's a ')', is that valid? (as 0n+0)
else {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
UngetToken();
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
// for +-an case
if ( mToken.mHasSign && hasSign[0] ) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
numbers[0] = mToken.mInteger * sign[0];
}
// XXX If it's a ')', is that valid? (as 0n+0)
else {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
UngetToken();
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
if (! GetToken(true)) {
@ -6460,9 +6447,9 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
// If it is separated by whitespace from what follows it, it appears
// as a separate token rather than part of the number token.
if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
hasSign[1] = true;
hasSign = true;
if (mToken.IsSymbol('-')) {
sign[1] = -1;
sign = -1;
}
if (! GetToken(true)) {
REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
@ -6470,12 +6457,12 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
}
}
if (eCSSToken_Number != mToken.mType ||
!mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
!mToken.mIntegerValid || mToken.mHasSign == hasSign) {
REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
UngetToken();
return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
}
numbers[1] = mToken.mInteger * sign[1];
numbers[1] = mToken.mInteger * sign;
if (! GetToken(true)) {
REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
return eSelectorParsingStatus_Error;

View File

@ -140,7 +140,7 @@ to mochitest command.
* test_selectors_on_anonymous_content.html: xbl and :nth-child [1]
* test_parse_rule.html `rgb(0, 128, 0)`: color properties not getting computed [5]
* test_selectors.html `:nth-child`: &lt;an+b&gt; parsing difference bug 1364009 [14]
* test_selectors.html `:nth-child`: https://github.com/servo/rust-cssparser/issues/153 [4]
## Ignore

View File

@ -647,7 +647,7 @@ function run() {
test_parseable(":nth-child(+n/**/+2)");
test_parseable(":nth-child(+n+/**/2)");
test_parseable(":nth-child(+n+2/**/)");
test_parseable(":nth-child(+1/**/n+2)");
test_balanced_unparseable(":nth-child(+1/**/n+2)");
test_parseable(":nth-child(+1n/**/+2)");
test_parseable(":nth-child(+1n/**/+2)");
test_parseable(":nth-child(+1n+/**/2)");
@ -657,7 +657,7 @@ function run() {
test_parseable(":nth-child(-n/**/+2)");
test_parseable(":nth-child(-n+/**/2)");
test_parseable(":nth-child(-n+2/**/)");
test_parseable(":nth-child(-1/**/n+2)");
test_balanced_unparseable(":nth-child(-1/**/n+2)");
test_parseable(":nth-child(-1n/**/+2)");
test_parseable(":nth-child(-1n/**/+2)");
test_parseable(":nth-child(-1n+/**/2)");
@ -671,7 +671,7 @@ function run() {
test_parseable(":nth-child(+n/**/-2)");
test_parseable(":nth-child(+n-/**/2)");
test_parseable(":nth-child(+n-2/**/)");
test_parseable(":nth-child(+1/**/n-2)");
test_balanced_unparseable(":nth-child(+1/**/n-2)");
test_parseable(":nth-child(+1n/**/-2)");
test_parseable(":nth-child(+1n/**/-2)");
test_parseable(":nth-child(+1n-/**/2)");
@ -681,7 +681,7 @@ function run() {
test_parseable(":nth-child(-n/**/-2)");
test_parseable(":nth-child(-n-/**/2)");
test_parseable(":nth-child(-n-2/**/)");
test_parseable(":nth-child(-1/**/n-2)");
test_balanced_unparseable(":nth-child(-1/**/n-2)");
test_parseable(":nth-child(-1n/**/-2)");
test_parseable(":nth-child(-1n/**/-2)");
test_parseable(":nth-child(-1n-/**/2)");
@ -695,7 +695,7 @@ function run() {
test_parseable(":nth-child(+N/**/-2)");
test_parseable(":nth-child(+N-/**/2)");
test_parseable(":nth-child(+N-2/**/)");
test_parseable(":nth-child(+1/**/N-2)");
test_balanced_unparseable(":nth-child(+1/**/N-2)");
test_parseable(":nth-child(+1N/**/-2)");
test_parseable(":nth-child(+1N/**/-2)");
test_parseable(":nth-child(+1N-/**/2)");
@ -705,7 +705,7 @@ function run() {
test_parseable(":nth-child(-N/**/-2)");
test_parseable(":nth-child(-N-/**/2)");
test_parseable(":nth-child(-N-2/**/)");
test_parseable(":nth-child(-1/**/N-2)");
test_balanced_unparseable(":nth-child(-1/**/N-2)");
test_parseable(":nth-child(-1N/**/-2)");
test_parseable(":nth-child(-1N/**/-2)");
test_parseable(":nth-child(-1N-/**/2)");
@ -716,14 +716,14 @@ function run() {
test_balanced_unparseable(":nth-child(+ /**/N-2)");
test_parseable(":nth-child( +n + 1 )");
test_parseable(":nth-child( +/**/n + 1 )");
test_parseable(":nth-child( -/**/2/**/n/**/+/**/4 )");
test_balanced_unparseable(":nth-child( -/**/ 2/**/n/**/+/**/4 )");
test_balanced_unparseable(":nth-child( -/**/2 /**/n/**/+/**/4 )");
test_balanced_unparseable(":nth-child( -/**/2/**/ n/**/+/**/4 )");
test_parseable(":nth-child( -/**/2/**/n /**/+/**/4 )");
test_parseable(":nth-child( -/**/2/**/n/**/ +/**/4 )");
test_parseable(":nth-child(+1/**/n-1)");
test_parseable(":nth-child(1/**/n-1)");
test_balanced_unparseable(":nth-child( -/**/2/**/n/**/+/**/4 )");
test_parseable(":nth-child( -2n/**/ + /**/4 )");
test_parseable(":nth-child( -2n/**/+/**/4 )");
test_parseable(":nth-child( -2n /**/+/**/4 )");
test_parseable(":nth-child( -/**/n /**/+ /**/ 4 )");
test_parseable(":nth-child( +/**/n /**/+ /**/ 4 )");
test_balanced_unparseable(":nth-child(+1/**/n-1)");
test_balanced_unparseable(":nth-child(1/**/n-1)");
// bug 876570
test_balanced_unparseable(":nth-child(+2n-)");
test_balanced_unparseable(":nth-child(+n-)");