Bug 1290825 - Reject various things that aren't user-action pseudo classes when they follow pseudo-elements. r=bz

The existing code, from bug 922669, in ParsePseudoSelector that allows
things to come after a pseudo-element requires that the first character
after the pseudo-element be a colon.  However, this doesn't forbid
things like ::-moz-color-swatch:hover#foo, which need to be errors in
ParseSelector; those tests are added here.  Furthermore, the
error-checking in ParsePseudoSelector doesn't prevent the pseudo-element
from being followed by a :not() or by an additional pseudo-element; to
fix that this patch moves the error tests out of the pseudo-class
condition and also has it test !isPseudoClass.

Without the patch, the tests produced the following failures:
TEST-UNEXPECTED-FAIL | layout/style/test/test_selectors.html | selector ::-moz-color-swatch:not(.foo) was a parser error - got "1402", expected "auto"
TEST-UNEXPECTED-FAIL | layout/style/test/test_selectors.html | selector '::-moz-color-swatch:not(.foo)' plus EOF is parse error
followed by a crash due to:
Assertion failure: !(IsPseudoElement() && (mIDList || mAttrList)) (If pseudo-elements can have id or attribute selectors after them, specificity calculation must be updated), at /home/dbaron/builds/ssd/mozilla-central/mozilla/layout/style/StyleRule.cpp:503 in CalcWeightWithoutNegations
from the "::-moz-color-swatch:hover#foo" test.

With that test commented out (and still without the code changes), there
is instead an additional pair of failures from the following test:
TEST-UNEXPECTED-FAIL | layout/style/test/test_selectors.html | selector .foo::after:not(.bar) ~ h3 was a parser error - got "1406", expected "auto"
TEST-UNEXPECTED-FAIL | layout/style/test/test_selectors.html | selector '.foo::after:not(.bar) ~ h3' plus EOF is parse error
along with a failure due to an unexpected assertion:
###!!! ASSERTION: Shouldn't have negations: '!selector->mNegations', file /home/dbaron/builds/ssd/mozilla-central/mozilla/layout/style/nsCSSRuleProcessor.cpp, function AddRule, line 3415

With the patch, the tests pass.

MozReview-Commit-ID: KxAFSQtPVhu
This commit is contained in:
L. David Baron 2016-08-29 11:43:30 -07:00
parent dfe11b3205
commit 6e37c27e75
2 changed files with 42 additions and 23 deletions

View File

@ -6031,6 +6031,23 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
return eSelectorParsingStatus_Error;
}
if (aSelector.IsPseudoElement()) {
CSSPseudoElementType type = aSelector.PseudoType();
if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
// We only allow user action pseudo-classes on certain pseudo-elements.
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
UngetToken();
return eSelectorParsingStatus_Error;
}
if (!isPseudoClass || !pseudoClassIsUserAction) {
// CSS 4 Selectors says that pseudo-elements can only be followed by
// a user action pseudo-class.
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
UngetToken();
return eSelectorParsingStatus_Error;
}
}
if (!parsingPseudoElement &&
CSSPseudoClassType::negation == pseudoClassType) {
if (aIsNegated) { // :not() can't be itself negated
@ -6046,22 +6063,6 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
}
}
else if (!parsingPseudoElement && isPseudoClass) {
if (aSelector.IsPseudoElement()) {
CSSPseudoElementType type = aSelector.PseudoType();
if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
// We only allow user action pseudo-classes on certain pseudo-elements.
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
UngetToken();
return eSelectorParsingStatus_Error;
}
if (!pseudoClassIsUserAction) {
// CSS 4 Selectors says that pseudo-elements can only be followed by
// a user action pseudo-class.
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
UngetToken();
return eSelectorParsingStatus_Error;
}
}
aDataMask |= SEL_MASK_PCLASS;
if (eCSSToken_Function == mToken.mType) {
nsSelectorParsingStatus parsingStatus;
@ -6503,13 +6504,7 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
ParseTypeOrUniversalSelector(dataMask, *selector, false);
while (parsingStatus == eSelectorParsingStatus_Continue) {
if (eCSSToken_ID == mToken.mType) { // #id
parsingStatus = ParseIDSelector(dataMask, *selector);
}
else if (mToken.IsSymbol('.')) { // .class
parsingStatus = ParseClassSelector(dataMask, *selector);
}
else if (mToken.IsSymbol(':')) { // :pseudo
if (mToken.IsSymbol(':')) { // :pseudo
parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
getter_AddRefs(pseudoElement),
getter_Transfers(pseudoElementArgs),
@ -6527,6 +6522,17 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
selector->mClassList = pseudoElementArgs.forget();
selector->SetPseudoType(pseudoElementType);
}
} else if (selector->IsPseudoElement()) {
// Once we parsed a pseudo-element, we can only parse
// pseudo-classes (and only a limited set, which
// ParsePseudoSelector knows how to handle).
parsingStatus = eSelectorParsingStatus_Done;
UngetToken();
break;
} else if (eCSSToken_ID == mToken.mType) { // #id
parsingStatus = ParseIDSelector(dataMask, *selector);
} else if (mToken.IsSymbol('.')) { // .class
parsingStatus = ParseClassSelector(dataMask, *selector);
}
else if (mToken.IsSymbol('[')) { // [attribute
parsingStatus = ParseAttributeSelector(dataMask, *selector);

View File

@ -1244,6 +1244,19 @@ function run() {
// Test that :-moz-placeholder is parsable.
test_parseable(":-moz-placeholder");
// Test that things other than user-action pseudo-classes are
// rejected after pseudo-elements. Some of these tests rely on
// using a pseudo-element that supports a user-action pseudo-class
// after it, so we need to use the prefixed ::-moz-color-swatch,
// which is one of the ones with
// CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (none of which are
// unprefixed).
test_parseable("::-moz-color-swatch:hover");
test_balanced_unparseable("::-moz-color-swatch:not(.foo)");
test_balanced_unparseable("::-moz-color-swatch:first-child");
test_balanced_unparseable("::-moz-color-swatch:hover#foo");
test_balanced_unparseable(".foo::after:not(.bar) ~ h3");
run_deferred_tests();
}