Bug 783213 - Part 2: Don't apply the quirk to selectors that use a pseudo-element or are part of a pseudo-class argument. r=dbaron

This commit is contained in:
Brian Marshall 2014-11-13 21:37:42 -08:00
parent c0a3b6e87d
commit ce5f1f5c0d

View File

@ -53,6 +53,8 @@
#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Likely.h"
#include "mozilla/TypedEnum.h"
#include "mozilla/TypedEnumBits.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -1429,6 +1431,30 @@ struct NodeMatchContext {
}
};
/**
* Additional information about a selector (without combinators) that is
* being matched.
*/
MOZ_BEGIN_ENUM_CLASS(SelectorMatchesFlags, uint8_t)
NONE = 0,
// The selector's flags are unknown. This happens when you don't know
// if you're starting from the top of a selector. Only used in cases
// where it's acceptable for matching to return a false positive.
// (It's not OK to return a false negative.)
UNKNOWN = 1 << 0,
// The selector is part of a compound selector which has been split in
// half, where the other half is a pseudo-element. The current
// selector is not a pseudo-element itself.
HAS_PSEUDO_ELEMENT = 1 << 1,
// The selector is part of an argument to a functional pseudo-class or
// pseudo-element.
IS_PSEUDO_CLASS_ARGUMENT = 1 << 2
MOZ_END_ENUM_CLASS(SelectorMatchesFlags)
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags)
static bool ValueIncludes(const nsSubstring& aValueList,
const nsSubstring& aValue,
const nsStringComparator& aComparator)
@ -1460,10 +1486,18 @@ static bool ValueIncludes(const nsSubstring& aValueList,
// Return whether the selector matches conditions for the :active and
// :hover quirk.
static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector)
static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector,
SelectorMatchesFlags aSelectorFlags)
{
if (aSelector->HasTagSelector() || aSelector->mAttrList ||
aSelector->mIDList || aSelector->mClassList) {
aSelector->mIDList || aSelector->mClassList ||
aSelector->IsPseudoElement() ||
// Having this quirk means that some selectors will no longer match,
// so it's better to return false when we aren't sure (i.e., the
// flags are unknown).
aSelectorFlags & (SelectorMatchesFlags::UNKNOWN |
SelectorMatchesFlags::HAS_PSEUDO_ELEMENT |
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
return false;
}
@ -1673,6 +1707,7 @@ StateSelectorMatches(Element* aElement,
nsCSSSelector* aSelector,
NodeMatchContext& aNodeMatchContext,
TreeMatchContext& aTreeMatchContext,
SelectorMatchesFlags aSelectorFlags,
bool* const aDependence,
EventStates aStatesToCheck)
{
@ -1680,18 +1715,11 @@ StateSelectorMatches(Element* aElement,
"should only need to call StateSelectorMatches if "
"aStatesToCheck is not empty");
const bool isNegated = aDependence != nullptr;
// Bit-based pseudo-classes
if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE |
NS_EVENT_STATE_HOVER) &&
aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks &&
ActiveHoverQuirkMatches(aSelector) &&
// This (or the other way around) both make :not() asymmetric
// in quirks mode (and it's hard to work around since we're
// testing the current mNegations, not the first
// (unnegated)). This at least makes it closer to the spec.
!isNegated &&
ActiveHoverQuirkMatches(aSelector, aSelectorFlags) &&
aElement->IsHTML() && !nsCSSRuleProcessor::IsLink(aElement)) {
// In quirks mode, only make links sensitive to selectors ":active"
// and ":hover".
@ -1727,14 +1755,16 @@ static bool
StateSelectorMatches(Element* aElement,
nsCSSSelector* aSelector,
NodeMatchContext& aNodeMatchContext,
TreeMatchContext& aTreeMatchContext)
TreeMatchContext& aTreeMatchContext,
SelectorMatchesFlags aSelectorFlags)
{
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
pseudoClass; pseudoClass = pseudoClass->mNext) {
EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
if (!statesToCheck.IsEmpty() &&
!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
aTreeMatchContext, nullptr, statesToCheck)) {
aTreeMatchContext, aSelectorFlags, nullptr,
statesToCheck)) {
return false;
}
}
@ -1747,11 +1777,11 @@ StateSelectorMatches(Element* aElement,
// * what it points to should be set to true whenever a test is skipped
// because of aNodeMatchContent.mStateMask
static bool SelectorMatches(Element* aElement,
nsCSSSelector* aSelector,
NodeMatchContext& aNodeMatchContext,
TreeMatchContext& aTreeMatchContext,
bool* const aDependence = nullptr)
nsCSSSelector* aSelector,
NodeMatchContext& aNodeMatchContext,
TreeMatchContext& aTreeMatchContext,
SelectorMatchesFlags aSelectorFlags,
bool* const aDependence = nullptr)
{
NS_PRECONDITION(!aSelector->IsPseudoElement(),
"Pseudo-element snuck into SelectorMatches?");
@ -1966,8 +1996,9 @@ static bool SelectorMatches(Element* aElement,
nsCSSSelector *s = l->mSelectors;
NS_ABORT_IF_FALSE(!s->mNext && !s->IsPseudoElement(),
"parser failed");
if (SelectorMatches(aElement, s, aNodeMatchContext,
aTreeMatchContext)) {
if (SelectorMatches(
aElement, s, aNodeMatchContext, aTreeMatchContext,
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
break;
}
}
@ -2229,7 +2260,7 @@ static bool SelectorMatches(Element* aElement,
}
} else {
if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
aTreeMatchContext, aDependence,
aTreeMatchContext, aSelectorFlags, aDependence,
statesToCheck)) {
return false;
}
@ -2319,7 +2350,9 @@ static bool SelectorMatches(Element* aElement,
result && negation; negation = negation->mNegations) {
bool dependence = false;
result = !SelectorMatches(aElement, negation, aNodeMatchContext,
aTreeMatchContext, &dependence);
aTreeMatchContext,
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT,
&dependence);
// If the selector does match due to the dependence on
// aNodeMatchContext.mStateMask, then we want to keep result true
// so that the final result of SelectorMatches is true. Doing so
@ -2427,7 +2460,8 @@ static bool SelectorMatchesTree(Element* aPrevElement,
aLookForRelevantLink = false;
aTreeMatchContext.SetHaveRelevantLink();
}
if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext)) {
if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext,
SelectorMatchesFlags::NONE)) {
// to avoid greedy matching, we need to recur if this is a
// descendant or general sibling combinator and the next
// combinator is different, but we can make an exception for
@ -2509,13 +2543,19 @@ void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
return;
}
if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext,
data->mTreeMatchContext)) {
data->mTreeMatchContext,
SelectorMatchesFlags::NONE)) {
return;
}
selector = selector->mNext;
}
SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::NONE;
if (aSelector->IsPseudoElement()) {
selectorFlags |= SelectorMatchesFlags::HAS_PSEUDO_ELEMENT;
}
if (SelectorMatches(data->mElement, selector, nodeContext,
data->mTreeMatchContext)) {
data->mTreeMatchContext, selectorFlags)) {
nsCSSSelector *next = selector->mNext;
if (!next || SelectorMatchesTree(data->mElement, next,
data->mTreeMatchContext,
@ -2660,6 +2700,7 @@ nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aD
}
nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::UNKNOWN;
// If hint already includes all the bits of possibleChange,
// don't bother calling SelectorMatches, since even if it returns false
@ -2689,9 +2730,9 @@ nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aD
(!isPseudoElement ||
StateSelectorMatches(aStatefulElement, selectorForPseudo,
nodeContext, aData->mTreeMatchContext,
nullptr, aStateMask)) &&
selectorFlags, nullptr, aStateMask)) &&
SelectorMatches(aData->mElement, selector, nodeContext,
aData->mTreeMatchContext) &&
aData->mTreeMatchContext, selectorFlags) &&
SelectorMatchesTree(aData->mElement, selector->mNext,
aData->mTreeMatchContext,
false))
@ -2758,7 +2799,7 @@ AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
NodeMatchContext nodeContext(EventStates(), false);
if ((possibleChange & ~(aData->change)) &&
SelectorMatches(data->mElement, aSelector, nodeContext,
data->mTreeMatchContext) &&
data->mTreeMatchContext, SelectorMatchesFlags::UNKNOWN) &&
SelectorMatchesTree(data->mElement, aSelector->mNext,
data->mTreeMatchContext, false)) {
aData->change = nsRestyleHint(aData->change | possibleChange);
@ -3575,7 +3616,8 @@ nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
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)) {
if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext,
SelectorMatchesFlags::NONE)) {
nsCSSSelector* next = sel->mNext;
if (!next ||
SelectorMatchesTree(aElement, next, aTreeMatchContext, false)) {