mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AnySelectorInArgListMatches(Element* aElement,
|
static bool SelectorListMatches(Element* aElement,
|
||||||
nsPseudoClassList* aList,
|
nsCSSSelectorList* aList,
|
||||||
NodeMatchContext& aNodeMatchContext,
|
NodeMatchContext& aNodeMatchContext,
|
||||||
TreeMatchContext& aTreeMatchContext,
|
TreeMatchContext& aTreeMatchContext,
|
||||||
bool aIsForgiving = false);
|
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
|
static bool
|
||||||
StateSelectorMatches(Element* aElement,
|
StateSelectorMatches(Element* aElement,
|
||||||
|
|
@ -1913,10 +1922,11 @@ static bool SelectorMatches(Element* aElement,
|
||||||
case CSSPseudoClassType::any:
|
case CSSPseudoClassType::any:
|
||||||
case CSSPseudoClassType::where:
|
case CSSPseudoClassType::where:
|
||||||
{
|
{
|
||||||
if (!AnySelectorInArgListMatches(aElement, pseudoClass,
|
if (!SelectorListMatches(aElement,
|
||||||
aNodeMatchContext,
|
pseudoClass,
|
||||||
aTreeMatchContext,
|
aNodeMatchContext,
|
||||||
true)) {
|
aTreeMatchContext,
|
||||||
|
true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1924,9 +1934,15 @@ static bool SelectorMatches(Element* aElement,
|
||||||
|
|
||||||
case CSSPseudoClassType::mozAny:
|
case CSSPseudoClassType::mozAny:
|
||||||
{
|
{
|
||||||
if (!AnySelectorInArgListMatches(aElement, pseudoClass,
|
// XXX: For compatibility, we retain :-moz-any()'s original behavior,
|
||||||
aNodeMatchContext,
|
// which is to be unforgiving and reject complex selectors in
|
||||||
aTreeMatchContext)) {
|
// its selector list argument.
|
||||||
|
if (!SelectorListMatches(aElement,
|
||||||
|
pseudoClass,
|
||||||
|
aNodeMatchContext,
|
||||||
|
aTreeMatchContext,
|
||||||
|
false,
|
||||||
|
true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1955,9 +1971,10 @@ static bool SelectorMatches(Element* aElement,
|
||||||
|
|
||||||
NodeMatchContext nodeContext(EventStates(),
|
NodeMatchContext nodeContext(EventStates(),
|
||||||
nsCSSRuleProcessor::IsLink(aElement));
|
nsCSSRuleProcessor::IsLink(aElement));
|
||||||
if (AnySelectorInArgListMatches(aElement, pseudoClass,
|
if (SelectorListMatches(aElement,
|
||||||
nodeContext,
|
pseudoClass,
|
||||||
aTreeMatchContext)) {
|
nodeContext,
|
||||||
|
aTreeMatchContext)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1996,9 +2013,10 @@ static bool SelectorMatches(Element* aElement,
|
||||||
while (currentElement) {
|
while (currentElement) {
|
||||||
NodeMatchContext nodeContext(EventStates(),
|
NodeMatchContext nodeContext(EventStates(),
|
||||||
nsCSSRuleProcessor::IsLink(currentElement));
|
nsCSSRuleProcessor::IsLink(currentElement));
|
||||||
if (AnySelectorInArgListMatches(currentElement, pseudoClass,
|
if (SelectorListMatches(currentElement,
|
||||||
nodeContext,
|
pseudoClass,
|
||||||
aTreeMatchContext)) {
|
nodeContext,
|
||||||
|
aTreeMatchContext)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2388,32 +2406,6 @@ static bool SelectorMatches(Element* aElement,
|
||||||
return result;
|
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
|
#undef STATE_CHECK
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -2658,6 +2650,70 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
return true; // all the selectors matched.
|
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
|
static inline
|
||||||
void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
||||||
ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
|
ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
|
||||||
|
|
@ -4102,25 +4158,12 @@ nsCSSRuleProcessor::RestrictedSelectorListMatches(Element* aElement,
|
||||||
"mCurrentStyleScope will need to be saved and restored after the "
|
"mCurrentStyleScope will need to be saved and restored after the "
|
||||||
"SelectorMatchesTree call");
|
"SelectorMatchesTree call");
|
||||||
|
|
||||||
while (aSelectorList) {
|
NodeMatchContext nodeContext(EventStates(), false);
|
||||||
nsCSSSelector* sel = aSelectorList->mSelectors;
|
return SelectorListMatches(aElement,
|
||||||
NS_ASSERTION(sel, "Should have *some* selectors");
|
aSelectorList,
|
||||||
NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
|
nodeContext,
|
||||||
NodeMatchContext nodeContext(EventStates(), false);
|
aTreeMatchContext,
|
||||||
if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext,
|
SelectorMatchesFlags::NONE);
|
||||||
SelectorMatchesFlags::NONE)) {
|
|
||||||
nsCSSSelector* next = sel->mNext;
|
|
||||||
if (!next ||
|
|
||||||
SelectorMatchesTree(aElement, next, aTreeMatchContext,
|
|
||||||
SelectorMatchesTreeFlags(0))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aSelectorList = aSelectorList->mNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue