Change storage of :not() selectors so that there is one item in mNegations per :not(), and simplify various code accordingly. Simplify SelectorMatches a little more. b=315567 r+sr=bzbarsky

This commit is contained in:
dbaron%dbaron.org 2005-11-09 04:17:26 +00:00
parent d2a0bba83a
commit eac6a24015
4 changed files with 223 additions and 248 deletions

View File

@ -2581,42 +2581,39 @@ CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask,
REPORT_UNEXPECTED_EOF(PENegationEOF);
return eSelectorParsingStatus_Error;
}
if (!aSelector.mNegations) {
aSelector.mNegations = new nsCSSSelector();
if (!aSelector.mNegations) {
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
return eSelectorParsingStatus_Error;
}
// Create a new nsCSSSelector and add it to the end of
// aSelector.mNegations.
// Given the current parsing rules, every selector in mNegations
// contains only one simple selector (css3 definition) within it.
// This could easily change in future versions of CSS, and the only
// thing we need to change to support that is this parsing code.
nsCSSSelector *newSel = new nsCSSSelector();
if (!newSel) {
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
return eSelectorParsingStatus_Error;
}
// ID, class and attribute selectors and pseudo-classes are stored in
// the first mNegations attached to a selector
nsCSSSelector* negations = &aSelector;
while (negations->mNegations) {
negations = negations->mNegations;
}
negations->mNegations = newSel;
nsSelectorParsingStatus parsingStatus;
if (eCSSToken_ID == mToken.mType) { // #id
parsingStatus = ParseIDSelector(aDataMask, *aSelector.mNegations, aErrorCode);
parsingStatus = ParseIDSelector(aDataMask, *newSel, aErrorCode);
}
else if (mToken.IsSymbol('.')) { // .class
parsingStatus = ParseClassSelector(aDataMask, *aSelector.mNegations, aErrorCode);
parsingStatus = ParseClassSelector(aDataMask, *newSel, aErrorCode);
}
else if (mToken.IsSymbol(':')) { // :pseudo
parsingStatus = ParsePseudoSelector(aDataMask, *aSelector.mNegations, aErrorCode, PR_TRUE);
parsingStatus = ParsePseudoSelector(aDataMask, *newSel, aErrorCode, PR_TRUE);
}
else if (mToken.IsSymbol('[')) { // [attribute
parsingStatus = ParseAttributeSelector(aDataMask, *aSelector.mNegations, aErrorCode);
parsingStatus = ParseAttributeSelector(aDataMask, *newSel, aErrorCode);
}
else {
// then it should be a type element or universal selector
nsCSSSelector *newSel = new nsCSSSelector();
if (!newSel) {
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
return eSelectorParsingStatus_Error;
}
nsCSSSelector* negations = aSelector.mNegations;
while (nsnull != negations->mNegations) {
negations = negations->mNegations;
}
// negated type element selectors and universal selectors are stored after the first
// mNegations containing only negated IDs, classes, attributes and pseudo-classes
negations->mNegations = newSel;
parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, aErrorCode, PR_TRUE);
}
if (eSelectorParsingStatus_Error == parsingStatus) {

View File

@ -80,12 +80,6 @@
#define NS_IF_DELETE(ptr) \
if (ptr) { delete ptr; ptr = nsnull; }
#define NS_IF_NEGATED_START(bool,str) \
if (bool) { str.AppendLiteral(":not("); }
#define NS_IF_NEGATED_END(bool,str) \
if (bool) { str.Append(PRUnichar(')')); }
MOZ_DECL_CTOR_COUNTER(nsAtomList)
nsAtomList::nsAtomList(nsIAtom* aAtom)
@ -542,16 +536,15 @@ nsCSSSelector::ToString(nsAString& aString, nsICSSStyleSheet* aSheet,
if (!aAppend)
aString.Truncate();
ToStringInternal(aString, aSheet, IsPseudoElement(mTag), 0);
ToStringInternal(aString, aSheet, IsPseudoElement(mTag), PR_FALSE);
}
void nsCSSSelector::ToStringInternal(nsAString& aString,
nsICSSStyleSheet* aSheet,
PRBool aIsPseudoElem,
PRIntn aNegatedIndex) const
PRBool aIsNegated) const
{
nsAutoString temp;
PRBool aIsNegated = PRBool(0 < aNegatedIndex);
PRBool isPseudoElement = IsPseudoElement(mTag);
// selectors are linked from right-to-left, so the next selector in the linked list
@ -564,11 +557,6 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
aString.Append(PRUnichar(' '));
}
}
if (1 < aNegatedIndex) {
// the first mNegations does not contain a negated type element selector
// or a negated universal selector
NS_IF_NEGATED_START(aIsNegated, aString)
}
// For non-pseudo-element selectors or for lone pseudo-elements, deal with
// namespace prefixes.
@ -611,13 +599,9 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
}
// smells like a universal selector
if (!mTag && !mIDList && !mClassList) {
if (1 != aNegatedIndex) {
aString.Append(PRUnichar('*'));
}
if (1 < aNegatedIndex) {
NS_IF_NEGATED_END(aIsNegated, aString)
}
if (!mTag && !mIDList && !mClassList && !mPseudoClassList && !mAttrList) {
// XXXldb Or if we've written a namespace.
aString.Append(PRUnichar('*'));
} else {
// Append the tag name, if there is one
if (mTag) {
@ -633,17 +617,14 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
nsAutoString prefix;
mTag->ToString(prefix);
aString.Append(prefix);
NS_IF_NEGATED_END(aIsNegated, aString)
}
// Append the id, if there is one
if (mIDList) {
nsAtomList* list = mIDList;
while (list != nsnull) {
list->mAtom->ToString(temp);
NS_IF_NEGATED_START(aIsNegated, aString)
aString.Append(PRUnichar('#'));
aString.Append(temp);
NS_IF_NEGATED_END(aIsNegated, aString)
list = list->mNext;
}
}
@ -652,10 +633,8 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
nsAtomList* list = mClassList;
while (list != nsnull) {
list->mAtom->ToString(temp);
NS_IF_NEGATED_START(aIsNegated, aString)
aString.Append(PRUnichar('.'));
aString.Append(temp);
NS_IF_NEGATED_END(aIsNegated, aString)
list = list->mNext;
}
}
@ -665,7 +644,6 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
if (mAttrList) {
nsAttrSelector* list = mAttrList;
while (list != nsnull) {
NS_IF_NEGATED_START(aIsNegated, aString)
aString.Append(PRUnichar('['));
// Append the namespace prefix
if (list->mNameSpace > 0) {
@ -709,7 +687,6 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
aString.Append(PRUnichar(']'));
NS_IF_NEGATED_END(aIsNegated, aString)
list = list->mNext;
}
}
@ -719,21 +696,23 @@ void nsCSSSelector::ToStringInternal(nsAString& aString,
nsAtomStringList* list = mPseudoClassList;
while (list != nsnull) {
list->mAtom->ToString(temp);
NS_IF_NEGATED_START(aIsNegated, aString)
aString.Append(temp);
if (nsnull != list->mString) {
aString.Append(PRUnichar('('));
aString.Append(list->mString);
aString.Append(PRUnichar(')'));
}
NS_IF_NEGATED_END(aIsNegated, aString)
list = list->mNext;
}
}
if (mNegations) {
// chain all the negated selectors
mNegations->ToStringInternal(aString, aSheet, PR_FALSE, aNegatedIndex + 1);
if (!aIsNegated) {
for (nsCSSSelector* negation = mNegations; negation;
negation = negation->mNegations) {
aString.AppendLiteral(":not(");
negation->ToStringInternal(aString, aSheet, PR_FALSE, PR_TRUE);
aString.Append(PRUnichar(')'));
}
}
// Append the operator only if the selector is not negated and is not

View File

@ -2613,17 +2613,14 @@ RuleProcessorData::RuleProcessorData(nsPresContext* aPresContext,
mClasses = nsnull;
// get the compat. mode (unless it is provided)
if(!aCompat) {
if (!aCompat) {
mCompatMode = mPresContext->CompatibilityMode();
} else {
mCompatMode = *aCompat;
}
if(aContent){
// we hold no ref to the content...
mContent = aContent;
if (aContent) {
// get the tag and parent
mContentTag = aContent->Tag();
mParentContent = aContent->GetParent();
@ -2742,16 +2739,6 @@ static PRBool ValueIncludes(const nsSubstring& aValueList,
return PR_FALSE;
}
inline PRBool IsEventPseudo(nsIAtom* aAtom)
{
return nsCSSPseudoClasses::active == aAtom ||
nsCSSPseudoClasses::mozDragOver == aAtom ||
nsCSSPseudoClasses::focus == aAtom ||
nsCSSPseudoClasses::hover == aAtom ||
nsCSSPseudoClasses::target == aAtom;
// XXX selected, enabled, disabled, selection?
}
inline PRBool IsLinkPseudo(nsIAtom* aAtom)
{
return PRBool ((nsCSSPseudoClasses::link == aAtom) ||
@ -2832,54 +2819,43 @@ static PRBool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
}
}
#define STATE_CHECK(_state) \
((aStateMask & (_state)) || \
(localTrue == (0 != (data.mEventState & (_state)))))
// NOTE: The |aStateMask| code isn't going to work correctly anymore if
// we start batching style changes, because if multiple states change in
// separate notifications then we might determine the style is not
// state-dependent when it really is (e.g., determining that a
// :hover:active rule no longer matches when both states are unset).
// XXXldb This is a real problem for things like [checked]:checked where
// both states are determined exactly by an attribute.
// |aDependence| has two functions:
// * when non-null, it indicates that we're processing a negation,
// which is done only when SelectorMatches calls itself recursively
// * what it points to should be set to true whenever a test is skipped
// because of aStateMask or aAttribute
static PRBool SelectorMatches(RuleProcessorData &data,
nsCSSSelector* aSelector,
PRInt32 aStateMask, // states NOT to test
nsIAtom* aAttribute, // attribute NOT to test
PRInt8 aNegationIndex)
PRBool* const aDependence = nsnull)
{
// if we are dealing with negations, reverse the values of PR_TRUE and PR_FALSE
PRBool localFalse = 0 < aNegationIndex;
PRBool localTrue = 0 == aNegationIndex;
PRBool result = localTrue;
// Do not perform the test if aNegationIndex==1
// because it then contains only negated IDs, classes, attributes and pseudo-
// classes
if (1 != aNegationIndex) {
if (kNameSpaceID_Unknown != aSelector->mNameSpace) {
if (data.mNameSpaceID != aSelector->mNameSpace) {
result = localFalse;
}
}
if (localTrue == result) {
if ((nsnull != aSelector->mTag) && (aSelector->mTag != data.mContentTag)) {
result = localFalse;
}
}
// namespace/tag match
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
data.mNameSpaceID != aSelector->mNameSpace) ||
(aSelector->mTag && aSelector->mTag != data.mContentTag)) {
// optimization : bail out early if we can
if (!result) {
return PR_FALSE;
}
return PR_FALSE;
}
result = PR_TRUE;
PRBool result = PR_TRUE;
const PRBool isNegated = (aDependence != nsnull);
// test for pseudo class match
// first-child, root, lang, active, focus, hover, link, visited...
// XXX disabled, enabled, selected, selection
for (nsAtomStringList* pseudoClass = aSelector->mPseudoClassList;
pseudoClass && result; pseudoClass = pseudoClass->mNext) {
PRInt32 stateToCheck = 0;
if ((nsCSSPseudoClasses::firstChild == pseudoClass->mAtom) ||
(nsCSSPseudoClasses::firstNode == pseudoClass->mAtom) ) {
nsIContent *firstChild = nsnull;
@ -2895,7 +2871,7 @@ static PRBool SelectorMatches(RuleProcessorData &data,
} while (firstChild &&
!IsSignificantChild(firstChild, acceptNonWhitespace, PR_FALSE));
}
result = localTrue == (data.mContent == firstChild);
result = (data.mContent == firstChild);
}
else if ((nsCSSPseudoClasses::lastChild == pseudoClass->mAtom) ||
(nsCSSPseudoClasses::lastNode == pseudoClass->mAtom)) {
@ -2912,7 +2888,7 @@ static PRBool SelectorMatches(RuleProcessorData &data,
} while (lastChild &&
!IsSignificantChild(lastChild, acceptNonWhitespace, PR_FALSE));
}
result = localTrue == (data.mContent == lastChild);
result = (data.mContent == lastChild);
}
else if (nsCSSPseudoClasses::onlyChild == pseudoClass->mAtom) {
nsIContent *onlyChild = nsnull;
@ -2932,14 +2908,13 @@ static PRBool SelectorMatches(RuleProcessorData &data,
} while (moreChild && !IsSignificantChild(moreChild, PR_FALSE, PR_FALSE));
}
}
result = localTrue == (data.mContent == onlyChild &&
moreChild == nsnull);
result = (data.mContent == onlyChild && moreChild == nsnull);
}
else if (nsCSSPseudoClasses::empty == pseudoClass->mAtom ||
nsCSSPseudoClasses::mozOnlyWhitespace == pseudoClass->mAtom) {
nsIContent *child = nsnull;
nsIContent *element = data.mContent;
PRBool isWhitespaceSignificant =
const PRBool isWhitespaceSignificant =
nsCSSPseudoClasses::empty == pseudoClass->mAtom;
PRInt32 index = -1;
@ -2948,7 +2923,7 @@ static PRBool SelectorMatches(RuleProcessorData &data,
// stop at first non-comment (and non-whitespace for
// :-moz-only-whitespace) node
} while (child && !IsSignificantChild(child, PR_TRUE, isWhitespaceSignificant));
result = localTrue == (child == nsnull);
result = (child == nsnull);
}
else if (nsCSSPseudoClasses::mozEmptyExceptChildrenWithLocalname == pseudoClass->mAtom) {
NS_ASSERTION(pseudoClass->mString, "Must have string!");
@ -2962,26 +2937,20 @@ static PRBool SelectorMatches(RuleProcessorData &data,
(!IsSignificantChild(child, PR_TRUE, PR_FALSE) ||
(child->GetNameSpaceID() == element->GetNameSpaceID() &&
child->Tag()->Equals(nsDependentString(pseudoClass->mString)))));
result = localTrue == (child == nsnull);
result = (child == nsnull);
}
else if (nsCSSPseudoClasses::root == pseudoClass->mAtom) {
if (data.mParentContent) {
result = localFalse;
}
else {
result = localTrue;
}
result = (data.mParentContent == nsnull);
}
else if (nsCSSPseudoClasses::mozBoundElement == pseudoClass->mAtom) {
// XXXldb How do we know where the selector came from? And what
// if there are multiple bindings, and we should be matching the
// outer one?
result = (data.mScopedRoot && data.mScopedRoot == data.mContent)
? localTrue : localFalse;
result = (data.mScopedRoot && data.mScopedRoot == data.mContent);
}
else if (nsCSSPseudoClasses::lang == pseudoClass->mAtom) {
NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter");
result = localFalse;
result = PR_FALSE;
if (pseudoClass->mString && *pseudoClass->mString) {
// We have to determine the language of the current element. Since
// this is currently no property and since the language is inherited
@ -2989,7 +2958,7 @@ static PRBool SelectorMatches(RuleProcessorData &data,
// nodes. The language itself is encoded in the LANG attribute.
const nsString* lang = data.GetLang();
if (lang && !lang->IsEmpty()) { // null check for out-of-memory
result = localTrue == nsStyleUtil::DashMatchCompare(*lang,
result = nsStyleUtil::DashMatchCompare(*lang,
nsDependentString(pseudoClass->mString),
nsCaseInsensitiveStringComparator());
}
@ -3015,7 +2984,7 @@ static PRBool SelectorMatches(RuleProcessorData &data,
if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end-begin),
langString,
nsCaseInsensitiveStringComparator())) {
result = localTrue;
result = PR_TRUE;
break;
}
begin = end + 1;
@ -3023,60 +2992,46 @@ static PRBool SelectorMatches(RuleProcessorData &data,
}
}
}
} else if (nsCSSPseudoClasses::active == pseudoClass->mAtom) {
stateToCheck = NS_EVENT_STATE_ACTIVE;
}
else if (IsEventPseudo(pseudoClass->mAtom)) {
// check if the element is event-sensitive
if (data.mCompatMode == eCompatibility_NavQuirks &&
// global selector (but don't check .class):
!aSelector->mTag && !aSelector->mIDList && !aSelector->mAttrList &&
// 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.
aNegationIndex == 0 &&
// :hover or :active
(nsCSSPseudoClasses::active == pseudoClass->mAtom ||
nsCSSPseudoClasses::hover == pseudoClass->mAtom) &&
// important for |IsQuirkEventSensitive|:
data.mIsHTMLContent && !data.mIsHTMLLink &&
!IsQuirkEventSensitive(data.mContentTag)) {
// In quirks mode, only make certain elements sensitive to
// selectors ":hover" and ":active".
result = localFalse;
} else {
if (nsCSSPseudoClasses::active == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_ACTIVE);
}
else if (nsCSSPseudoClasses::focus == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_FOCUS);
}
else if (nsCSSPseudoClasses::hover == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_HOVER);
}
else if (nsCSSPseudoClasses::mozDragOver == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_DRAGOVER);
}
else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_URLTARGET);
}
}
else if (nsCSSPseudoClasses::focus == pseudoClass->mAtom) {
stateToCheck = NS_EVENT_STATE_FOCUS;
}
else if (nsCSSPseudoClasses::hover == pseudoClass->mAtom) {
stateToCheck = NS_EVENT_STATE_HOVER;
}
else if (nsCSSPseudoClasses::mozDragOver == pseudoClass->mAtom) {
stateToCheck = NS_EVENT_STATE_DRAGOVER;
}
else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {
stateToCheck = NS_EVENT_STATE_URLTARGET;
}
else if (IsLinkPseudo(pseudoClass->mAtom)) {
if (data.mIsHTMLLink || data.mIsSimpleXLink) {
if (nsCSSPseudoClasses::mozAnyLink == pseudoClass->mAtom) {
result = localTrue;
result = PR_TRUE;
}
else if (nsCSSPseudoClasses::link == pseudoClass->mAtom) {
result = (aStateMask & NS_EVENT_STATE_VISITED) ||
localTrue == (eLinkState_Unvisited == data.mLinkState);
}
else if (nsCSSPseudoClasses::visited == pseudoClass->mAtom) {
result = (aStateMask & NS_EVENT_STATE_VISITED) ||
localTrue == (eLinkState_Visited == data.mLinkState);
else {
NS_ASSERTION(nsCSSPseudoClasses::link == pseudoClass->mAtom ||
nsCSSPseudoClasses::visited == pseudoClass->mAtom,
"somebody changed IsLinkPseudo");
NS_ASSERTION(data.mLinkState == eLinkState_Unvisited ||
data.mLinkState == eLinkState_Visited,
"unexpected link state for "
"mIsHTMLLink || mIsSimpleXLink");
if (aStateMask & NS_EVENT_STATE_VISITED) {
result = PR_TRUE;
if (aDependence)
*aDependence = PR_TRUE;
} else {
result = ((eLinkState_Unvisited == data.mLinkState) ==
(nsCSSPseudoClasses::link == pseudoClass->mAtom));
}
}
}
else {
result = localFalse; // not a link
result = PR_FALSE; // not a link
}
}
else if (nsCSSPseudoClasses::checked == pseudoClass->mAtom) {
@ -3084,74 +3039,101 @@ static PRBool SelectorMatches(RuleProcessorData &data,
// <option>
// <input type=checkbox>
// <input type=radio>
result = STATE_CHECK(NS_EVENT_STATE_CHECKED);
stateToCheck = NS_EVENT_STATE_CHECKED;
}
else if (nsCSSPseudoClasses::enabled == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_ENABLED);
stateToCheck = NS_EVENT_STATE_ENABLED;
}
else if (nsCSSPseudoClasses::disabled == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_DISABLED);
stateToCheck = NS_EVENT_STATE_DISABLED;
}
else if (nsCSSPseudoClasses::mozBroken == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_BROKEN);
stateToCheck = NS_EVENT_STATE_BROKEN;
}
else if (nsCSSPseudoClasses::mozUserDisabled == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_USERDISABLED);
stateToCheck = NS_EVENT_STATE_USERDISABLED;
}
else if (nsCSSPseudoClasses::mozSuppressed == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_SUPPRESSED);
stateToCheck = NS_EVENT_STATE_SUPPRESSED;
}
else if (nsCSSPseudoClasses::mozLoading == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_LOADING);
stateToCheck = NS_EVENT_STATE_LOADING;
}
else if (nsCSSPseudoClasses::required == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_REQUIRED);
stateToCheck = NS_EVENT_STATE_REQUIRED;
}
else if (nsCSSPseudoClasses::optional == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_OPTIONAL);
stateToCheck = NS_EVENT_STATE_OPTIONAL;
}
else if (nsCSSPseudoClasses::valid == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_VALID);
stateToCheck = NS_EVENT_STATE_VALID;
}
else if (nsCSSPseudoClasses::invalid == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_INVALID);
stateToCheck = NS_EVENT_STATE_INVALID;
}
else if (nsCSSPseudoClasses::inRange == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_INRANGE);
stateToCheck = NS_EVENT_STATE_INRANGE;
}
else if (nsCSSPseudoClasses::outOfRange == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_OUTOFRANGE);
stateToCheck = NS_EVENT_STATE_OUTOFRANGE;
}
else if (nsCSSPseudoClasses::mozReadOnly == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_MOZ_READONLY);
stateToCheck = NS_EVENT_STATE_MOZ_READONLY;
}
else if (nsCSSPseudoClasses::mozReadWrite == pseudoClass->mAtom) {
result = STATE_CHECK(NS_EVENT_STATE_MOZ_READWRITE);
stateToCheck = NS_EVENT_STATE_MOZ_READWRITE;
}
else {
NS_ERROR("CSS parser parsed a pseudo-class that we do not handle");
result = PR_FALSE; // unknown pseudo class
}
if (stateToCheck) {
// check if the element is event-sensitive for :hover and :active
if ((stateToCheck & (NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE)) &&
data.mCompatMode == eCompatibility_NavQuirks &&
// global selector (but don't check .class):
!aSelector->mTag && !aSelector->mIDList && !aSelector->mAttrList &&
// 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 &&
// important for |IsQuirkEventSensitive|:
data.mIsHTMLContent && !data.mIsHTMLLink &&
!IsQuirkEventSensitive(data.mContentTag)) {
// In quirks mode, only make certain elements sensitive to
// selectors ":hover" and ":active".
result = PR_FALSE;
} else {
if (aStateMask & stateToCheck) {
result = PR_TRUE;
if (aDependence)
*aDependence = PR_TRUE;
} else {
result = (0 != (data.mEventState & stateToCheck));
}
}
}
}
// namespace/tag match
if (result && aSelector->mAttrList) {
// test for attribute match
if (!data.mHasAttributes && !aAttribute) {
// if no attributes on the content, no match
result = localFalse;
result = PR_FALSE;
} else {
NS_ASSERTION(data.mContent,
"Must have content if either data.mHasAttributes or "
"aAttribute is set!");
result = localTrue;
result = PR_TRUE;
nsAttrSelector* attr = aSelector->mAttrList;
do {
if (attr->mAttr == aAttribute) {
// XXX we should really have a namespace, not just an attr
// name, in HasAttributeDependentStyle!
result = PR_TRUE;
if (aDependence)
*aDependence = PR_TRUE;
}
else if (attr->mNameSpace == kNameSpaceID_Unknown) {
// Attr selector with a wildcard namespace. We have to examine all
@ -3161,12 +3143,11 @@ static PRBool SelectorMatches(RuleProcessorData &data,
// matches, evaluate for each namespace (the only namespaces that
// have a chance at matching, of course, are ones that the element
// actually has attributes in), short-circuiting if we ever match.
// Then deal with the localFalse/localTrue stuff.
PRUint32 attrCount = data.mContent->GetAttrCount();
PRInt32 nameSpaceID;
nsCOMPtr<nsIAtom> name;
nsCOMPtr<nsIAtom> prefix;
PRBool attrSelectorMatched = PR_FALSE;
result = PR_FALSE;
for (PRUint32 i = 0; i < attrCount; ++i) {
#ifdef DEBUG
nsresult attrState =
@ -3180,7 +3161,7 @@ static PRBool SelectorMatches(RuleProcessorData &data,
continue;
}
if (attr->mFunction == NS_ATTR_FUNC_SET) {
attrSelectorMatched = PR_TRUE;
result = PR_TRUE;
} else {
nsAutoString value;
#ifdef DEBUG
@ -3188,30 +3169,27 @@ static PRBool SelectorMatches(RuleProcessorData &data,
#endif
data.mContent->GetAttr(nameSpaceID, name, value);
NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
attrSelectorMatched = AttrMatchesValue(attr, value);
result = AttrMatchesValue(attr, value);
}
// At this point |attrSelectorMatched| has been set by us
// At this point |result| has been set by us
// explicitly in this loop. If it's PR_FALSE, we may still match
// -- the content may have another attribute with the same name but
// in a different namespace. But if it's PR_TRUE, we are done (we
// can short-circuit the boolean OR described above).
if (attrSelectorMatched) {
if (result) {
break;
}
}
// Now adjust for a possible negation
result = localTrue == attrSelectorMatched;
}
else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
result =
localTrue == data.mContent->
data.mContent->
AttrValueIs(attr->mNameSpace, attr->mAttr, attr->mValue,
attr->mCaseSensitive ? eCaseMatters : eIgnoreCase);
}
else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) {
result = localFalse;
result = PR_FALSE;
}
else if (attr->mFunction != NS_ATTR_FUNC_SET) {
nsAutoString value;
@ -3220,71 +3198,92 @@ static PRBool SelectorMatches(RuleProcessorData &data,
#endif
data.mContent->GetAttr(attr->mNameSpace, attr->mAttr, value);
NS_ASSERTION(hasAttr, "HasAttr lied");
result = localTrue == AttrMatchesValue(attr, value);
result = AttrMatchesValue(attr, value);
}
attr = attr->mNext;
} while (attr && result);
}
}
if (result && (aSelector->mIDList || aSelector->mClassList)) {
// test for ID & class match
result = localFalse;
// No attributes means no match on class or id
if (data.mHasAttributes) {
nsAtomList* IDList = aSelector->mIDList;
if (result && IDList) {
// test for ID match
result = PR_FALSE;
if (aAttribute && aAttribute == data.mContent->GetIDAttributeName()) {
result = PR_TRUE;
if (aDependence)
*aDependence = PR_TRUE;
}
else if (nsnull != data.mContentID) {
// case sensitivity: bug 93371
PRBool isCaseSensitive = data.mCompatMode != eCompatibility_NavQuirks;
nsAtomList* IDList = aSelector->mIDList;
if (nsnull == IDList ||
(aAttribute && aAttribute == data.mContent->GetIDAttributeName())) {
result = PR_TRUE;
}
else if (nsnull != data.mContentID) {
result = PR_TRUE;
if (isCaseSensitive) {
do {
if (localTrue == (IDList->mAtom != data.mContentID)) {
result = PR_FALSE;
break;
}
IDList = IDList->mNext;
} while (IDList);
} else {
const char* id1Str;
data.mContentID->GetUTF8String(&id1Str);
nsDependentCString id1(id1Str);
do {
const char* id2Str;
IDList->mAtom->GetUTF8String(&id2Str);
if (localTrue !=
id1.Equals(id2Str, nsCaseInsensitiveCStringComparator())) {
result = PR_FALSE;
break;
}
IDList = IDList->mNext;
} while (IDList);
}
}
if (result &&
(!aAttribute || aAttribute != data.mContent->GetClassAttributeName())) {
nsAtomList* classList = aSelector->mClassList;
const nsAttrValue *elementClasses = data.mClasses;
while (nsnull != classList) {
if (localTrue == (!(elementClasses && elementClasses->Contains(classList->mAtom, isCaseSensitive ? eCaseMatters : eIgnoreCase)))) {
const PRBool isCaseSensitive =
data.mCompatMode != eCompatibility_NavQuirks;
result = PR_TRUE;
if (isCaseSensitive) {
do {
if (IDList->mAtom != data.mContentID) {
result = PR_FALSE;
break;
}
classList = classList->mNext;
IDList = IDList->mNext;
} while (IDList);
} else {
const char* id1Str;
data.mContentID->GetUTF8String(&id1Str);
nsDependentCString id1(id1Str);
do {
const char* id2Str;
IDList->mAtom->GetUTF8String(&id2Str);
if (!id1.Equals(id2Str, nsCaseInsensitiveCStringComparator())) {
result = PR_FALSE;
break;
}
IDList = IDList->mNext;
} while (IDList);
}
}
}
if (result && aSelector->mClassList) {
// test for class match
if (aAttribute && aAttribute == data.mContent->GetClassAttributeName()) {
result = PR_TRUE;
if (aDependence)
*aDependence = PR_TRUE;
}
else {
// case sensitivity: bug 93371
const PRBool isCaseSensitive =
data.mCompatMode != eCompatibility_NavQuirks;
nsAtomList* classList = aSelector->mClassList;
const nsAttrValue *elementClasses = data.mClasses;
while (nsnull != classList) {
if (!(elementClasses && elementClasses->Contains(classList->mAtom, isCaseSensitive ? eCaseMatters : eIgnoreCase))) {
result = PR_FALSE;
break;
}
classList = classList->mNext;
}
}
}
// apply SelectorMatches to the negated selectors in the chain
if (result && (nsnull != aSelector->mNegations)) {
result = SelectorMatches(data, aSelector->mNegations, aStateMask,
aAttribute, aNegationIndex+1);
if (!isNegated) {
for (nsCSSSelector *negation = aSelector->mNegations;
result && negation; negation = negation->mNegations) {
PRBool dependence = PR_FALSE;
result = !SelectorMatches(data, negation, aStateMask,
aAttribute, &dependence);
// If the selector does match due to the dependence on aStateMask
// or aAttribute, then we want to keep result true so that the
// final result of SelectorMatches is true. Doing so tells
// StateEnumFunc or AttributeEnumFunc that there is a dependence
// on the state or attribute.
result = result || dependence;
}
}
return result;
}
@ -3350,7 +3349,7 @@ static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
if (! data) {
return PR_FALSE;
}
if (SelectorMatches(*data, selector, 0, nsnull, 0)) {
if (SelectorMatches(*data, selector, 0, nsnull)) {
// to avoid greedy matching, we need to recur if this is a
// descendant combinator and the next combinator is not
if ((NS_IS_GREEDY_OPERATOR(selector->mOperator)) &&
@ -3387,7 +3386,7 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
{
ElementRuleProcessorData* data = (ElementRuleProcessorData*)aData;
if (SelectorMatches(*data, aSelector, 0, nsnull, 0)) {
if (SelectorMatches(*data, aSelector, 0, nsnull)) {
nsCSSSelector *next = aSelector->mNext;
if (!next || SelectorMatchesTree(*data, next)) {
// for performance, require that every implementation of
@ -3440,7 +3439,7 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
if (PRUnichar('+') == selector->mOperator) {
return; // not valid here, can't match
}
if (SelectorMatches(*data, selector, 0, nsnull, 0)) {
if (SelectorMatches(*data, selector, 0, nsnull)) {
selector = selector->mNext;
}
else {
@ -3504,7 +3503,7 @@ PR_STATIC_CALLBACK(PRBool) StateEnumFunc(void* aSelector, void* aData)
StateRuleProcessorData *data = enumData->data;
nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector);
if (SelectorMatches(*data, selector, data->mStateMask, nsnull, 0) &&
if (SelectorMatches(*data, selector, data->mStateMask, nsnull) &&
SelectorMatchesTree(*data, selector->mNext)) {
if (IsSiblingOperator(selector->mOperator))
enumData->change = nsReStyleHint(enumData->change | eReStyle_LaterSiblings);
@ -3554,7 +3553,7 @@ PR_STATIC_CALLBACK(PRBool) AttributeEnumFunc(void* aSelector, void* aData)
AttributeRuleProcessorData *data = enumData->data;
nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector);
if (SelectorMatches(*data, selector, 0, data->mAttribute, 0) &&
if (SelectorMatches(*data, selector, 0, data->mAttribute) &&
SelectorMatchesTree(*data, selector->mNext)) {
if (IsSiblingOperator(selector->mOperator))
enumData->change = nsReStyleHint(enumData->change | eReStyle_LaterSiblings);

View File

@ -131,7 +131,7 @@ private:
void AppendNegationToString(nsAString& aString);
void ToStringInternal(nsAString& aString, nsICSSStyleSheet* aSheet,
PRBool aIsPseudoElem,
PRIntn aNegatedIndex) const;
PRBool aIsNegated) const;
public:
PRInt32 mNameSpace;