mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-22 04:13:06 +09:00
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`.
This commit is contained in:
parent
14e8922dca
commit
1c1473166f
1 changed files with 106 additions and 63 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue