diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 2f08d88f14..f4a3d05e5b 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -920,9 +920,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); @@ -951,7 +955,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 23620e35af..26d04a2d82 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, #ifdef MOZ_XUL isTree || #endif - 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); } @@ -6583,8 +6584,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, @@ -6612,22 +6611,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)) { @@ -17891,6 +17903,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 ebc3dad63e..692419b566 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1952,6 +1952,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, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 9478c92bd8..10bd5aaf46 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2551,6 +2551,9 @@ pref("layout.css.prefixes.webkit", true); // pref is set to false.) pref("layout.css.prefixes.device-pixel-ratio-webkit", false); +// Is the legacy negation pseudo-class behavior enabled? +pref("layout.css.legacy-negation-pseudo.enabled", false); + // Is support for the :is() and :where() selectors enabled? pref("layout.css.is-where-pseudo.enabled", true);