mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
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.
This commit is contained in:
parent
e8f840bf2f
commit
453b715ef6
6 changed files with 103 additions and 35 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<nsCSSSelectorList> 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<nsCSSSelectorList> 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 {
|
||||
|
|
|
|||
|
|
@ -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, "")
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue