From e8f840bf2fd31f29220cd26e581f4698abcb7484 Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Sat, 26 Nov 2022 11:17:02 +0200 Subject: [PATCH 01/13] MailNews: Re-check availability of OAuth2 when hostname fields modified. This is for those cases where the hostnme of the email address is not the actual hostname of the e-mail service (e.g. GMail with a custom domain). This should resolve issue athenian200/epyrus#53. Signed-off-by: Mavridis Philippe --- .../content/accountcreation/emailWizard.js | 74 ++++++++++++------- .../content/accountcreation/emailWizard.xul | 2 + 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/mailnews/base/prefs/content/accountcreation/emailWizard.js b/mailnews/base/prefs/content/accountcreation/emailWizard.js index 2e4a44e2a0..5a594b16f7 100644 --- a/mailnews/base/prefs/content/accountcreation/emailWizard.js +++ b/mailnews/base/prefs/content/accountcreation/emailWizard.js @@ -1006,6 +1006,50 @@ EmailConfigWizard.prototype = this.validateManualEditComplete(); }, + /** + * Checks whether OAuth2 is supported by current incoming hostname. + */ + checkIncomingSupportsOAuth2 : function() { +#ifdef MOZ_MAILNEWS_OAUTH2 + var config = this.getUserConfig(); + + // If the hostname supports OAuth2 and imap is enabled, enable OAuth2. + let iDetails = OAuth2Providers.getHostnameDetails(config.incoming.hostname); + gEmailWizardLogger.info("OAuth2 details for incoming hostname " + + config.incoming.hostname + " is " + iDetails); + e("in-authMethod-oauth2").hidden = !(iDetails && e("incoming_protocol").value == 1); + if (!e("in-authMethod-oauth2").hidden) { + config.oauthSettings = {}; + [config.oauthSettings.issuer, config.oauthSettings.scope] = iDetails; + // oauthsettings are not stored nor changable in the user interface, so just + // store them in the base configuration. + this._currentConfig.oauthSettings = config.oauthSettings; + } +#endif + }, + + /** + * Checks whether OAuth2 is supported by current outgoing hostname. + */ + checkOutgoingSupportsOAuth2 : function() { +#ifdef MOZ_MAILNEWS_OAUTH2 + var config = this.getUserConfig(); + + // If the hostname supports OAuth2 and imap is enabled, enable OAuth2. + let oDetails = OAuth2Providers.getHostnameDetails(config.outgoing.hostname); + gEmailWizardLogger.info("OAuth2 details for outgoing hostname " + + config.outgoing.hostname + " is " + oDetails); + e("out-authMethod-oauth2").hidden = !oDetails; + if (!e("out-authMethod-oauth2").hidden) { + config.oauthSettings = {}; + [config.oauthSettings.issuer, config.oauthSettings.scope] = oDetails; + // oauthsettings are not stored nor changable in the user interface, so just + // store them in the base configuration. + this._currentConfig.oauthSettings = config.oauthSettings; + } +#endif + }, + /** * Fills the manual edit textfields with the provided config. * @param config {AccountConfig} The config to present to user @@ -1034,20 +1078,7 @@ EmailConfigWizard.prototype = } this.fillPortDropdown(config.incoming.type); -#ifdef MOZ_MAILNEWS_OAUTH2 - // If the hostname supports OAuth2 and imap is enabled, enable OAuth2. - let iDetails = OAuth2Providers.getHostnameDetails(config.incoming.hostname); - gEmailWizardLogger.info("OAuth2 details for incoming hostname " + - config.incoming.hostname + " is " + iDetails); - e("in-authMethod-oauth2").hidden = !(iDetails && e("incoming_protocol").value == 1); - if (!e("in-authMethod-oauth2").hidden) { - config.oauthSettings = {}; - [config.oauthSettings.issuer, config.oauthSettings.scope] = iDetails; - // oauthsettings are not stored nor changable in the user interface, so just - // store them in the base configuration. - this._currentConfig.oauthSettings = config.oauthSettings; - } -#endif + this.checkIncomingSupportsOAuth2(); // outgoing server e("outgoing_hostname").value = config.outgoing.hostname; @@ -1065,20 +1096,7 @@ EmailConfigWizard.prototype = this.adjustOutgoingPortToSSLAndProtocol(config); } -#ifdef MOZ_MAILNEWS_OAUTH2 - // If the hostname supports OAuth2 and imap is enabled, enable OAuth2. - let oDetails = OAuth2Providers.getHostnameDetails(config.outgoing.hostname); - gEmailWizardLogger.info("OAuth2 details for outgoing hostname " + - config.outgoing.hostname + " is " + oDetails); - e("out-authMethod-oauth2").hidden = !oDetails; - if (!e("out-authMethod-oauth2").hidden) { - config.oauthSettings = {}; - [config.oauthSettings.issuer, config.oauthSettings.scope] = oDetails; - // oauthsettings are not stored nor changable in the user interface, so just - // store them in the base configuration. - this._currentConfig.oauthSettings = config.oauthSettings; - } -#endif + this.checkOutgoingSupportsOAuth2(); // populate fields even if existingServerKey, in case user changes back if (config.outgoing.existingServerKey) { diff --git a/mailnews/base/prefs/content/accountcreation/emailWizard.xul b/mailnews/base/prefs/content/accountcreation/emailWizard.xul index 261545671e..f61cd509ea 100644 --- a/mailnews/base/prefs/content/accountcreation/emailWizard.xul +++ b/mailnews/base/prefs/content/accountcreation/emailWizard.xul @@ -276,6 +276,7 @@ From 453b715ef6fe4e0e4b8b58c276be90725362fc3c Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Tue, 3 Jan 2023 20:52:31 +0800 Subject: [PATCH 02/13] Issue #2078 - Part 1: Update CSS rule processor to handle :is() and :where() CSS pseudo-classes This modifies selector list parsing to accommodate being "forgiving". Aliases for the :is selector's former names were also included. Note that the older and prefixed variant `-moz-any` remains unforgiving. --- layout/style/StyleRule.cpp | 2 +- layout/style/nsCSSParser.cpp | 83 ++++++++++++++++++++--------- layout/style/nsCSSPseudoClassList.h | 6 ++- layout/style/nsCSSPseudoClasses.h | 13 +++-- layout/style/nsCSSRuleProcessor.cpp | 31 ++++++++--- modules/libpref/init/all.js | 3 ++ 6 files changed, 103 insertions(+), 35 deletions(-) diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 8fe19bb34a..61d0d659e4 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -149,7 +149,7 @@ nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType, mNext(nullptr) { NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType) || - nsCSSPseudoClasses::HasOptionalSelectorListArg(aType), + nsCSSPseudoClasses::HasOptionalSelectorListArg(aType), "unexpected pseudo-class"); NS_ASSERTION(aSelectorList, "selector list expected"); MOZ_COUNT_CTOR(nsPseudoClassList); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 801e9356d5..d793f453ff 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -780,7 +780,8 @@ protected: CSSPseudoClassType aType); nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, - CSSPseudoClassType aType); + CSSPseudoClassType aType, + bool aIsForgiving); nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask, nsCSSSelector& aSelector); @@ -788,9 +789,13 @@ protected: // 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 ParseSelectorGroup(nsCSSSelectorList*& aListHead); - bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator); + char16_t aStopChar, + bool aIsForgiving); + bool ParseSelectorGroup(nsCSSSelectorList*& aListHead, + bool aIsForgiving); + bool ParseSelector(nsCSSSelectorList* aList, + char16_t aPrevCombinator, + bool aIsForgiving); enum { eParseDeclaration_InBraces = 1 << 0, @@ -2321,7 +2326,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)); + bool success = ParseSelectorList(*aSelectorList, char16_t(0), false); // We deliberately do not call OUTPUT_ERROR here, because all our // callers map a failure return to a JS exception, and if that JS @@ -5403,7 +5408,7 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, nsCSSSelectorList* slist = nullptr; uint32_t linenum, colnum; if (!GetNextTokenLocation(true, &linenum, &colnum) || - !ParseSelectorList(slist, char16_t('{'))) { + !ParseSelectorList(slist, char16_t('{'), false)) { REPORT_UNEXPECTED(PEBadSelectorRSIgnored); OUTPUT_ERROR(); SkipRuleSet(aInsideBraces); @@ -5439,13 +5444,20 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, bool CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, - char16_t aStopChar) + char16_t aStopChar, + bool aIsForgiving) { nsCSSSelectorList* list = nullptr; - if (! ParseSelectorGroup(list)) { - // must have at least one selector group - aListHead = nullptr; - return false; + if (! ParseSelectorGroup(list, aIsForgiving)) { + if (aIsForgiving) { + // Initialize to an empty list if the first selector group was invalid + // and we're a forgiving selector list. + list = new nsCSSSelectorList(); + } else { + // must have at least one selector group + aListHead = nullptr; + return false; + } } NS_ASSERTION(nullptr != list, "no selector list"); aListHead = list; @@ -5467,7 +5479,11 @@ CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, if (',' == tk->mSymbol) { nsCSSSelectorList* newList = nullptr; // Another selector group must follow - if (! ParseSelectorGroup(newList)) { + if (! ParseSelectorGroup(newList, aIsForgiving)) { + // Ignore invalid selectors if we're a forgiving selector list. + if (aIsForgiving) { + continue; + } break; } // add new list to the end of the selector list @@ -5479,9 +5495,12 @@ CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, return true; } } - REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); - UngetToken(); - break; + + if (!aIsForgiving) { + REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); + UngetToken(); + break; + } } delete aListHead; @@ -5501,13 +5520,13 @@ static bool IsUniversalSelector(const nsCSSSelector& aSelector) } bool -CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList) +CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList, bool aIsForgiving) { char16_t combinator = 0; nsAutoPtr list(new nsCSSSelectorList()); for (;;) { - if (!ParseSelector(list, combinator)) { + if (!ParseSelector(list, combinator, aIsForgiving)) { return false; } @@ -6140,8 +6159,11 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, else { MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType), "unexpected pseudo with function token"); + bool isForgiving = + nsCSSPseudoClasses::HasForgivingSelectorListArg(pseudoClassType); parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector, - pseudoClassType); + pseudoClassType, + isForgiving); } if (eSelectorParsingStatus_Continue != parsingStatus) { if (eSelectorParsingStatus_Error == parsingStatus) { @@ -6515,18 +6537,24 @@ CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, // CSSParserImpl::nsSelectorParsingStatus CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, - CSSPseudoClassType aType) + CSSPseudoClassType aType, + bool aIsForgiving) { nsAutoPtr slist; - if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) { + if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'), aIsForgiving)) { return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') } - // Check that none of the selectors in the list have combinators or - // pseudo-elements. for (nsCSSSelectorList *l = slist; l; l = l->mNext) { nsCSSSelector *s = l->mSelectors; - if (s->mNext || s->IsPseudoElement()) { + if (s == nullptr) { + MOZ_ASSERT(aIsForgiving, + "unexpected empty selector in unforgiving selector list"); + break; + } + // Check that none of the selectors in the list have combinators or + // pseudo-elements. + if ((!aIsForgiving && s->mNext) || s->IsPseudoElement()) { return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') } } @@ -6550,7 +6578,8 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, */ bool CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, - char16_t aPrevCombinator) + char16_t aPrevCombinator, + bool aIsForgiving) { if (! GetToken(true)) { REPORT_UNEXPECTED_EOF(PESelectorEOF); @@ -6624,6 +6653,12 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, } if (!dataMask) { + // 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) { + return false; + } if (selector->mNext) { REPORT_UNEXPECTED(PESelectorGroupExtraCombinator); } else { diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index c57c28bc0b..740ebcc422 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -74,7 +74,11 @@ CSS_PSEUDO_CLASS(mozEmptyExceptChildrenWithLocalname, ":-moz-empty-except-childr CSS_PSEUDO_CLASS(lang, ":lang", 0, "") CSS_PSEUDO_CLASS(mozBoundElement, ":-moz-bound-element", 0, "") CSS_PSEUDO_CLASS(root, ":root", 0, "") -CSS_PSEUDO_CLASS(any, ":-moz-any", 0, "") +CSS_PSEUDO_CLASS(mozAny, ":-moz-any", 0, "") +CSS_PSEUDO_CLASS(is, ":is", 0, "layout.css.is-where-pseudo.enabled") +CSS_PSEUDO_CLASS(matches, ":matches", 0, "layout.css.is-where-pseudo.enabled") +CSS_PSEUDO_CLASS(any, ":any", 0, "layout.css.is-where-pseudo.enabled") +CSS_PSEUDO_CLASS(where, ":where", 0, "layout.css.is-where-pseudo.enabled") CSS_PSEUDO_CLASS(firstChild, ":first-child", 0, "") CSS_PSEUDO_CLASS(firstNode, ":-moz-first-node", 0, "") diff --git a/layout/style/nsCSSPseudoClasses.h b/layout/style/nsCSSPseudoClasses.h index 05f67b05cd..4a4bbe188c 100644 --- a/layout/style/nsCSSPseudoClasses.h +++ b/layout/style/nsCSSPseudoClasses.h @@ -58,10 +58,17 @@ public: static Type GetPseudoType(nsIAtom* aAtom, EnabledState aEnabledState); static bool HasStringArg(Type aType); static bool HasNthPairArg(Type aType); + static bool HasForgivingSelectorListArg(Type aType) { + return aType == Type::is || + aType == Type::matches || + aType == Type::any || + aType == Type::where; + } static bool HasSelectorListArg(Type aType) { - return aType == Type::any || - aType == Type::host || - aType == Type::hostContext; + return HasForgivingSelectorListArg(aType) || + aType == Type::mozAny || + aType == Type::host || + aType == Type::hostContext; } static bool HasOptionalSelectorListArg(Type aType) { return aType == Type::host; diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index dbd0e099b0..5b259ee991 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1643,7 +1643,8 @@ StateSelectorMatches(Element* aElement, static bool AnySelectorInArgListMatches(Element* aElement, nsPseudoClassList* aList, NodeMatchContext& aNodeMatchContext, - TreeMatchContext& aTreeMatchContext); + TreeMatchContext& aTreeMatchContext, + bool aIsForgiving = false); static bool StateSelectorMatches(Element* aElement, @@ -1907,7 +1908,21 @@ static bool SelectorMatches(Element* aElement, } break; + case CSSPseudoClassType::is: + case CSSPseudoClassType::matches: case CSSPseudoClassType::any: + case CSSPseudoClassType::where: + { + if (!AnySelectorInArgListMatches(aElement, pseudoClass, + aNodeMatchContext, + aTreeMatchContext, + true)) { + return false; + } + } + break; + + case CSSPseudoClassType::mozAny: { if (!AnySelectorInArgListMatches(aElement, pseudoClass, aNodeMatchContext, @@ -2376,11 +2391,17 @@ static bool SelectorMatches(Element* aElement, static bool AnySelectorInArgListMatches(Element* aElement, nsPseudoClassList* aList, NodeMatchContext& aNodeMatchContext, - TreeMatchContext& aTreeMatchContext) + TreeMatchContext& aTreeMatchContext, + bool aIsForgiving) { nsCSSSelectorList *l; for (l = aList->u.mSelectors; l; l = l->mNext) { nsCSSSelector *s = l->mSelectors; + if (s == nullptr) { + MOZ_ASSERT(aIsForgiving, + "unexpected empty selector in unforgiving selector list"); + return false; + } MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(), "parser failed"); if (SelectorMatches( @@ -3517,12 +3538,10 @@ AddSelector(RuleCascadeData* aCascade, } } - // Recur through any :-moz-any or :host-context selectors + // Recur through any pseudo-class that has a selector list argument. for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList; pseudoClass; pseudoClass = pseudoClass->mNext) { - if (pseudoClass->mType == CSSPseudoClassType::any || - pseudoClass->mType == CSSPseudoClassType::host || - pseudoClass->mType == CSSPseudoClassType::hostContext) { + if (nsCSSPseudoClasses::HasSelectorListArg(pseudoClass->mType)) { for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) { nsCSSSelector *s = l->mSelectors; if (!AddSelector(aCascade, aSelectorInTopLevel, s, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 535ca1c93b..88ffb6ca7f 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2553,6 +2553,9 @@ pref("layout.css.prefixes.webkit", true); // pref is set to false.) pref("layout.css.prefixes.device-pixel-ratio-webkit", false); +// Is support for the :is() and :where() selectors enabled? +pref("layout.css.is-where-pseudo.enabled", true); + // Is support for the :scope selector enabled? pref("layout.css.scope-pseudo.enabled", true); From 0648ec64bb227cabd31e8d412f0f54c59e314534 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 18 Feb 2023 11:05:36 +0800 Subject: [PATCH 03/13] Issue #2078 - Part 2: Rename nsPseudoClassList->u.mSelectors to mSelectorList This makes it clear that we're accessing a selector list (nsCSSSelectorList), which is different from the selectors (nsCSSSelector) contained inside a selector list. Previously, both were confusingly referred to as mSelectors. --- layout/style/StyleRule.cpp | 10 +++++----- layout/style/StyleRule.h | 2 +- layout/style/nsCSSRuleProcessor.cpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 61d0d659e4..f885bda64b 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -153,7 +153,7 @@ nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType, "unexpected pseudo-class"); NS_ASSERTION(aSelectorList, "selector list expected"); MOZ_COUNT_CTOR(nsPseudoClassList); - u.mSelectors = aSelectorList; + u.mSelectorList = aSelectorList; } nsPseudoClassList* @@ -170,7 +170,7 @@ nsPseudoClassList::Clone(bool aDeep) const NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType), "unexpected pseudo-class"); // This constructor adopts its selector list argument. - result = new nsPseudoClassList(mType, u.mSelectors->Clone()); + result = new nsPseudoClassList(mType, u.mSelectorList->Clone()); } if (aDeep) @@ -199,7 +199,7 @@ nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) cons } else { NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType), "unexpected pseudo-class"); - n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf); + n += p->u.mSelectorList->SizeOfIncludingThis(aMallocSizeOf); } p = p->mNext; } @@ -210,7 +210,7 @@ nsPseudoClassList::~nsPseudoClassList(void) { MOZ_COUNT_DTOR(nsPseudoClassList); if (nsCSSPseudoClasses::HasSelectorListArg(mType)) { - delete u.mSelectors; + delete u.mSelectorList; } else if (u.mMemory) { free(u.mMemory); } @@ -927,7 +927,7 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType), "unexpected pseudo-class"); nsString tmp; - list->u.mSelectors->ToString(tmp, aSheet); + list->u.mSelectorList->ToString(tmp, aSheet); aString.Append(tmp); } aString.Append(char16_t(')')); diff --git a/layout/style/StyleRule.h b/layout/style/StyleRule.h index dbf4e08406..d619b5090b 100644 --- a/layout/style/StyleRule.h +++ b/layout/style/StyleRule.h @@ -77,7 +77,7 @@ public: void* mMemory; // mString and mNumbers use moz_xmalloc/free char16_t* mString; int32_t* mNumbers; - nsCSSSelectorList* mSelectors; + nsCSSSelectorList* mSelectorList; } u; CSSPseudoClassType mType; nsPseudoClassList* mNext; diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 5b259ee991..f15ab9de5c 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -2395,7 +2395,7 @@ static bool AnySelectorInArgListMatches(Element* aElement, bool aIsForgiving) { nsCSSSelectorList *l; - for (l = aList->u.mSelectors; l; l = l->mNext) { + for (l = aList->u.mSelectorList; l; l = l->mNext) { nsCSSSelector *s = l->mSelectors; if (s == nullptr) { MOZ_ASSERT(aIsForgiving, @@ -2422,7 +2422,7 @@ HasPseudoClassSelectorArgsWithCombinators(nsCSSSelector* aSelector) { for (nsPseudoClassList* p = aSelector->mPseudoClassList; p; p = p->mNext) { if (nsCSSPseudoClasses::HasSelectorListArg(p->mType)) { - for (nsCSSSelectorList* l = p->u.mSelectors; l; l = l->mNext) { + for (nsCSSSelectorList* l = p->u.mSelectorList; l; l = l->mNext) { if (l->mSelectors->mNext) { return true; } @@ -3542,7 +3542,7 @@ AddSelector(RuleCascadeData* aCascade, for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList; pseudoClass; pseudoClass = pseudoClass->mNext) { if (nsCSSPseudoClasses::HasSelectorListArg(pseudoClass->mType)) { - for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) { + for (nsCSSSelectorList *l = pseudoClass->u.mSelectorList; l; l = l->mNext) { nsCSSSelector *s = l->mSelectors; if (!AddSelector(aCascade, aSelectorInTopLevel, s, aRightmostSelector)) { From 14e8922dca72e69d4841ed6a421954f204d50fee Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 18 Feb 2023 19:29:42 +0800 Subject: [PATCH 04/13] Issue #2078 - Part 3: Rename nsCSSRuleProcessor::SelectorListMatches to RestrictedSelectorListMatches This is in preparation for merging the logic of RestrictedSelectorListMatches and AnySelectorInArgListMatches. --- dom/base/Element.cpp | 11 ++++++----- dom/base/nsINode.cpp | 12 ++++++------ layout/inspector/inDOMUtils.cpp | 7 ++++--- layout/style/nsCSSRuleProcessor.cpp | 10 +++++----- layout/style/nsCSSRuleProcessor.h | 6 +++--- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index f4ab994e87..99120db433 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3438,9 +3438,9 @@ Element::Closest(const nsAString& aSelector, ErrorResult& aResult) matchingContext.AddScopeElement(this); for (nsINode* node = this; node; node = node->GetParentNode()) { if (node->IsElement() && - nsCSSRuleProcessor::SelectorListMatches(node->AsElement(), - matchingContext, - selectorList)) { + nsCSSRuleProcessor::RestrictedSelectorListMatches(node->AsElement(), + matchingContext, + selectorList)) { return node->AsElement(); } } @@ -3464,8 +3464,9 @@ Element::Matches(const nsAString& aSelector, ErrorResult& aError) TreeMatchContext::eNeverMatchVisited); matchingContext.SetHasSpecifiedScope(); matchingContext.AddScopeElement(this); - return nsCSSRuleProcessor::SelectorListMatches(this, matchingContext, - selectorList); + return nsCSSRuleProcessor::RestrictedSelectorListMatches(this, + matchingContext, + selectorList); } static const nsAttrValue::EnumTable kCORSAttributeTable[] = { diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index db8986c4e8..749a3ee4a2 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -2879,9 +2879,9 @@ FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot, // We have an element with the right id and it's a strict descendant // of aRoot. Make sure it really matches the selector. if (!aMatchInfo || - nsCSSRuleProcessor::SelectorListMatches(element, - aMatchInfo->mMatchContext, - aMatchInfo->mSelectorList)) { + nsCSSRuleProcessor::RestrictedSelectorListMatches(element, + aMatchInfo->mMatchContext, + aMatchInfo->mSelectorList)) { aList.AppendElement(element); if (onlyFirstMatch) { return; @@ -2932,9 +2932,9 @@ FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList, cur; cur = cur->GetNextNode(aRoot)) { if (cur->IsElement() && - nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(), - matchingContext, - aSelectorList)) { + nsCSSRuleProcessor::RestrictedSelectorListMatches(cur->AsElement(), + matchingContext, + aSelectorList)) { if (onlyFirstMatch) { aList.AppendElement(cur->AsElement()); return; diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index 800201ce21..f2b52aed21 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -465,7 +465,7 @@ inDOMUtils::SelectorMatchesElement(nsIDOMElement* aElement, } // We have a matching pseudo element, now remove it so we can compare - // directly against |element| when proceeding into SelectorListMatches. + // directly against |element| when proceeding into RestrictedSelectorListMatches. // It's OK to do this - we just cloned sel and nothing else is using it. sel->RemoveRightmostSelector(); } @@ -476,8 +476,9 @@ inDOMUtils::SelectorMatchesElement(nsIDOMElement* aElement, nsRuleWalker::eRelevantLinkUnvisited, element->OwnerDoc(), TreeMatchContext::eNeverMatchVisited); - *aMatches = nsCSSRuleProcessor::SelectorListMatches(element, matchingContext, - sel); + *aMatches = nsCSSRuleProcessor::RestrictedSelectorListMatches(element, + matchingContext, + sel); return NS_OK; } diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index f15ab9de5c..667747b815 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1337,8 +1337,8 @@ struct NodeMatchContext { // node being matched that is itself or an ancestor.) // // Always false when TreeMatchContext::mForStyling is false. (We - // could figure it out for SelectorListMatches, but we're starting - // from the middle of the selector list when doing + // could figure it out for RestrictedSelectorListMatches, but we're + // starting from the middle of the selector list when doing // Has{Attribute,State}DependentStyle, so we can't tell. So when // mForStyling is false, we have to assume we don't know.) const bool mIsRelevantLink; @@ -4094,9 +4094,9 @@ nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext) } /* static */ bool -nsCSSRuleProcessor::SelectorListMatches(Element* aElement, - TreeMatchContext& aTreeMatchContext, - nsCSSSelectorList* aSelectorList) +nsCSSRuleProcessor::RestrictedSelectorListMatches(Element* aElement, + TreeMatchContext& aTreeMatchContext, + nsCSSSelectorList* aSelectorList) { MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle, "mCurrentStyleScope will need to be saved and restored after the " diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index e207a71afe..af077f842b 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -92,9 +92,9 @@ public: * include any pseudo-element selectors. aSelectorList is allowed * to be null; in this case false will be returned. */ - static bool SelectorListMatches(mozilla::dom::Element* aElement, - TreeMatchContext& aTreeMatchContext, - nsCSSSelectorList* aSelectorList); + static bool RestrictedSelectorListMatches(mozilla::dom::Element* aElement, + TreeMatchContext& aTreeMatchContext, + nsCSSSelectorList* aSelectorList); /* * Helper to get the content state for a content node. This may be From 1c1473166f0808321f63f73bda8a6f732737a2c6 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 18 Feb 2023 23:24:08 +0800 Subject: [PATCH 05/13] Issue #2078 - Part 4: Unify selector list matching This should allow pseudo-classes that accept selector lists to properly handle complex selectors. `:visited` is also matched inside selector lists. A previous iteration of this commit had incorrect matching behavior when called from `RestrictedSelectorListMatches`. --- layout/style/nsCSSRuleProcessor.cpp | 169 +++++++++++++++++----------- 1 file changed, 106 insertions(+), 63 deletions(-) diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 667747b815..c8f888e7a1 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1640,11 +1640,20 @@ StateSelectorMatches(Element* aElement, return true; } -static bool AnySelectorInArgListMatches(Element* aElement, - nsPseudoClassList* aList, - NodeMatchContext& aNodeMatchContext, - TreeMatchContext& aTreeMatchContext, - bool aIsForgiving = false); +static bool SelectorListMatches(Element* aElement, + nsCSSSelectorList* aList, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesFlags aSelectorFlags, + bool aIsForgiving = false, + bool aPreventComplexSelectors = false); + +static bool SelectorListMatches(Element* aElement, + nsPseudoClassList* aList, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + bool aIsForgiving = false, + bool aPreventComplexSelectors = false); static bool StateSelectorMatches(Element* aElement, @@ -1913,10 +1922,11 @@ static bool SelectorMatches(Element* aElement, case CSSPseudoClassType::any: case CSSPseudoClassType::where: { - if (!AnySelectorInArgListMatches(aElement, pseudoClass, - aNodeMatchContext, - aTreeMatchContext, - true)) { + if (!SelectorListMatches(aElement, + pseudoClass, + aNodeMatchContext, + aTreeMatchContext, + true)) { return false; } } @@ -1924,9 +1934,15 @@ static bool SelectorMatches(Element* aElement, case CSSPseudoClassType::mozAny: { - if (!AnySelectorInArgListMatches(aElement, pseudoClass, - aNodeMatchContext, - aTreeMatchContext)) { + // XXX: For compatibility, we retain :-moz-any()'s original behavior, + // which is to be unforgiving and reject complex selectors in + // its selector list argument. + if (!SelectorListMatches(aElement, + pseudoClass, + aNodeMatchContext, + aTreeMatchContext, + false, + true)) { return false; } } @@ -1955,9 +1971,10 @@ static bool SelectorMatches(Element* aElement, NodeMatchContext nodeContext(EventStates(), nsCSSRuleProcessor::IsLink(aElement)); - if (AnySelectorInArgListMatches(aElement, pseudoClass, - nodeContext, - aTreeMatchContext)) { + if (SelectorListMatches(aElement, + pseudoClass, + nodeContext, + aTreeMatchContext)) { break; } @@ -1996,9 +2013,10 @@ static bool SelectorMatches(Element* aElement, while (currentElement) { NodeMatchContext nodeContext(EventStates(), nsCSSRuleProcessor::IsLink(currentElement)); - if (AnySelectorInArgListMatches(currentElement, pseudoClass, - nodeContext, - aTreeMatchContext)) { + if (SelectorListMatches(currentElement, + pseudoClass, + nodeContext, + aTreeMatchContext)) { break; } @@ -2388,32 +2406,6 @@ static bool SelectorMatches(Element* aElement, return result; } -static bool AnySelectorInArgListMatches(Element* aElement, - nsPseudoClassList* aList, - NodeMatchContext& aNodeMatchContext, - TreeMatchContext& aTreeMatchContext, - bool aIsForgiving) -{ - nsCSSSelectorList *l; - for (l = aList->u.mSelectorList; l; l = l->mNext) { - nsCSSSelector *s = l->mSelectors; - if (s == nullptr) { - MOZ_ASSERT(aIsForgiving, - "unexpected empty selector in unforgiving selector list"); - return false; - } - MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(), - "parser failed"); - if (SelectorMatches( - aElement, s, aNodeMatchContext, aTreeMatchContext, - SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) { - break; - } - } - - return !!l; -} - #undef STATE_CHECK #ifdef DEBUG @@ -2658,6 +2650,70 @@ SelectorMatchesTree(Element* aPrevElement, return true; // all the selectors matched. } +static bool SelectorListMatches(Element* aElement, + nsCSSSelectorList* aList, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesFlags aSelectorFlags, + bool aIsForgiving, + bool aPreventComplexSelectors) +{ + while (aList) { + nsCSSSelector *selector = aList->mSelectors; + // Forgiving selector lists are allowed to be empty, but they + // don't match anything. + if (!selector && aIsForgiving) { + return false; + } + NS_ASSERTION(selector, "Should have *some* selectors"); + NS_ASSERTION(!selector->IsPseudoElement(), "Shouldn't have been called"); + if (aPreventComplexSelectors) { + NS_ASSERTION(!selector->mNext, "Shouldn't have complex selectors"); + } + if (SelectorMatches(aElement, + selector, + aNodeMatchContext, + aTreeMatchContext, + aSelectorFlags)) { + nsCSSSelector* next = selector->mNext; + SelectorMatchesTreeFlags selectorTreeFlags = SelectorMatchesTreeFlags(0); + // Try to look for the closest ancestor link element if we're processing + // the selector list argument of a pseudo-class. + if (!aNodeMatchContext.mIsRelevantLink && + (aSelectorFlags & SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) { + selectorTreeFlags = eLookForRelevantLink; + } + + if (!next || + SelectorMatchesTree(aElement, + next, + aTreeMatchContext, + selectorTreeFlags)) { + return true; + } + } + + aList = aList->mNext; + } + + return false; +} + +static bool SelectorListMatches(Element* aElement, + nsPseudoClassList* aList, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + bool aIsForgiving, + bool aPreventComplexSelectors) +{ + return SelectorListMatches(aElement, + aList->u.mSelectorList, + aNodeMatchContext, + aTreeMatchContext, + SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT, + aIsForgiving); +} + static inline void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector, ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext, @@ -4102,25 +4158,12 @@ nsCSSRuleProcessor::RestrictedSelectorListMatches(Element* aElement, "mCurrentStyleScope will need to be saved and restored after the " "SelectorMatchesTree call"); - while (aSelectorList) { - nsCSSSelector* sel = aSelectorList->mSelectors; - NS_ASSERTION(sel, "Should have *some* selectors"); - NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called"); - NodeMatchContext nodeContext(EventStates(), false); - if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext, - SelectorMatchesFlags::NONE)) { - nsCSSSelector* next = sel->mNext; - if (!next || - SelectorMatchesTree(aElement, next, aTreeMatchContext, - SelectorMatchesTreeFlags(0))) { - return true; - } - } - - aSelectorList = aSelectorList->mNext; - } - - return false; + NodeMatchContext nodeContext(EventStates(), false); + return SelectorListMatches(aElement, + aSelectorList, + nodeContext, + aTreeMatchContext, + SelectorMatchesFlags::NONE); } void From 4ece7873e09a93a342cbd2665607433ac0553b49 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sat, 18 Feb 2023 23:27:06 +0800 Subject: [PATCH 06/13] Issue #2078 - Part 5: Ensure :is() and :where() are given proper weights --- layout/style/StyleRule.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index f885bda64b..75cb6b7864 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -537,14 +537,29 @@ int32_t nsCSSSelector::CalcWeightWithoutNegations() const weight += 0x000100; list = list->mNext; } - // FIXME (bug 561154): This is incorrect for :-moz-any(), which isn't - // really a pseudo-class. In order to handle :-moz-any() correctly, - // we need to compute specificity after we match, based on which - // option we matched with (and thus also need to try the - // highest-specificity options first). nsPseudoClassList *plist = mPseudoClassList; while (nullptr != plist) { - weight += 0x000100; + int pseudoClassWeight = 0x000100; + // XXX(franklindm): Check for correctness. + // The specificity of the :where() pseudo-class is always zero. + if (plist->mType == CSSPseudoClassType::where) { + pseudoClassWeight = 0; + } else if (nsCSSPseudoClasses::HasForgivingSelectorListArg(plist->mType)) { + // The specificity of the :is() pseudo-class is replaced by the + // specificity of its most specific argument. + pseudoClassWeight = 0; + nsCSSSelectorList* slist = plist->u.mSelectorList; + while (slist) { + int currentWeight = slist->mSelectors ? + slist->mWeight : + 0; + if (currentWeight > pseudoClassWeight) { + pseudoClassWeight = currentWeight; + } + slist = slist->mNext; + } + } + weight += pseudoClassWeight; plist = plist->mNext; } nsAttrSelector* attr = mAttrList; From 6d77f755da27dd644d6c29e6da47e8786a989fe8 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Mon, 20 Feb 2023 22:28:50 +0800 Subject: [PATCH 07/13] Issue #2078 - Part 6: Replace empty list head with the next non-empty list for forgiving selector lists What happens here if aListHead is an empty selector list: (1) next selector group is parsed and continues to the next iteration if it's empty or invalid (2) if we're a forgiving selector list and aListHead is empty, replace it with the selector group that we've just parsed (3) step 1 ignores invalid/empty, so we assert that step 2 should never have an empty selector list --- layout/style/nsCSSParser.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index d793f453ff..45d78f93dc 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -5486,8 +5486,15 @@ CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, } break; } - // add new list to the end of the selector list - list->mNext = newList; + // 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) { + MOZ_ASSERT(newList->mSelectors, + "replacing empty list head with an empty selector list?"); + aListHead = newList; + } else { + list->mNext = newList; + } list = newList; continue; } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) { From ba7e29a0527e98e3d12b926dcba86dc80d6b5f0a Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sun, 19 Feb 2023 21:16:07 +0800 Subject: [PATCH 08/13] Issue #1823 - Ensure :host() and :host-context() are given proper weights --- layout/style/StyleRule.cpp | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 75cb6b7864..2f08d88f14 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -541,22 +541,28 @@ int32_t nsCSSSelector::CalcWeightWithoutNegations() const while (nullptr != plist) { int pseudoClassWeight = 0x000100; // XXX(franklindm): Check for correctness. - // The specificity of the :where() pseudo-class is always zero. - if (plist->mType == CSSPseudoClassType::where) { - pseudoClassWeight = 0; - } else if (nsCSSPseudoClasses::HasForgivingSelectorListArg(plist->mType)) { - // The specificity of the :is() pseudo-class is replaced by the - // specificity of its most specific argument. - pseudoClassWeight = 0; - nsCSSSelectorList* slist = plist->u.mSelectorList; - while (slist) { - int currentWeight = slist->mSelectors ? - slist->mWeight : - 0; - if (currentWeight > pseudoClassWeight) { - pseudoClassWeight = currentWeight; + if (nsCSSPseudoClasses::HasSelectorListArg(plist->mType) && + plist->mType != CSSPseudoClassType::mozAny) { + // The specificity of :host() and :host-context is that of a + // pseudo-class, plus the specificity of its argument. + if (plist->mType != CSSPseudoClassType::host && + plist->mType != CSSPseudoClassType::hostContext) { + pseudoClassWeight = 0; + } + // The specificity of the :where() pseudo-class is always zero. + if (plist->mType != CSSPseudoClassType::where) { + // The specificity of the :is() pseudo-class is replaced by the + // specificity of its most specific argument. + nsCSSSelectorList* slist = plist->u.mSelectorList; + while (slist) { + int currentWeight = slist->mSelectors ? + slist->mWeight : + 0; + if (currentWeight > pseudoClassWeight) { + pseudoClassWeight = currentWeight; + } + slist = slist->mNext; } - slist = slist->mNext; } } weight += pseudoClassWeight; From 9bebb3a61a14bd53e77f890a198b7b51fff7d68c Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Sun, 19 Feb 2023 23:38:20 +0800 Subject: [PATCH 09/13] Issue #1593 - Follow-up: :host() should not automatically match if we have a non-empty selector list --- layout/style/nsCSSRuleProcessor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index c8f888e7a1..3644e3e45b 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1960,15 +1960,15 @@ static bool SelectorMatches(Element* aElement, // selector. // We match automatically if GetParent() and GetShadowRoot() have - // the same result. Without special casing this ahead of all other - // selector matching, it fails. Have not determined the cause. - - if (aElement->GetParent() == aElement->GetShadowRoot()) { + // the same result iff our selector list is empty. Without special + // casing this ahead of all other selector matching, it fails. + // Have not determined the cause. + if (!pseudoClass->u.mSelectorList && + aElement->GetParent() == aElement->GetShadowRoot()) { break; } // Match if any selector in the argument list matches. - NodeMatchContext nodeContext(EventStates(), nsCSSRuleProcessor::IsLink(aElement)); if (SelectorListMatches(aElement, From 0391e8ab09295be02d2805fd8485ee11b1db1760 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Mon, 20 Feb 2023 01:28:13 +0800 Subject: [PATCH 10/13] Issue #1593 - Follow-up: Copy mIsRelevantLink value over rather than calling IsLink Calling IsLink ignores whatever state we have for aTreeMatchContext and will trigger an assertion if aTreeMatchContext.mForStyling is false. This also adds a comment about what that line effectively does. --- layout/style/nsCSSRuleProcessor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 3644e3e45b..de4ee0fc3b 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1969,8 +1969,10 @@ static bool SelectorMatches(Element* aElement, } // Match if any selector in the argument list matches. + // FIXME: What this effectively does is bypass the "featureless" + // selector check under SelectorMatches. NodeMatchContext nodeContext(EventStates(), - nsCSSRuleProcessor::IsLink(aElement)); + aNodeMatchContext.mIsRelevantLink); if (SelectorListMatches(aElement, pseudoClass, nodeContext, From 9037d5662dd52dfd3f944fdfc1797ba462603e6d Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Tue, 21 Feb 2023 20:00:53 +0800 Subject: [PATCH 11/13] Issue #1593 - Follow-up: Accept only a single selector in the argument of :host/:host-context Current spec says these two pseudo-classes accept only a single compound selector: :host( ) :host-context( ) --- layout/style/nsCSSParser.cpp | 5 +++++ layout/style/nsCSSPseudoClasses.cpp | 7 +++++++ layout/style/nsCSSPseudoClasses.h | 1 + 3 files changed, 13 insertions(+) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 45d78f93dc..ba97bbd3d7 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -6552,6 +6552,11 @@ CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') } + if (nsCSSPseudoClasses::HasSingleSelectorArg(aType) && + slist->mNext) { + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + for (nsCSSSelectorList *l = slist; l; l = l->mNext) { nsCSSSelector *s = l->mSelectors; if (s == nullptr) { diff --git a/layout/style/nsCSSPseudoClasses.cpp b/layout/style/nsCSSPseudoClasses.cpp index a174525b1c..928326e399 100644 --- a/layout/style/nsCSSPseudoClasses.cpp +++ b/layout/style/nsCSSPseudoClasses.cpp @@ -102,6 +102,13 @@ nsCSSPseudoClasses::HasNthPairArg(Type aType) aType == Type::nthLastOfType; } +bool +nsCSSPseudoClasses::HasSingleSelectorArg(Type aType) +{ + return aType == Type::host || + aType == Type::hostContext; +} + void nsCSSPseudoClasses::PseudoTypeToString(Type aType, nsAString& aString) { diff --git a/layout/style/nsCSSPseudoClasses.h b/layout/style/nsCSSPseudoClasses.h index 4a4bbe188c..76fcef3f78 100644 --- a/layout/style/nsCSSPseudoClasses.h +++ b/layout/style/nsCSSPseudoClasses.h @@ -58,6 +58,7 @@ public: static Type GetPseudoType(nsIAtom* aAtom, EnabledState aEnabledState); static bool HasStringArg(Type aType); static bool HasNthPairArg(Type aType); + static bool HasSingleSelectorArg(Type aType); static bool HasForgivingSelectorListArg(Type aType) { return aType == Type::is || aType == Type::matches || From db89c5a05dc6752e177c67e541e7b4d7a87fff3d Mon Sep 17 00:00:00 2001 From: Moonchild Date: Tue, 21 Feb 2023 20:29:35 +0100 Subject: [PATCH 12/13] No issue - Refactor FindErrorInstanceOrPrototype The logic here wasn't very transparent or easy to follow. Doing a positive check to set the result instead of defaulting to it also potentially prevents issues. --- js/src/vm/ErrorObject.cpp | 42 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index 2fa36089ed..7c966e0fe5 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -557,34 +557,30 @@ FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj, MutableHandleObjec // (new NYI).stack // to continue returning stacks that are useless, but at least don't throw. - RootedObject target(cx, CheckedUnwrap(obj)); - if (!target) { - JS_ReportErrorASCII(cx, "Permission denied to access object"); - return false; - } - - RootedObject proto(cx); - while (!IsErrorProtoKey(StandardProtoKeyOrNull(target))) { - if (!GetPrototype(cx, target, &proto)) - return false; - - if (!proto) { - // We walked the whole prototype chain and did not find an Error - // object. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, - js_Error_str, "(get stack)", obj->getClass()->name); - return false; - } - - target = CheckedUnwrap(proto); + RootedObject curr(cx, obj); + RootedObject target(cx); + do { + target = CheckedUnwrap(curr); if (!target) { JS_ReportErrorASCII(cx, "Permission denied to access object"); return false; } - } + + if (IsErrorProtoKey(StandardProtoKeyOrNull(target))) { + result.set(target); + return true; + } - result.set(target); - return true; + if (!GetPrototype(cx, target, &curr)) { + return false; + } + } while (curr); + + // We walked the whole prototype chain and did not find an Error + // object. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, + js_Error_str, "(get stack)", obj->getClass()->name); + return false; } From 0b9698c80a72a09dbbac2f5af0177d834251ec63 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Tue, 21 Feb 2023 21:07:04 +0100 Subject: [PATCH 13/13] [DOM] WebCrypto: Check decoded key type before using it. Just in case someone forces the wrong key type and misuses WebCrypto. It won't be usable anyway so better to throw. --- dom/crypto/WebCryptoTask.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp index ad81b6d4c6..ed47325f8d 100644 --- a/dom/crypto/WebCryptoTask.cpp +++ b/dom/crypto/WebCryptoTask.cpp @@ -1741,6 +1741,10 @@ private: return NS_ERROR_DOM_SYNTAX_ERR; } + if (pubKey->keyType != rsaKey) { + return NS_ERROR_DOM_DATA_ERR; + } + // Extract relevant information from the public key mModulusLength = 8 * pubKey->u.rsa.modulus.len; if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) { @@ -1874,6 +1878,10 @@ private: } if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) { + if (pubKey->keyType != ecKey) { + return NS_ERROR_DOM_DATA_ERR; + } + if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) { return NS_ERROR_DOM_OPERATION_ERR; }