From 2249fbdf495bb0f018445df0db1dcf0d06f6a1d5 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Fri, 10 Mar 2023 11:44:28 +0100 Subject: [PATCH 01/10] Issue #2135 - Follow-up: Check for aElement not being null This was introduced to Make work in shadow trees. eElement can, however, be `null` here and if so, it would crash. --- layout/style/Loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 3d1e4826d5..48ca1739dc 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -2118,7 +2118,7 @@ Loader::LoadStyleLink(nsIContent* aElement, PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate, *aIsExplicitlyEnabled); - if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) { + if (aElement && aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) { aElement->GetContainingShadow()->InsertSheet(sheet, aElement); } else { rv = InsertSheetInDoc(sheet, aElement, mDocument); From 82fa9fb80b22d10cb7c606ef8679091678f9c969 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 00:20:44 +0800 Subject: [PATCH 02/10] 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 ++++ modules/libpref/init/all.js | 3 ++ 7 files changed, 83 insertions(+), 35 deletions(-) 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); From 3bb3c193d09c7c56cddb9beaf880fd5356018d4d Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 12:18:39 +0800 Subject: [PATCH 03/10] Issue #2137 - Part 2: Implement SelectorParsingFlags and use it to pass info around --- layout/style/nsCSSParser.cpp | 177 ++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 84 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 26d04a2d82..557722ad12 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 @@ -759,21 +771,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); @@ -783,24 +795,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, @@ -2332,7 +2342,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 @@ -5448,7 +5458,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); @@ -5485,12 +5495,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(); @@ -5520,16 +5529,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; @@ -5544,7 +5554,7 @@ CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, } } - if (!aIsForgiving) { + if (!(aFlags & SelectorParsingFlags::eIsForgiving)) { REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); UngetToken(); break; @@ -5568,13 +5578,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; } @@ -5610,7 +5621,7 @@ CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList, bool aIsForgiving, return false; } - if (aIsForgiving && aDisallowCombinators) { + if (aFlags & SelectorParsingFlags::eDisallowCombinators) { return false; } } @@ -5669,9 +5680,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 @@ -5778,7 +5789,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(); } @@ -6032,17 +6043,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) +CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + SelectorParsingFlags aFlags, + nsIAtom** aPseudoElement, + nsAtomList** aPseudoElementArgs, + CSSPseudoElementType* aPseudoElementType) { - NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs), + 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| @@ -6310,9 +6333,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; @@ -6345,9 +6371,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); @@ -6358,7 +6383,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); @@ -6588,22 +6613,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(')') } @@ -6623,20 +6648,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()); } @@ -6658,8 +6669,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); @@ -6673,15 +6683,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 @@ -6737,7 +6746,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) { From b257a71cce64b692e1c644c5d61c9d68dc2f231c Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 17:19:18 +0800 Subject: [PATCH 04/10] 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(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 557722ad12..b419d5a275 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -6642,11 +6642,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()); From ef36c56593f034175650cbd1b98c8d3f7f74e8f5 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Fri, 10 Mar 2023 19:09:22 +0800 Subject: [PATCH 05/10] Issue #2137 - Part 4: Fix namespace regression --- layout/style/nsCSSParser.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index b419d5a275..fee2445ec7 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) @@ -5786,7 +5787,9 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, } } else { - SetDefaultNamespaceOnSelector(aSelector); + if (!(aFlags & SelectorParsingFlags::eInheritNamespace)) { + SetDefaultNamespaceOnSelector(aSelector); + } } if (aFlags & SelectorParsingFlags::eIsNegated) { @@ -6622,6 +6625,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; From 18e693464db4ad123afc59c06aa0b8f5eaad94d8 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 11 Mar 2023 00:02:28 +0800 Subject: [PATCH 06/10] Issue #2135 - Destroy the host frame and restyle when there are content changes This is likely inefficient, but I haven't found a way other than this to ensure that the host frame is updated if the inserted/removed content isn't slotted. I'm assuming that Firefox is handling this somewhere else (and may have been even moved to Stylo), but was mentioned in an m-c bug we haven't seen yet. In fact, this is actually similar to how we handled elements passed to ContentInserted before landing e31ed5b07466d4a579fe4b025f97c971003fbc3f. --- dom/base/ShadowRoot.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index e31b97262c..92d5a2f2a9 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -578,6 +578,13 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument, if (slot && slot->GetContainingShadow() == this && slot->AssignedNodes().IsEmpty()) { slot->EnqueueSlotChangeEvent(); + return; + } + + // XXX: The following makes the host destroy its frames and force a + // restyle for cases where the content isn't slotted. + if (aContainer == this) { + DistributionChanged(); } } @@ -614,6 +621,13 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument, if (slot && slot->GetContainingShadow() == this && slot->AssignedNodes().IsEmpty()) { slot->EnqueueSlotChangeEvent(); + return; + } + + // XXX: The following makes the host destroy its frames and force a + // restyle for cases where the content isn't slotted. + if (aContainer == this) { + DistributionChanged(); } } From 2be0261d545c84a2886ef31b573f92c9573e59f2 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 11 Mar 2023 12:08:20 +0800 Subject: [PATCH 07/10] Issue #2078 - Follow-up: Ensure empty selector lists aren't iterated when serialized Really odd that this wasn't caught/doesn't cause any issues on MSVC/unoptimized GCC. Must've been luck, I guess. --- layout/style/StyleRule.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index f4a3d05e5b..0ae939098a 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -1048,6 +1048,10 @@ nsCSSSelectorList::ToString(nsAString& aResult, CSSStyleSheet* aSheet) { aResult.Truncate(); nsCSSSelectorList *p = this; + // Don't append anything if we're an empty selector list. + if (!mSelectors) { + return; + } for (;;) { p->mSelectors->ToString(aResult, aSheet, true); p = p->mNext; From 76cd3dfac6754f0db75e0316213246d71d294aa4 Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Sat, 11 Mar 2023 15:58:27 -0500 Subject: [PATCH 08/10] [Basilisk] Add useragent override for instagram port of MoonchildProductions/Pale-Moon commit d641959f15cb1172f6f39130b579ccf6111a6f2f and 6d41a6139440a9c396308deca1725b0db823e127 --- application/basilisk/branding/shared/uaoverrides.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/application/basilisk/branding/shared/uaoverrides.inc b/application/basilisk/branding/shared/uaoverrides.inc index d0cb13b923..5e01a8f9f0 100644 --- a/application/basilisk/branding/shared/uaoverrides.inc +++ b/application/basilisk/branding/shared/uaoverrides.inc @@ -24,6 +24,7 @@ pref("@GUAO_PREF@.aol.com","Mozilla/5.0 (%OS_SLICE% rv:@GK_VERSION@) @GK_SLICE@ pref("@GUAO_PREF@.bing.com","Mozilla/5.0 (%OS_SLICE% rv:@GK_VERSION@) @GK_SLICE@ @FX_SLICE@ (Basilisk)"); pref("@GUAO_PREF@.chase.com","Mozilla/5.0 (%OS_SLICE% rv:79.0) @GK_SLICE@ Firefox/79.0"); pref("@GUAO_PREF@.dropbox.com","Mozilla/5.0 (%OS_SLICE% rv:68.9) @GK_SLICE@ Firefox/68.9 (Basilisk)"); +pref("@GUAO_PREF@.instagram.com","Mozilla/5.0 (%OS_SLICE% rv:68.0) @GK_SLICE@ Firefox/68.0"); pref("@GUAO_PREF@.google.com","Mozilla/5.0 (%OS_SLICE% rv:71.0) @GK_SLICE@ Firefox/71.0 @APP_SLICE@"); pref("@GUAO_PREF@.googlevideos.com","Mozilla/5.0 (%OS_SLICE% rv:38.9) @GK_SLICE@ @UXP_VERSION_SLICE@ Firefox/38.9 @APP_SLICE@"); pref("@GUAO_PREF@.gstatic.com","Mozilla/5.0 (%OS_SLICE% rv:71.0) @GK_SLICE@ Firefox/71.0 @APP_SLICE@"); From 4abf537287576326335d11e67d2a7386fc2378be Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Sat, 11 Mar 2023 16:03:42 -0500 Subject: [PATCH 09/10] [Basilisk] Port Pale Moon version of Tab fading Backport of roytam1/UXP commit https://github.com/roytam1/UXP/commit/2a32b18f465a8d068d1b1866d027374b4706cd5a.patch --- application/basilisk/app/profile/basilisk.js | 4 ++++ application/basilisk/base/content/browser.css | 10 +++++++++ .../basilisk/base/content/tabbrowser.xml | 22 ++++++++++++++++--- .../components/sessionstore/SessionStore.jsm | 3 +++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/application/basilisk/app/profile/basilisk.js b/application/basilisk/app/profile/basilisk.js index e332e1c624..c4a1f0f266 100644 --- a/application/basilisk/app/profile/basilisk.js +++ b/application/basilisk/app/profile/basilisk.js @@ -418,6 +418,10 @@ pref("browser.tabs.dontfocusfordialogs", true); // window is enabled. pref("browser.tabs.allowTabDetach", true); +// Whether to fade tab labels instead of using ellipses when cutting off +// long page titles. +pref("browser.tabs.fadeLabels", true); + pref("browser.ctrlTab.previews", false); // By default, do not export HTML at shutdown. diff --git a/application/basilisk/base/content/browser.css b/application/basilisk/base/content/browser.css index 776cea1104..c1af9d1739 100644 --- a/application/basilisk/base/content/browser.css +++ b/application/basilisk/base/content/browser.css @@ -187,6 +187,16 @@ tabbrowser { transition: transform 200ms ease-out; } +@supports -moz-bool-pref("browser.tabs.fadeLabels") { + .tabbrowser-tab .tab-text[_is_cropped] { + mask-image: linear-gradient(to left, transparent, black 2em); + } + + .tabbrowser-tab .tab-text[_is_cropped]:-moz-locale-dir(rtl) { + mask-image: linear-gradient(to right, transparent, black 2em); + } +} + .new-tab-popup, #alltabs-popup { -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup"); diff --git a/application/basilisk/base/content/tabbrowser.xml b/application/basilisk/base/content/tabbrowser.xml index fec7c9ecca..0d1ad238bc 100644 --- a/application/basilisk/base/content/tabbrowser.xml +++ b/application/basilisk/base/content/tabbrowser.xml @@ -1397,7 +1397,11 @@ @@ -1442,6 +1446,10 @@ title = this.mStringBundle.getString("tabs.emptyTabTitle"); } + if (Services.prefs.getBoolPref("browser.tabs.fadeLabels")) { + crop = "clip"; + } + if (aTab.label == title && aTab.crop == crop) return false; @@ -2128,7 +2136,11 @@ t.setAttribute("label", aURI); } - t.setAttribute("crop", "end"); + if (Services.prefs.getBoolPref("browser.tabs.fadeLabels")) { + t.setAttribute("crop", "clip"); + } else { + t.setAttribute("crop", "end"); + } t.setAttribute("onerror", "this.removeAttribute('image');"); if (aSkipBackgroundNotify) { @@ -5191,7 +5203,11 @@ var tab = this.firstChild; tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle"); - tab.setAttribute("crop", "end"); + if (Services.prefs.getBoolPref("browser.tabs.fadeLabels")) { + tab.setAttribute("crop", "clip"); + } else { + tab.setAttribute("crop", "end"); + } tab.setAttribute("onerror", "this.removeAttribute('image');"); window.addEventListener("resize", this, false); diff --git a/application/basilisk/components/sessionstore/SessionStore.jsm b/application/basilisk/components/sessionstore/SessionStore.jsm index 9d9ef55939..fdde08730a 100644 --- a/application/basilisk/components/sessionstore/SessionStore.jsm +++ b/application/basilisk/components/sessionstore/SessionStore.jsm @@ -849,6 +849,9 @@ var SessionStoreInternal = { tab.label = activePageData.url; tab.crop = "center"; } + if (this._prefBranch.getBoolPref("tabs.fadeLabels")) { + tab.crop = "clip"; + } } else if (tab.hasAttribute("customizemode")) { win.gCustomizeMode.setTab(tab); } From 78bb7159ee66b85d9747d82873c6946b820942ce Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Sat, 11 Mar 2023 16:05:45 -0500 Subject: [PATCH 10/10] [Basilisk] add tab caption fading to CtrlTab backport of roytam1/UXP commit add tab caption fading to CtrlTab --- application/basilisk/themes/shared/ctrlTab.inc.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/application/basilisk/themes/shared/ctrlTab.inc.css b/application/basilisk/themes/shared/ctrlTab.inc.css index 1205d04995..30ffb3b4fc 100644 --- a/application/basilisk/themes/shared/ctrlTab.inc.css +++ b/application/basilisk/themes/shared/ctrlTab.inc.css @@ -42,6 +42,16 @@ border-radius: .5em; } +@supports -moz-bool-pref("browser.tabs.fadeLabels") { + .ctrlTab-preview-inner label[_is_cropped] { + mask-image: linear-gradient(to left, transparent, black 2em); + } + + .ctrlTab-preview-inner label[_is_cropped]:-moz-locale-dir(rtl) { + mask-image: linear-gradient(to right, transparent, black 2em); + } +} + .ctrlTab-preview:not(#ctrlTab-showAll) > * > .ctrlTab-preview-inner { margin: -10px -10px 0; }