From de225cca0d256117e2a0da990fd8c50e79ac6a1c Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 00:20:44 +0800 Subject: Issue #2137 - Part 1: Modify :not() selector to accept a complex selector list --- layout/style/StyleRule.cpp | 12 ++++-- layout/style/nsCSSParser.cpp | 78 ++++++++++++++++++++++--------------- layout/style/nsCSSPseudoClassList.h | 5 +++ layout/style/nsCSSPseudoClasses.cpp | 8 ++++ layout/style/nsCSSPseudoClasses.h | 1 + layout/style/nsCSSRuleProcessor.cpp | 11 ++++++ 6 files changed, 80 insertions(+), 35 deletions(-) (limited to 'layout') diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 815c6f96cc..9c6f73ffe1 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -908,9 +908,13 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations // has a ":" that can't be escaped and (b) all pseudo-classes at // this point are known, and therefore we know they don't need // escaping. - aString.Append(temp); + if (!nsCSSPseudoClasses::IsHiddenFromSerialization(list->mType)) { + aString.Append(temp); + } if (list->u.mMemory) { - aString.Append(char16_t('(')); + if (!nsCSSPseudoClasses::IsHiddenFromSerialization(list->mType)) { + aString.Append(char16_t('(')); + } if (nsCSSPseudoClasses::HasStringArg(list->mType)) { nsStyleUtil::AppendEscapedCSSIdent( nsDependentString(list->u.mString), aString); @@ -939,7 +943,9 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations list->u.mSelectorList->ToString(tmp, aSheet); aString.Append(tmp); } - aString.Append(char16_t(')')); + if (!nsCSSPseudoClasses::IsHiddenFromSerialization(list->mType)) { + aString.Append(char16_t(')')); + } } } } diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index f9f198417f..f60b1c5287 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -67,6 +67,7 @@ static bool sWebkitPrefixedAliasesEnabled; static bool sWebkitDevicePixelRatioEnabled; static bool sMozGradientsEnabled; static bool sControlCharVisibility; +static bool sLegacyNegationPseudoClassEnabled; const uint32_t nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = { @@ -6144,7 +6145,6 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, // is that of a function pseudo it better be a function token if ((eCSSToken_Function == mToken.mType) != (isTree || - CSSPseudoClassType::negation == pseudoClassType || nsCSSPseudoClasses::HasStringArg(pseudoClassType) || nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) || nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType)) && @@ -6182,25 +6182,26 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, } } - if (!parsingPseudoElement && - CSSPseudoClassType::negation == pseudoClassType) { - if (aIsNegated) { // :not() can't be itself negated - REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot); - UngetToken(); - return eSelectorParsingStatus_Error; - } - // CSS 3 Negation pseudo-class takes one simple selector as argument - nsSelectorParsingStatus parsingStatus = - ParseNegatedSimpleSelector(aDataMask, aSelector); - if (eSelectorParsingStatus_Continue != parsingStatus) { - return parsingStatus; - } - } - else if (!parsingPseudoElement && isPseudoClass) { + if (!parsingPseudoElement && isPseudoClass) { aDataMask |= SEL_MASK_PCLASS; if (eCSSToken_Function == mToken.mType) { nsSelectorParsingStatus parsingStatus; - if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) { + if (sLegacyNegationPseudoClassEnabled && + CSSPseudoClassType::negation == pseudoClassType) { + // :not() can't be itself negated + if (aIsNegated) { + REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot); + UngetToken(); + return eSelectorParsingStatus_Error; + } + // CSS 3 Negation pseudo-class takes one simple selector as argument + parsingStatus = + ParseNegatedSimpleSelector(aDataMask, aSelector); + if (eSelectorParsingStatus_Continue != parsingStatus) { + return parsingStatus; + } + } + else if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) { parsingStatus = ParsePseudoClassWithIdentArg(aSelector, pseudoClassType); } @@ -6578,8 +6579,6 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, // // Parse the argument of a pseudo-class that has a selector list argument. -// Such selector lists cannot contain combinators, but can contain -// anything that goes between a pair of combinators. // CSSParserImpl::nsSelectorParsingStatus CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, @@ -6607,22 +6606,35 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') } - for (nsCSSSelectorList *l = slist; l; l = l->mNext) { - nsCSSSelector *s = l->mSelectors; - if (s == nullptr) { - MOZ_ASSERT(isForgiving, - "unexpected empty selector in unforgiving selector list"); - break; + // Special handling for the :not() pseudo-class. + if (aType == CSSPseudoClassType::negation) { + nsCSSSelector* negations = &aSelector; + while (negations->mNegations) { + negations = negations->mNegations; } - // Check that none of the selectors in the list have combinators or - // pseudo-elements. - if ((!isForgiving && s->mNext) || s->IsPseudoElement()) { - return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + // XXX: Use a special internal-only pseudo-class to handle selector lists. + // TODO: This should only happen if we don't have a simple selector. + nsCSSSelector* newSel = new nsCSSSelector(); + newSel->AddPseudoClass(CSSPseudoClassType::mozAnyPrivate, slist.forget()); + negations->mNegations = newSel; + } else { + for (nsCSSSelectorList *l = slist; l; l = l->mNext) { + nsCSSSelector *s = l->mSelectors; + if (s == nullptr) { + MOZ_ASSERT(isForgiving, + "unexpected empty selector in unforgiving selector list"); + break; + } + // Check that none of the selectors in the list have combinators or + // pseudo-elements. + if ((!isForgiving && s->mNext) || s->IsPseudoElement()) { + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } } - } - // Add the pseudo with the selector list parameter - aSelector.AddPseudoClass(aType, slist.forget()); + // Add the pseudo with the selector list parameter + aSelector.AddPseudoClass(aType, slist.forget()); + } // close the parenthesis if (!ExpectSymbol(')', true)) { @@ -17899,6 +17911,8 @@ nsCSSParser::Startup() "layout.css.prefixes.gradients"); Preferences::AddBoolVarCache(&sControlCharVisibility, "layout.css.control-characters.visible"); + Preferences::AddBoolVarCache(&sLegacyNegationPseudoClassEnabled, + "layout.css.legacy-negation-pseudo.enabled"); } nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader, diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index 740ebcc422..cbe3bd8f92 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -133,6 +133,11 @@ CSS_PSEUDO_CLASS(mozBrowserFrame, ":-moz-browser-frame", // matching operation. CSS_PSEUDO_CLASS(scope, ":scope", 0, "layout.css.scope-pseudo.enabled") +// Matches selectors inside the selector list argument. Unlike :is(), +// this is unforgiving and hidden from serialization. +CSS_PSEUDO_CLASS(mozAnyPrivate, ":-moz-any-private", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "") + // :not needs to come at the end of the non-bit pseudo-class list, since // it doesn't actually get directly matched on in SelectorMatches. CSS_PSEUDO_CLASS(negation, ":not", 0, "") diff --git a/layout/style/nsCSSPseudoClasses.cpp b/layout/style/nsCSSPseudoClasses.cpp index 23517ef52c..0fc460a514 100644 --- a/layout/style/nsCSSPseudoClasses.cpp +++ b/layout/style/nsCSSPseudoClasses.cpp @@ -122,7 +122,9 @@ bool nsCSSPseudoClasses::HasSelectorListArg(Type aType) { return HasForgivingSelectorListArg(aType) || + aType == Type::negation || aType == Type::mozAny || + aType == Type::mozAnyPrivate || aType == Type::host || aType == Type::hostContext; } @@ -133,6 +135,12 @@ nsCSSPseudoClasses::HasOptionalSelectorListArg(Type aType) return aType == Type::host; } +bool +nsCSSPseudoClasses::IsHiddenFromSerialization(Type aType) +{ + return aType == Type::mozAnyPrivate; +} + void nsCSSPseudoClasses::PseudoTypeToString(Type aType, nsAString& aString) { diff --git a/layout/style/nsCSSPseudoClasses.h b/layout/style/nsCSSPseudoClasses.h index 99c05b019f..ff2da74ff0 100644 --- a/layout/style/nsCSSPseudoClasses.h +++ b/layout/style/nsCSSPseudoClasses.h @@ -62,6 +62,7 @@ public: static bool HasForgivingSelectorListArg(Type aType); static bool HasSelectorListArg(Type aType); static bool HasOptionalSelectorListArg(Type aType); + static bool IsHiddenFromSerialization(Type aType); static bool IsUserActionPseudoClass(Type aType); // Should only be used on types other than Count and NotPseudoClass diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index f637a063a6..dc2157084b 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1931,6 +1931,17 @@ static bool SelectorMatches(Element* aElement, } break; + case CSSPseudoClassType::mozAnyPrivate: + { + if (!SelectorListMatches(aElement, + pseudoClass, + aNodeMatchContext, + aTreeMatchContext)) { + return false; + } + } + break; + case CSSPseudoClassType::host: { // In order to match :host, the element must be a shadow root host, -- cgit v1.2.3 From e9a18599158301e5aa2b8ff3597455f5fe62ca44 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 12:18:39 +0800 Subject: Issue #2137 - Part 2: Implement SelectorParsingFlags and use it to pass info around --- layout/style/nsCSSParser.cpp | 179 +++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 85 deletions(-) (limited to 'layout') diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index f60b1c5287..38e2759bb4 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -110,6 +110,18 @@ enum class GridTrackListFlags { }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackListFlags) +/** + * Additional information about a selector being parsed. + */ +enum class SelectorParsingFlags { + eNone = 0, + eIsNegated = 1 << 0, + eIsForgiving = 1 << 1, + eDisallowCombinators = 1 << 2, + eDisallowPseudoElements = 1 << 3 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorParsingFlags) + namespace { // Rule processing function @@ -760,21 +772,21 @@ protected: // aPseudoElement and aPseudoElementArgs are the location where // pseudo-elements (as opposed to pseudo-classes) are stored; // pseudo-classes are stored on aSelector. aPseudoElement and - // aPseudoElementArgs must be non-null iff !aIsNegated. - nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask, - nsCSSSelector& aSelector, - bool aIsNegated, - nsIAtom** aPseudoElement, - nsAtomList** aPseudoElementArgs, - CSSPseudoElementType* aPseudoElementType, - bool aDisallowCombinators); + // aPseudoElementArgs must be non-null iff the eIsNegated flag of + // aFlags is not set. + nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags, + nsIAtom** aPseudoElement, + nsAtomList** aPseudoElementArgs, + CSSPseudoElementType* aPseudoElementType); nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask, nsCSSSelector& aSelector); - nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask, - nsCSSSelector& aSelector, - bool aIsNegated); + nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags); nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector, CSSPseudoClassType aType); @@ -784,24 +796,22 @@ protected: nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, CSSPseudoClassType aType, - bool aDisallowCombinators); + SelectorParsingFlags aFlags); - nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask, - nsCSSSelector& aSelector); + nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags); // If aStopChar is non-zero, the selector list is done when we hit // aStopChar. Otherwise, it's done when we hit EOF. bool ParseSelectorList(nsCSSSelectorList*& aListHead, char16_t aStopChar, - bool aIsForgiving, - bool aDisallowCombinators); + SelectorParsingFlags aFlags = SelectorParsingFlags::eNone); bool ParseSelectorGroup(nsCSSSelectorList*& aListHead, - bool aIsForgiving, - bool aDisallowCombinators); + SelectorParsingFlags aFlags); bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator, - bool aIsForgiving, - bool aDisallowCombinators); + SelectorParsingFlags aFlags); enum { eParseDeclaration_InBraces = 1 << 0, @@ -2331,7 +2341,7 @@ CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString, css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); InitScanner(scanner, reporter, aURI, aURI, nullptr); - bool success = ParseSelectorList(*aSelectorList, char16_t(0), false, false); + bool success = ParseSelectorList(*aSelectorList, char16_t(0)); // We deliberately do not call OUTPUT_ERROR here, because all our // callers map a failure return to a JS exception, and if that JS @@ -5454,7 +5464,7 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, nsCSSSelectorList* slist = nullptr; uint32_t linenum, colnum; if (!GetNextTokenLocation(true, &linenum, &colnum) || - !ParseSelectorList(slist, char16_t('{'), false, false)) { + !ParseSelectorList(slist, char16_t('{'))) { REPORT_UNEXPECTED(PEBadSelectorRSIgnored); OUTPUT_ERROR(); SkipRuleSet(aInsideBraces); @@ -5491,12 +5501,11 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, bool CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, char16_t aStopChar, - bool aIsForgiving, - bool aDisallowCombinators) + SelectorParsingFlags aFlags) { nsCSSSelectorList* list = nullptr; - if (! ParseSelectorGroup(list, aIsForgiving, aDisallowCombinators)) { - if (aIsForgiving) { + if (! ParseSelectorGroup(list, aFlags)) { + if (aFlags & SelectorParsingFlags::eIsForgiving) { // Initialize to an empty list if the first selector group was invalid // and we're a forgiving selector list. list = new nsCSSSelectorList(); @@ -5526,16 +5535,17 @@ CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, if (',' == tk->mSymbol) { nsCSSSelectorList* newList = nullptr; // Another selector group must follow - if (! ParseSelectorGroup(newList, aIsForgiving, aDisallowCombinators)) { + if (! ParseSelectorGroup(newList, aFlags)) { // Ignore invalid selectors if we're a forgiving selector list. - if (aIsForgiving) { + if (aFlags & SelectorParsingFlags::eIsForgiving) { continue; } break; } // Replace the list head if: it's empty and we're a forgiving selector // list. Otherwise, add the new list to the end of the selector list. - if (aIsForgiving && !aListHead->mSelectors) { + if ((aFlags & SelectorParsingFlags::eIsForgiving) && + !aListHead->mSelectors) { MOZ_ASSERT(newList->mSelectors, "replacing empty list head with an empty selector list?"); aListHead = newList; @@ -5550,7 +5560,7 @@ CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, } } - if (!aIsForgiving) { + if (!(aFlags & SelectorParsingFlags::eIsForgiving)) { REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); UngetToken(); break; @@ -5574,13 +5584,14 @@ static bool IsUniversalSelector(const nsCSSSelector& aSelector) } bool -CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList, bool aIsForgiving, bool aDisallowCombinators) +CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList, + SelectorParsingFlags aFlags) { char16_t combinator = 0; nsAutoPtr list(new nsCSSSelectorList()); for (;;) { - if (!ParseSelector(list, combinator, aIsForgiving, aDisallowCombinators)) { + if (!ParseSelector(list, combinator, aFlags)) { return false; } @@ -5616,7 +5627,7 @@ CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList, bool aIsForgiving, return false; } - if (aIsForgiving && aDisallowCombinators) { + if (aFlags & SelectorParsingFlags::eDisallowCombinators) { return false; } } @@ -5675,9 +5686,9 @@ CSSParserImpl::ParseClassSelector(int32_t& aDataMask, // namespace|type or namespace|* or *|* or * // CSSParserImpl::nsSelectorParsingStatus -CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, - nsCSSSelector& aSelector, - bool aIsNegated) +CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags) { nsAutoString buffer; if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace @@ -5784,7 +5795,7 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, SetDefaultNamespaceOnSelector(aSelector); } - if (aIsNegated) { + if (aFlags & SelectorParsingFlags::eIsNegated) { // restore last token read in case of a negated type selector UngetToken(); } @@ -6036,17 +6047,17 @@ CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask, // Parse pseudo-classes and pseudo-elements // CSSParserImpl::nsSelectorParsingStatus -CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, - nsCSSSelector& aSelector, - bool aIsNegated, - nsIAtom** aPseudoElement, - nsAtomList** aPseudoElementArgs, - CSSPseudoElementType* aPseudoElementType, - bool aDisallowCombinators) -{ - NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs), +CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags, + nsIAtom** aPseudoElement, + nsAtomList** aPseudoElementArgs, + CSSPseudoElementType* aPseudoElementType) +{ + bool isNegated = !!(aFlags & SelectorParsingFlags::eIsNegated); + NS_ASSERTION(isNegated || (aPseudoElement && aPseudoElementArgs), "expected location to store pseudo element"); - NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs), + NS_ASSERTION(!isNegated || (!aPseudoElement && !aPseudoElementArgs), "negated selectors shouldn't have a place to store " "pseudo elements"); if (! GetToken(false)) { // premature eof @@ -6186,17 +6197,22 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, aDataMask |= SEL_MASK_PCLASS; if (eCSSToken_Function == mToken.mType) { nsSelectorParsingStatus parsingStatus; + // Only the combinators restriction should be passed down the chain. + SelectorParsingFlags flags = + (aFlags & SelectorParsingFlags::eDisallowCombinators) ? + SelectorParsingFlags::eDisallowCombinators : + SelectorParsingFlags::eNone; if (sLegacyNegationPseudoClassEnabled && CSSPseudoClassType::negation == pseudoClassType) { // :not() can't be itself negated - if (aIsNegated) { + if (isNegated) { REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot); UngetToken(); return eSelectorParsingStatus_Error; } // CSS 3 Negation pseudo-class takes one simple selector as argument parsingStatus = - ParseNegatedSimpleSelector(aDataMask, aSelector); + ParseNegatedSimpleSelector(aDataMask, aSelector, flags); if (eSelectorParsingStatus_Continue != parsingStatus) { return parsingStatus; } @@ -6214,7 +6230,7 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, "unexpected pseudo with function token"); parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector, pseudoClassType, - aDisallowCombinators); + flags); } if (eSelectorParsingStatus_Continue != parsingStatus) { if (eSelectorParsingStatus_Error == parsingStatus) { @@ -6230,11 +6246,18 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, else if (isPseudoElement || isAnonBox) { // Pseudo-element. Make some more sanity checks. - if (aIsNegated) { // pseudo-elements can't be negated + // Pseudo-elements can't be negated. + if (isNegated) { REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot); UngetToken(); return eSelectorParsingStatus_Error; } + // Pseudo-elements might not be allowed from appearing + // (e.g. as an argument to the functional part of a pseudo-class). + if (aFlags & SelectorParsingFlags::eDisallowPseudoElements) { + UngetToken(); + return eSelectorParsingStatus_Error; + } // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed // to have a single ':' on them. Others (CSS3+ pseudo-elements and // various -moz-* pseudo-elements) must have |parsingPseudoElement| @@ -6305,9 +6328,12 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, // Parse the argument of a negation pseudo-class :not() // CSSParserImpl::nsSelectorParsingStatus -CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask, - nsCSSSelector& aSelector) +CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags) { + aFlags |= SelectorParsingFlags::eIsNegated; + if (! GetToken(true)) { // premature eof REPORT_UNEXPECTED_EOF(PENegationEOF); return eSelectorParsingStatus_Error; @@ -6340,9 +6366,8 @@ CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask, parsingStatus = ParseClassSelector(aDataMask, *newSel); } else if (mToken.IsSymbol(':')) { // :pseudo - parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true, - nullptr, nullptr, nullptr, - false); + parsingStatus = ParsePseudoSelector(aDataMask, *newSel, aFlags, + nullptr, nullptr, nullptr); } else if (mToken.IsSymbol('[')) { // [attribute parsingStatus = ParseAttributeSelector(aDataMask, *newSel); @@ -6353,7 +6378,7 @@ CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask, } else { // then it should be a type element or universal selector - parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true); + parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, aFlags); } if (eSelectorParsingStatus_Error == parsingStatus) { REPORT_UNEXPECTED_TOKEN(PENegationBadInner); @@ -6583,22 +6608,22 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, CSSParserImpl::nsSelectorParsingStatus CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, CSSPseudoClassType aType, - bool aDisallowCombinators) + SelectorParsingFlags aFlags) { - bool isForgiving = - nsCSSPseudoClasses::HasForgivingSelectorListArg(aType); bool isSingleSelector = nsCSSPseudoClasses::HasSingleSelectorArg(aType); - if (isSingleSelector && !aDisallowCombinators) { - aDisallowCombinators = true; + if (nsCSSPseudoClasses::HasForgivingSelectorListArg(aType)) { + aFlags |= SelectorParsingFlags::eIsForgiving; + } else if (isSingleSelector || aType == CSSPseudoClassType::mozAny) { + aFlags |= SelectorParsingFlags::eDisallowCombinators; } + aFlags |= SelectorParsingFlags::eDisallowPseudoElements; nsAutoPtr slist; if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'), - isForgiving, - aDisallowCombinators)) { + aFlags)) { return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') } @@ -6618,20 +6643,6 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, newSel->AddPseudoClass(CSSPseudoClassType::mozAnyPrivate, slist.forget()); negations->mNegations = newSel; } else { - for (nsCSSSelectorList *l = slist; l; l = l->mNext) { - nsCSSSelector *s = l->mSelectors; - if (s == nullptr) { - MOZ_ASSERT(isForgiving, - "unexpected empty selector in unforgiving selector list"); - break; - } - // Check that none of the selectors in the list have combinators or - // pseudo-elements. - if ((!isForgiving && s->mNext) || s->IsPseudoElement()) { - return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') - } - } - // Add the pseudo with the selector list parameter aSelector.AddPseudoClass(aType, slist.forget()); } @@ -6653,8 +6664,7 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, bool CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator, - bool aIsForgiving, - bool aDisallowCombinators) + SelectorParsingFlags aFlags) { if (! GetToken(true)) { REPORT_UNEXPECTED_EOF(PESelectorEOF); @@ -6668,15 +6678,14 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, int32_t dataMask = 0; nsSelectorParsingStatus parsingStatus = - ParseTypeOrUniversalSelector(dataMask, *selector, false); + ParseTypeOrUniversalSelector(dataMask, *selector, aFlags); while (parsingStatus == eSelectorParsingStatus_Continue) { if (mToken.IsSymbol(':')) { // :pseudo - parsingStatus = ParsePseudoSelector(dataMask, *selector, false, + parsingStatus = ParsePseudoSelector(dataMask, *selector, aFlags, getter_AddRefs(pseudoElement), getter_Transfers(pseudoElementArgs), - &pseudoElementType, - aDisallowCombinators); + &pseudoElementType); if (pseudoElement && pseudoElementType != CSSPseudoElementType::AnonBox) { // Pseudo-elements other than anonymous boxes are represented with @@ -6732,7 +6741,7 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, // XXX(franklindm): We're effectively ignoring stray combinators // and empty selector groups here for forgiving selector lists. // It doesn't seem right, but this is how tainted browsers do it. - if (aIsForgiving) { + if (aFlags & SelectorParsingFlags::eIsForgiving) { return false; } if (selector->mNext) { -- cgit v1.2.3 From 40c9c92116ffbf26995c0186beef792f9f814bba Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 17:19:18 +0800 Subject: Issue #2137 - Part 3: Don't always use the internal pseudo-class for handling negations --- layout/style/nsCSSParser.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'layout') diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 38e2759bb4..1c41e80156 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -6637,11 +6637,21 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, while (negations->mNegations) { negations = negations->mNegations; } - // XXX: Use a special internal-only pseudo-class to handle selector lists. - // TODO: This should only happen if we don't have a simple selector. - nsCSSSelector* newSel = new nsCSSSelector(); - newSel->AddPseudoClass(CSSPseudoClassType::mozAnyPrivate, slist.forget()); - negations->mNegations = newSel; + // XXX: Use a special internal-only pseudo-class to handle selector lists + // if we have: (a) a complex selector, (b) nested negation pseudo-class, + // or (c) more than one selector argument in the list. + if (slist->mNext || + slist->mSelectors->mNext || + slist->mSelectors->mNegations) { + nsCSSSelector* newSel = new nsCSSSelector(); + newSel->AddPseudoClass(CSSPseudoClassType::mozAnyPrivate, + slist.forget()); + negations->mNegations = newSel; + } else { + // Otherwise, steal the first selector and add it directly to the + // end of aSelector.mNegations. + negations->mNegations = (slist.forget())->mSelectors; + } } else { // Add the pseudo with the selector list parameter aSelector.AddPseudoClass(aType, slist.forget()); -- cgit v1.2.3 From 18c6aa0252602789d50c7eef94b2356e44d8e468 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 19:09:22 +0800 Subject: Issue #2137 - Part 4: Fix namespace regression --- layout/style/nsCSSParser.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'layout') diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 1c41e80156..8c4f16f24d 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -114,11 +114,12 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackListFlags) * Additional information about a selector being parsed. */ enum class SelectorParsingFlags { - eNone = 0, - eIsNegated = 1 << 0, - eIsForgiving = 1 << 1, - eDisallowCombinators = 1 << 2, - eDisallowPseudoElements = 1 << 3 + eNone = 0, + eIsNegated = 1 << 0, + eIsForgiving = 1 << 1, + eDisallowCombinators = 1 << 2, + eDisallowPseudoElements = 1 << 3, + eInheritNamespace = 1 << 4 }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorParsingFlags) @@ -5792,7 +5793,9 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, } } else { - SetDefaultNamespaceOnSelector(aSelector); + if (!(aFlags & SelectorParsingFlags::eInheritNamespace)) { + SetDefaultNamespaceOnSelector(aSelector); + } } if (aFlags & SelectorParsingFlags::eIsNegated) { @@ -6617,6 +6620,8 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, aFlags |= SelectorParsingFlags::eIsForgiving; } else if (isSingleSelector || aType == CSSPseudoClassType::mozAny) { aFlags |= SelectorParsingFlags::eDisallowCombinators; + } else if (aType == CSSPseudoClassType::negation) { + aFlags |= SelectorParsingFlags::eInheritNamespace; } aFlags |= SelectorParsingFlags::eDisallowPseudoElements; -- cgit v1.2.3