From 710439fbb70fcd32b5c2b673cbdcc12a86b33aca Mon Sep 17 00:00:00 2001 From: "glazman%netscape.com" Date: Tue, 20 Mar 2001 11:49:20 +0000 Subject: [PATCH] adds support for :not() css3 pseudo-class; b=71647, r=pierre, sr=hyatt git-svn-id: svn://10.0.0.236/trunk@89901 18797224-902f-48f8-a5cc-f745e15eee43 --- .../content/html/style/src/nsCSSParser.cpp | 157 ++++++- .../content/html/style/src/nsCSSStyleRule.cpp | 93 +++- .../html/style/src/nsCSSStyleSheet.cpp | 407 +++++++++--------- .../content/html/style/src/nsICSSStyleRule.h | 9 +- mozilla/content/shared/public/nsCSSAtomList.h | 2 + mozilla/layout/style/nsCSSParser.cpp | 157 ++++++- mozilla/layout/style/nsCSSStyleRule.cpp | 93 +++- mozilla/layout/style/nsCSSStyleSheet.cpp | 407 +++++++++--------- mozilla/layout/style/nsICSSStyleRule.h | 9 +- 9 files changed, 886 insertions(+), 448 deletions(-) diff --git a/mozilla/content/html/style/src/nsCSSParser.cpp b/mozilla/content/html/style/src/nsCSSParser.cpp index 03ea66fceaa..0395e2b7e92 100644 --- a/mozilla/content/html/style/src/nsCSSParser.cpp +++ b/mozilla/content/html/style/src/nsCSSParser.cpp @@ -197,12 +197,16 @@ protected: void ParseClassSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); void ParsePseudoSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, - PRInt32& aParsingStatus, PRInt32& aErrorCode); + PRInt32& aParsingStatus, PRInt32& aErrorCode, + PRBool aIsNegated); void ParseAttributeSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); void ParseTypeOrUniversalSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode, + PRBool aIsNegated); + void ParseNegatedSimpleSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); @@ -1366,11 +1370,12 @@ static PRBool IsPseudoClass(const nsIAtom* aAtom) (nsCSSAtoms::enabledPseudo == aAtom) || (nsCSSAtoms::firstChildPseudo == aAtom) || (nsCSSAtoms::firstNodePseudo == aAtom) || - (nsCSSAtoms::lastNodePseudo == aAtom) || (nsCSSAtoms::focusPseudo == aAtom) || (nsCSSAtoms::hoverPseudo == aAtom) || (nsCSSAtoms::langPseudo == aAtom) || + (nsCSSAtoms::lastNodePseudo == aAtom) || (nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || (nsCSSAtoms::rootPseudo == aAtom) || (nsCSSAtoms::xblBoundElementPseudo == aAtom) || (nsCSSAtoms::outOfDatePseudo == aAtom) || @@ -1520,6 +1525,9 @@ PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, #define SELECTOR_PARSING_STOPPED_OK 2 #define SELECTOR_PARSING_STOPPED_ERROR 3 +// +// Parses an ID selector #name +// void CSSParserImpl::ParseIDSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, @@ -1539,6 +1547,9 @@ void CSSParserImpl::ParseIDSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } +// +// Parses a class selector .name +// void CSSParserImpl::ParseClassSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, @@ -1560,11 +1571,15 @@ void CSSParserImpl::ParseClassSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } - +// +// Parse a type element selector or a universal selector +// namespace|type or namespace|* or *|* or * +// void CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, - PRInt32& aErrorCode) + PRInt32& aErrorCode, + PRBool aIsNegated) { nsAutoString buffer; if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace @@ -1729,8 +1744,16 @@ void CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask, } } aParsingStatus = SELECTOR_PARSING_ENDED_OK; + if (aIsNegated) { + // restore last token read in case of a negated type selector + UngetToken(); + } } +// +// Parse attribute selectors [attr], [attr=value], [attr|=value], +// [attr~=value], [attr^=value], [attr$=value] and [attr*=value] +// void CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, @@ -1913,10 +1936,14 @@ void CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } +// +// Parse pseudo-classes and pseudo-elements +// void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, - PRInt32& aErrorCode) + PRInt32& aErrorCode, + PRBool aIsNegated) { nsAutoString buffer; if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof @@ -1924,12 +1951,24 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; } + + buffer.Truncate(); + buffer.AppendWithConversion(':'); + buffer.Append(mToken.mIdent); + buffer.ToLowerCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (eCSSToken_Ident != mToken.mType) { // malformed selector + if (eCSSToken_Function != mToken.mType || + !( #ifdef INCLUDE_XUL - if (eCSSToken_Function != mToken.mType || - !IsOutlinerPseudoElement(mToken.mIdent)) { + // -moz-outliner is a pseudo-element and therefore cannot be negated + (!aIsNegated && IsOutlinerPseudoElement(mToken.mIdent)) || #endif - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector but found")); + // the negation pseudo-class is a function + (nsCSSAtoms::notPseudo == pseudo))) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector not found")); + NS_RELEASE(pseudo); UngetToken(); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; @@ -1937,18 +1976,32 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, } #endif } - buffer.Truncate(); - buffer.AppendWithConversion(':'); - buffer.Append(mToken.mIdent); - buffer.ToLowerCase(); - nsIAtom* pseudo = NS_NewAtom(buffer); - if (IsPseudoClass(pseudo)) { - // XXX parse lang pseudo class + + if (nsCSSAtoms::notPseudo == pseudo) { + NS_RELEASE(pseudo); + if (aIsNegated) { // :not() can't be itself negated + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Negation pseudo-class can't be negated")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + // CSS 3 Negation pseudo-class takes one simple selector as argument + ParseNegatedSimpleSelector(aDataMask, aSelector, aParsingStatus, aErrorCode); + if (SELECTOR_PARSING_ENDED_OK != aParsingStatus) { + return; + } + } + else if (IsPseudoClass(pseudo)) { + // XXX parse pseudo classes accepting arguments aDataMask |= SEL_MASK_PCLASS; aSelector.AddPseudoClass(pseudo); NS_RELEASE(pseudo); } else { + if (aIsNegated) { // pseudo-elements can't be negated + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Pseudo-elements can't be negated")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } if (0 == (aDataMask & SEL_MASK_PELEM)) { aDataMask |= SEL_MASK_PELEM; aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later @@ -1992,6 +2045,76 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } +// +// Parse the argument of a negation pseudo-class :not() +// +void CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + // Check if we have the first parenthesis + if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) { + + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; + nsCSSSelector* newSel = new nsCSSSelector(); + if (nsnull == aSelector.mNegations && + ((eCSSToken_ID == mToken.mType) || + mToken.IsSymbol('.') || + mToken.IsSymbol(':') || + mToken.IsSymbol('['))) { + // ID, class and attribute selectors and pseudo-classes are stored in + // the first mNegations attached to a selector + aSelector.mNegations = newSel; + } + if (eCSSToken_ID == mToken.mType) { // #id + ParseIDSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode); + } + else if (mToken.IsSymbol('.')) { // .class + ParseClassSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode); + } + else if (mToken.IsSymbol(':')) { // :pseudo + ParsePseudoSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode, PR_TRUE); + } + else if (mToken.IsSymbol('[')) { // attribute + ParseAttributeSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode); + } + else { + // then it should be a type element or universal selector + if (nsnull == aSelector.mNegations) { + aSelector.mNegations = newSel; + } + newSel = new nsCSSSelector(); + 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; + ParseTypeOrUniversalSelector(aDataMask, *newSel, aParsingStatus, aErrorCode, PR_TRUE); + } + if (SELECTOR_PARSING_STOPPED_ERROR == aParsingStatus) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Malformed simple selector as negation pseudo-class argument")); + return; + } + // close the parenthesis + if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing closing ')' in negation pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing argument in negation pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } +} + /** * This is the format for selectors: * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* @@ -2007,7 +2130,7 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, return PR_FALSE; } - ParseTypeOrUniversalSelector(dataMask, aSelector, parsingStatus, aErrorCode); + ParseTypeOrUniversalSelector(dataMask, aSelector, parsingStatus, aErrorCode, PR_FALSE); if (SELECTOR_PARSING_STOPPED_OK == parsingStatus) { return PR_TRUE; } @@ -2024,7 +2147,7 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, ParseClassSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol(':')) { // :pseudo - ParsePseudoSelector(dataMask, aSelector, parsingStatus, aErrorCode); + ParsePseudoSelector(dataMask, aSelector, parsingStatus, aErrorCode, PR_FALSE); } else if (mToken.IsSymbol('[')) { // attribute ParseAttributeSelector(dataMask, aSelector, parsingStatus, aErrorCode); diff --git a/mozilla/content/html/style/src/nsCSSStyleRule.cpp b/mozilla/content/html/style/src/nsCSSStyleRule.cpp index a30e198964e..6767e00fdad 100644 --- a/mozilla/content/html/style/src/nsCSSStyleRule.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleRule.cpp @@ -90,6 +90,12 @@ static NS_DEFINE_IID(kCSSXULSID, NS_CSS_XUL_SID); #define NS_IF_DELETE(ptr) \ if (nsnull != ptr) { delete ptr; ptr = nsnull; } +#define NS_IF_NEGATED_START(bool,str) \ + if (bool) { str.Append(NS_LITERAL_STRING(":not(")); } + +#define NS_IF_NEGATED_END(bool,str) \ + if (bool) { str.Append(PRUnichar(')')); } + MOZ_DECL_CTOR_COUNTER(nsAtomList) nsAtomList::nsAtomList(nsIAtom* aAtom) @@ -292,6 +298,7 @@ nsCSSSelector::nsCSSSelector(void) mPseudoClassList(nsnull), mAttrList(nsnull), mOperator(0), + mNegations(nsnull), mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSSelector); @@ -309,6 +316,7 @@ nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) mPseudoClassList(nsnull), mAttrList(nsnull), mOperator(aCopy.mOperator), + mNegations(nsnull), mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSSelector); @@ -317,7 +325,8 @@ nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); - + NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); + #ifdef DEBUG_REFS gSelectorCount++; printf( "nsCSSSelector Instances (cp-ctor): %ld\n", (long)gSelectorCount); @@ -342,7 +351,8 @@ nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); NS_IF_DELETE(mAttrList); - + NS_IF_DELETE(mNegations); + mNameSpace = aCopy.mNameSpace; mTag = aCopy.mTag; NS_IF_COPY(mIDList, aCopy.mIDList, nsAtomList); @@ -350,6 +360,7 @@ nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); mOperator = aCopy.mOperator; + NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); NS_IF_ADDREF(mTag); return *this; @@ -404,6 +415,11 @@ PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const return PR_FALSE; } } + if (nsnull != mNegations) { + if (PR_FALSE == mNegations->Equals(aOther->mNegations)) { + return PR_FALSE; + } + } return PR_TRUE; } } @@ -419,6 +435,7 @@ void nsCSSSelector::Reset(void) NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); NS_IF_DELETE(mAttrList); + NS_IF_DELETE(mNegations); mOperator = PRUnichar(0); } @@ -534,6 +551,9 @@ PRInt32 nsCSSSelector::CalcWeight(void) const weight += 0x000100; attr = attr->mNext; } + if (nsnull != mNegations) { + weight += mNegations->CalcWeight(); + } return weight; } @@ -624,6 +644,13 @@ void nsCSSSelector::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) localSize = 0; mAttrList->SizeOf(aSizeOfHandler, localSize); } + + // don't forget the negated selectors + if(mNegations) { + localSize = 0; + mNegations->SizeOf(aSizeOfHandler, localSize); + } + // finally chain to the next... if(mNext){ localSize = 0; @@ -644,19 +671,36 @@ static PRBool IsPseudoElement(nsIAtom* aAtom) return PR_FALSE; } -nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem ) const +void nsCSSSelector::AppendNegationToString(nsAWritableString& aString) +{ + aString.Append(NS_LITERAL_STRING(":not(")); +} + +// +// Builds the textual representation of a selector. Called by DOM 2 CSS +// StyleRule:selectorText +// +nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem, + PRInt8 aNegatedIndex) const { const PRUnichar* temp; + PRBool aIsNegated = PRBool(0 < aNegatedIndex); // selectors are linked from right-to-left, so the next selector in the linked list // actually precedes this one in the resulting string - if (mNext) { - mNext->ToString(aString, aSheet, IsPseudoElement(mTag)); - if (!IsPseudoElement(mTag)) { + if (mNext) { + mNext->ToString(aString, aSheet, IsPseudoElement(mTag), PR_FALSE); + if (!aIsNegated && !IsPseudoElement(mTag)) { // don't add a leading whitespace if we have a pseudo-element + // or a negated simple selector 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) + } // append the namespace prefix if (mNameSpace > 0) { @@ -672,23 +716,30 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* aString.Append(PRUnichar('|')); } } - // smells like a universal selector if (!mTag && !mIDList && !mClassList) { - aString.Append(PRUnichar('*')); + if (1 != aNegatedIndex) { + aString.Append(PRUnichar('*')); + } + if (1 < aNegatedIndex) { + NS_IF_NEGATED_END(aIsNegated, aString) + } } else { // Append the tag name, if there is one if (mTag) { mTag->GetUnicode(&temp); aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) } // Append the id, if there is one if (mIDList) { nsAtomList* list = mIDList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); + NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(PRUnichar('#')); aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } @@ -697,8 +748,10 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* nsAtomList* list = mClassList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); + NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(PRUnichar('.')); aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } @@ -708,6 +761,7 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* 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) { @@ -748,6 +802,7 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* // Append the value aString.Append(list->mValue); aString.Append(PRUnichar(']')); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } @@ -757,18 +812,28 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* nsAtomList* list = mPseudoClassList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); + NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } - // Append the operator - if (mOperator && !aIsPseudoElem) { + + if (mNegations) { + // chain all the negated selectors + mNegations->ToString(aString, aSheet, PR_FALSE, aNegatedIndex + 1); + } + + // Append the operator only if the selector is not negated and is not + // a pseudo-element + if (!aIsNegated && mOperator && !aIsPseudoElem) { aString.Append(PRUnichar(' ')); aString.Append(mOperator); } return NS_OK; } + // -- CSSImportantRule ------------------------------- static nscoord CalcLength(const nsCSSValue& aValue, const nsFont& aFont, @@ -1452,7 +1517,8 @@ void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText) void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const { - mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag) ); + mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag), + 0 ); } PRUint32 CSSStyleRuleImpl::GetLineNumber(void) const @@ -3578,7 +3644,8 @@ CSSStyleRuleImpl::GetType(PRUint16* aType) NS_IMETHODIMP CSSStyleRuleImpl::GetCssText(nsAWritableString& aCssText) { - mSelector.ToString( aCssText, mSheet, IsPseudoElement(mSelector.mTag) ); + mSelector.ToString( aCssText, mSheet, IsPseudoElement(mSelector.mTag), + 0 ); aCssText.Append(PRUnichar(' ')); aCssText.Append(PRUnichar('{')); aCssText.Append(PRUnichar(' ')); @@ -3619,7 +3686,7 @@ CSSStyleRuleImpl::GetParentRule(nsIDOMCSSRule** aParentRule) NS_IMETHODIMP CSSStyleRuleImpl::GetSelectorText(nsAWritableString& aSelectorText) { - mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag) ); + mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag), 0 ); return NS_OK; } diff --git a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp index f696824af86..6b803fddb9f 100644 --- a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp @@ -2931,167 +2931,229 @@ static PRBool IsSignificantChild(nsIContent* aChild, PRBool aAcceptNonWhitespace static PRBool SelectorMatches(SelectorMatchesData &data, nsCSSSelector* aSelector, - PRBool aTestState) + PRBool aTestState, + PRInt8 aNegationIndex) { - PRBool result = PR_FALSE; + // if we are dealing with negations, reverse the values of PR_TRUE and PR_FALSE + PRBool localFalse = PRBool(0 < aNegationIndex); + PRBool localTrue = PRBool(0 == aNegationIndex); + PRBool checkType = (0 == aNegationIndex) || (1 < aNegationIndex); + PRBool result = localFalse; + nsAutoString buffer; - // Bail out early if we can - if(kNameSpaceID_Unknown != aSelector->mNameSpace) { - if(data.mNameSpaceID != aSelector->mNameSpace) { + // Bail out early if we can. Do not perform the test if aNegationIndex==1 + // because it then contains only negated IDs, classes, attributes and pseudo- + // classes + if (checkType) { + if (kNameSpaceID_Unknown != aSelector->mNameSpace) { + if (data.mNameSpaceID != aSelector->mNameSpace) { + return result; + } + } + } + if (checkType) { + if (localTrue == ((nsnull != aSelector->mTag) && (aSelector->mTag != data.mContentTag))) { return result; } + if (1 < aNegationIndex) { + // optimization : no other selector to test on negated type or + // universal selector + return localTrue; + } } - if ((nsnull == aSelector->mTag) || (aSelector->mTag == data.mContentTag)) { - - result = PR_TRUE; - // namespace/tag match - if (nsnull != aSelector->mAttrList) { // test for attribute match - // if no attributes on the content, no match - if(!data.mHasAttributes) { - result = PR_FALSE; - } else { - nsAttrSelector* attr = aSelector->mAttrList; - do { - nsAutoString value, partValue; - nsresult attrState = data.mContent->GetAttribute(attr->mNameSpace, attr->mAttr, value); - if (NS_FAILED(attrState) || (NS_CONTENT_ATTR_NOT_THERE == attrState)) { - result = PR_FALSE; + result = localTrue; + // namespace/tag match + if (nsnull != aSelector->mAttrList) { // test for attribute match + // if no attributes on the content, no match + if(!data.mHasAttributes) { + result = localFalse; + } else { + nsAttrSelector* attr = aSelector->mAttrList; + do { + nsAutoString value, partValue; + nsresult attrState = data.mContent->GetAttribute(attr->mNameSpace, attr->mAttr, value); + if (NS_FAILED(attrState) || (NS_CONTENT_ATTR_NOT_THERE == attrState)) { + result = localFalse; + } + else { + PRBool isCaseSensitive = (attr->mCaseSensitive && !data.mIsHTMLContent); // Bug 24390: html attributes should not be case-sensitive + switch (attr->mFunction) { + case NS_ATTR_FUNC_SET: break; + case NS_ATTR_FUNC_EQUALS: + if (isCaseSensitive) { + result = PRBool(localTrue == value.Equals(attr->mValue)); + } + else { + result = PRBool(localTrue == value.EqualsIgnoreCase(attr->mValue)); + } + break; + case NS_ATTR_FUNC_INCLUDES: + result = PRBool(localTrue == ValueIncludes(value, attr->mValue, isCaseSensitive)); + break; + case NS_ATTR_FUNC_DASHMATCH: + result = PRBool(localTrue == ValueDashMatch(value, attr->mValue, isCaseSensitive)); + break; + case NS_ATTR_FUNC_ENDSMATCH: + value.Right(partValue, attr->mValue.Length()); + if (isCaseSensitive) { + result = PRBool(localTrue == partValue.Equals(attr->mValue)); + } + else { + result = PRBool(localTrue == partValue.EqualsIgnoreCase(attr->mValue)); + } + break; + case NS_ATTR_FUNC_BEGINSMATCH: + value.Left(partValue, attr->mValue.Length()); + if (isCaseSensitive) { + result = PRBool(localTrue == partValue.Equals(attr->mValue)); + } + else { + result = PRBool(localTrue == partValue.EqualsIgnoreCase(attr->mValue)); + } + break; + case NS_ATTR_FUNC_CONTAINSMATCH: + result = PRBool(localTrue == (-1 != value.Find(attr->mValue, isCaseSensitive))); + break; } - else { - PRBool isCaseSensitive = (attr->mCaseSensitive && !data.mIsHTMLContent); // Bug 24390: html attributes should not be case-sensitive - switch (attr->mFunction) { - case NS_ATTR_FUNC_SET: break; - case NS_ATTR_FUNC_EQUALS: - if (isCaseSensitive) { - result = value.Equals(attr->mValue); - } - else { - result = value.EqualsIgnoreCase(attr->mValue); - } - break; - case NS_ATTR_FUNC_INCLUDES: - result = ValueIncludes(value, attr->mValue, isCaseSensitive); - break; - case NS_ATTR_FUNC_DASHMATCH: - result = ValueDashMatch(value, attr->mValue, isCaseSensitive); - break; - case NS_ATTR_FUNC_ENDSMATCH: - value.Right(partValue, attr->mValue.Length()); - if (isCaseSensitive) { - result = partValue.Equals(attr->mValue); - } - else { - result = partValue.EqualsIgnoreCase(attr->mValue); - } - break; - case NS_ATTR_FUNC_BEGINSMATCH: - value.Left(partValue, attr->mValue.Length()); - if (isCaseSensitive) { - result = partValue.Equals(attr->mValue); - } - else { - result = partValue.EqualsIgnoreCase(attr->mValue); - } - break; - case NS_ATTR_FUNC_CONTAINSMATCH: - result = (-1 != value.Find(attr->mValue, isCaseSensitive)); - break; - } - } - attr = attr->mNext; - } while ((PR_TRUE == result) && (nsnull != attr)); - } + } + attr = attr->mNext; + } while ((localTrue == result) && (nsnull != attr)); } - if ((PR_TRUE == result) && - ((nsnull != aSelector->mIDList) || (nsnull != aSelector->mClassList))) { // test for ID & class match - result = PR_FALSE; - if (data.mStyledContent) { - nsAtomList* IDList = aSelector->mIDList; - if (nsnull == IDList) { - result = PR_TRUE; - } - else if (nsnull != data.mContentID) { - result = PR_TRUE; - while (nsnull != IDList) { - if (IDList->mAtom != data.mContentID) { - result = PR_FALSE; - break; - } - IDList = IDList->mNext; + } + if ((localTrue == result) && + ((nsnull != aSelector->mIDList) || (nsnull != aSelector->mClassList))) { // test for ID & class match + result = localFalse; + if (data.mStyledContent) { + nsAtomList* IDList = aSelector->mIDList; + if (nsnull == IDList) { + result = localTrue; + } + else if (nsnull != data.mContentID) { + result = localTrue; + while (nsnull != IDList) { + if (IDList->mAtom != data.mContentID) { + result = localFalse; + break; } + IDList = IDList->mNext; } - - if (PR_TRUE == result) { - nsAtomList* classList = aSelector->mClassList; - while (nsnull != classList) { - if (NS_COMFALSE == data.mStyledContent->HasClass(classList->mAtom)) { - result = PR_FALSE; - break; - } - classList = classList->mNext; + } + + if (localTrue == result) { + nsAtomList* classList = aSelector->mClassList; + while (nsnull != classList) { + if (NS_COMFALSE == data.mStyledContent->HasClass(classList->mAtom)) { + result = localFalse; + break; } + classList = classList->mNext; } } } - if ((PR_TRUE == result) && - (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match - // first-child, root, lang, active, focus, hover, link, outOfDate, visited - // XXX disabled, enabled, selected, selection - nsAtomList* pseudoClass = aSelector->mPseudoClassList; + } + if ((localTrue == result) && + (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match + // first-child, root, lang, active, focus, hover, link, outOfDate, visited + // XXX disabled, enabled, selected, selection + nsAtomList* pseudoClass = aSelector->mPseudoClassList; - while ((PR_TRUE == result) && (nsnull != pseudoClass)) { - if ((nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) || - (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom) ) { - nsIContent* firstChild = nsnull; - nsIContent* parent = data.mParentContent; - if (parent) { - PRInt32 index = -1; - do { - parent->ChildAt(++index, firstChild); - if (firstChild) { // skip text & comments (and whitespace for firstNode as well) - if (IsSignificantChild(firstChild, (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom))) { - break; - } - NS_RELEASE(firstChild); - } - else { + while ((localTrue == result) && (nsnull != pseudoClass)) { + if ((nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) || + (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom) ) { + nsIContent* firstChild = nsnull; + nsIContent* parent = data.mParentContent; + if (parent) { + PRInt32 index = -1; + do { + parent->ChildAt(++index, firstChild); + if (firstChild) { // skip text & comments (and whitespace for firstNode as well) + if (IsSignificantChild(firstChild, (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom))) { break; } - } while (1 == 1); - } - result = PRBool(data.mContent == firstChild); - NS_IF_RELEASE(firstChild); + NS_RELEASE(firstChild); + } + else { + break; + } + } while (1 == 1); } - else if (nsCSSAtoms::lastNodePseudo == pseudoClass->mAtom) { - nsIContent* lastChild = nsnull; - nsIContent* parent = data.mParentContent; - if (parent) { - PRInt32 index; - parent->ChildCount(index); - do { - parent->ChildAt(--index, lastChild); - if (lastChild) { // skip whitespace text & comments - if (IsSignificantChild(lastChild, PR_TRUE)) { - break; - } - NS_RELEASE(lastChild); - } - else { + result = PRBool(localTrue == (data.mContent == firstChild)); + NS_IF_RELEASE(firstChild); + } + else if (nsCSSAtoms::lastNodePseudo == pseudoClass->mAtom) { + nsIContent* lastChild = nsnull; + nsIContent* parent = data.mParentContent; + if (parent) { + PRInt32 index; + parent->ChildCount(index); + do { + parent->ChildAt(--index, lastChild); + if (lastChild) { // skip whitespace text & comments + if (IsSignificantChild(lastChild, PR_TRUE)) { break; } - } while (1 == 1); - } - result = PRBool(data.mContent == lastChild); - NS_IF_RELEASE(lastChild); + NS_RELEASE(lastChild); + } + else { + break; + } + } while (1 == 1); } - else if (nsCSSAtoms::rootPseudo == pseudoClass->mAtom) { - if (data.mParentContent) { - result = PR_FALSE; + result = PRBool(localTrue == (data.mContent == lastChild)); + NS_IF_RELEASE(lastChild); + } + else if (nsCSSAtoms::rootPseudo == pseudoClass->mAtom) { + if (data.mParentContent) { + result = localFalse; + } + else { + result = localTrue; + } + } + else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { + // XXX not yet implemented + result = localFalse; + } + else if (IsEventPseudo(pseudoClass->mAtom)) { + // check if the element is event-sensitive + + // Quirk Mode: check to see if the element is event-sensitive + // - see if the selector applies to event pseudo classes + // NOTE: we distinguish between global and subjected selectors so + // pass that information on to the determining routine + PRBool isSelectorGlobal = aSelector->mTag==nsnull ? PR_TRUE : PR_FALSE; + if ((data.mIsQuirkMode) && + (!IsEventSensitive(pseudoClass->mAtom, data.mContentTag, isSelectorGlobal))){ + result = localFalse; + } else if (aTestState) { + if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_ACTIVE))); } - else { - result = PR_TRUE; + else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_FOCUS))); + } + else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_HOVER))); + } + else if (nsCSSAtoms::dragOverPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_DRAGOVER))); + } + } + } + else if (IsLinkPseudo(pseudoClass->mAtom)) { + if (data.mIsHTMLLink || data.mIsSimpleXLink) { + if ((localFalse != result) && (aTestState)) { + if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (eLinkState_Unvisited == data.mLinkState)); + } + else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (eLinkState_OutOfDate == data.mLinkState)); + } + else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (eLinkState_Visited == data.mLinkState)); + } } } else if (nsCSSAtoms::xblBoundElementPseudo == pseudoClass->mAtom) { @@ -3106,63 +3168,22 @@ static PRBool SelectorMatches(SelectorMatchesData &data, if (data.mStyleRuleSupplier) data.mStyleRuleSupplier->MatchesScopedRoot(data.mContent, &result); else - result = PR_FALSE; - } - else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { - // XXX not yet implemented - result = PR_FALSE; - } - else if (IsEventPseudo(pseudoClass->mAtom)) { - // check if the element is event-sensitive - - // Quirk Mode: check to see if the element is event-sensitive - // - see if the selector applies to event pseudo classes - // NOTE: we distinguish between global and subjected selectors so - // pass that information on to the determining routine - PRBool isSelectorGlobal = aSelector->mTag==nsnull ? PR_TRUE : PR_FALSE; - if ((data.mIsQuirkMode) && - (!IsEventSensitive(pseudoClass->mAtom, data.mContentTag, isSelectorGlobal))){ - result = PR_FALSE; - } else if (aTestState) { - if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_ACTIVE)); - } - else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_FOCUS)); - } - else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_HOVER)); - } - else if (nsCSSAtoms::dragOverPseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_DRAGOVER)); - } - } - } - else if (IsLinkPseudo(pseudoClass->mAtom)) { - if (data.mIsHTMLLink || data.mIsSimpleXLink) { - if ((PR_FALSE != result) && (aTestState)) { - if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { - result = PRBool(eLinkState_Unvisited == data.mLinkState); - } - else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { - result = PRBool(eLinkState_OutOfDate == data.mLinkState); - } - else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { - result = PRBool(eLinkState_Visited == data.mLinkState); - } - } - } - else { - result = PR_FALSE; // not a link - } + result = localFalse; } else { - result = PR_FALSE; // unknown pseudo class + result = localFalse; // not a link } - pseudoClass = pseudoClass->mNext; } + else { + result = localFalse; // unknown pseudo class + } + pseudoClass = pseudoClass->mNext; } } + // apply SelectorMatches to the negated selectors in the chain + if ((localTrue == result) && (nsnull != aSelector->mNegations)) { + result = SelectorMatches(data, aSelector->mNegations, aTestState, aNegationIndex+1); + } return result; } @@ -3219,7 +3240,7 @@ static PRBool SelectorMatchesTree(SelectorMatchesData &data, nsCompatibility compat = data.mIsQuirkMode ? eCompatibility_NavQuirks : eCompatibility_Standard; SelectorMatchesData newdata(data.mPresContext, content, data.mParentContext, data.mResults, &compat); - if (SelectorMatches(newdata, selector, PR_TRUE)) { + if (SelectorMatches(newdata, selector, PR_TRUE, 0)) { // to avoid greedy matching, we need to recurse if this is a // descendant combinator and the next combinator is not if ((NS_IS_GREEDY_OPERATOR(selector->mOperator)) && @@ -3262,7 +3283,7 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) ContentEnumData* data = (ContentEnumData*)aData; nsCSSSelector* selector = aRule->FirstSelector(); - if (SelectorMatches(*data, selector, PR_TRUE)) { + if (SelectorMatches(*data, selector, PR_TRUE, 0)) { selector = selector->mNext; if (SelectorMatchesTree(*data, selector)) { nsIStyleRule* iRule; @@ -3368,7 +3389,7 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) if (PRUnichar('+') == selector->mOperator) { return; // not valid here, can't match } - if (SelectorMatches(*data, selector, PR_TRUE)) { + if (SelectorMatches(*data, selector, PR_TRUE, 0)) { selector = selector->mNext; } else { @@ -3452,7 +3473,7 @@ PRBool PR_CALLBACK StateEnumFunc(void* aSelector, void* aData) StateEnumData* data = (StateEnumData*)aData; nsCSSSelector* selector = (nsCSSSelector*)aSelector; - if (SelectorMatches(*data, selector, PR_FALSE)) { + if (SelectorMatches(*data, selector, PR_FALSE, 0)) { selector = selector->mNext; if (SelectorMatchesTree(*data, selector)) { return PR_FALSE; diff --git a/mozilla/content/html/style/src/nsICSSStyleRule.h b/mozilla/content/html/style/src/nsICSSStyleRule.h index 6fb1b739bfc..d1951630e66 100644 --- a/mozilla/content/html/style/src/nsICSSStyleRule.h +++ b/mozilla/content/html/style/src/nsICSSStyleRule.h @@ -103,7 +103,12 @@ public: PRInt32 CalcWeight(void) const; void SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize); - nsresult ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem ) const; + nsresult ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, + PRBool aIsPseudoElem, PRInt8 aNegatedIndex ) const; + +private: + + void AppendNegationToString(nsAWritableString& aString); public: PRInt32 mNameSpace; @@ -113,10 +118,12 @@ public: nsAtomList* mPseudoClassList; nsAttrSelector* mAttrList; PRUnichar mOperator; + nsCSSSelector* mNegations; nsCSSSelector* mNext; }; + // IID for the nsICSSStyleRule interface {7c277af0-af19-11d1-8031-006008159b5a} #define NS_ICSS_STYLE_RULE_IID \ {0x7c277af0, 0xaf19, 0x11d1, {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}} diff --git a/mozilla/content/shared/public/nsCSSAtomList.h b/mozilla/content/shared/public/nsCSSAtomList.h index 7ca31d05821..9aa36e49a9e 100644 --- a/mozilla/content/shared/public/nsCSSAtomList.h +++ b/mozilla/content/shared/public/nsCSSAtomList.h @@ -66,6 +66,8 @@ CSS_ATOM(linkPseudo, ":link") CSS_ATOM(menuPseudo, ":menu") +CSS_ATOM(notPseudo, ":not") + CSS_ATOM(outOfDatePseudo, ":out-of-date") CSS_ATOM(rootPseudo, ":root") diff --git a/mozilla/layout/style/nsCSSParser.cpp b/mozilla/layout/style/nsCSSParser.cpp index 03ea66fceaa..0395e2b7e92 100644 --- a/mozilla/layout/style/nsCSSParser.cpp +++ b/mozilla/layout/style/nsCSSParser.cpp @@ -197,12 +197,16 @@ protected: void ParseClassSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); void ParsePseudoSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, - PRInt32& aParsingStatus, PRInt32& aErrorCode); + PRInt32& aParsingStatus, PRInt32& aErrorCode, + PRBool aIsNegated); void ParseAttributeSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); void ParseTypeOrUniversalSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, + PRInt32& aParsingStatus, PRInt32& aErrorCode, + PRBool aIsNegated); + void ParseNegatedSimpleSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); @@ -1366,11 +1370,12 @@ static PRBool IsPseudoClass(const nsIAtom* aAtom) (nsCSSAtoms::enabledPseudo == aAtom) || (nsCSSAtoms::firstChildPseudo == aAtom) || (nsCSSAtoms::firstNodePseudo == aAtom) || - (nsCSSAtoms::lastNodePseudo == aAtom) || (nsCSSAtoms::focusPseudo == aAtom) || (nsCSSAtoms::hoverPseudo == aAtom) || (nsCSSAtoms::langPseudo == aAtom) || + (nsCSSAtoms::lastNodePseudo == aAtom) || (nsCSSAtoms::linkPseudo == aAtom) || + (nsCSSAtoms::outOfDatePseudo == aAtom) || (nsCSSAtoms::rootPseudo == aAtom) || (nsCSSAtoms::xblBoundElementPseudo == aAtom) || (nsCSSAtoms::outOfDatePseudo == aAtom) || @@ -1520,6 +1525,9 @@ PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, #define SELECTOR_PARSING_STOPPED_OK 2 #define SELECTOR_PARSING_STOPPED_ERROR 3 +// +// Parses an ID selector #name +// void CSSParserImpl::ParseIDSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, @@ -1539,6 +1547,9 @@ void CSSParserImpl::ParseIDSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } +// +// Parses a class selector .name +// void CSSParserImpl::ParseClassSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, @@ -1560,11 +1571,15 @@ void CSSParserImpl::ParseClassSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } - +// +// Parse a type element selector or a universal selector +// namespace|type or namespace|* or *|* or * +// void CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, - PRInt32& aErrorCode) + PRInt32& aErrorCode, + PRBool aIsNegated) { nsAutoString buffer; if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace @@ -1729,8 +1744,16 @@ void CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask, } } aParsingStatus = SELECTOR_PARSING_ENDED_OK; + if (aIsNegated) { + // restore last token read in case of a negated type selector + UngetToken(); + } } +// +// Parse attribute selectors [attr], [attr=value], [attr|=value], +// [attr~=value], [attr^=value], [attr$=value] and [attr*=value] +// void CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, @@ -1913,10 +1936,14 @@ void CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } +// +// Parse pseudo-classes and pseudo-elements +// void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, - PRInt32& aErrorCode) + PRInt32& aErrorCode, + PRBool aIsNegated) { nsAutoString buffer; if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof @@ -1924,12 +1951,24 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; } + + buffer.Truncate(); + buffer.AppendWithConversion(':'); + buffer.Append(mToken.mIdent); + buffer.ToLowerCase(); + nsIAtom* pseudo = NS_NewAtom(buffer); + if (eCSSToken_Ident != mToken.mType) { // malformed selector + if (eCSSToken_Function != mToken.mType || + !( #ifdef INCLUDE_XUL - if (eCSSToken_Function != mToken.mType || - !IsOutlinerPseudoElement(mToken.mIdent)) { + // -moz-outliner is a pseudo-element and therefore cannot be negated + (!aIsNegated && IsOutlinerPseudoElement(mToken.mIdent)) || #endif - REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector but found")); + // the negation pseudo-class is a function + (nsCSSAtoms::notPseudo == pseudo))) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector not found")); + NS_RELEASE(pseudo); UngetToken(); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; @@ -1937,18 +1976,32 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, } #endif } - buffer.Truncate(); - buffer.AppendWithConversion(':'); - buffer.Append(mToken.mIdent); - buffer.ToLowerCase(); - nsIAtom* pseudo = NS_NewAtom(buffer); - if (IsPseudoClass(pseudo)) { - // XXX parse lang pseudo class + + if (nsCSSAtoms::notPseudo == pseudo) { + NS_RELEASE(pseudo); + if (aIsNegated) { // :not() can't be itself negated + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Negation pseudo-class can't be negated")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + // CSS 3 Negation pseudo-class takes one simple selector as argument + ParseNegatedSimpleSelector(aDataMask, aSelector, aParsingStatus, aErrorCode); + if (SELECTOR_PARSING_ENDED_OK != aParsingStatus) { + return; + } + } + else if (IsPseudoClass(pseudo)) { + // XXX parse pseudo classes accepting arguments aDataMask |= SEL_MASK_PCLASS; aSelector.AddPseudoClass(pseudo); NS_RELEASE(pseudo); } else { + if (aIsNegated) { // pseudo-elements can't be negated + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Pseudo-elements can't be negated")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } if (0 == (aDataMask & SEL_MASK_PELEM)) { aDataMask |= SEL_MASK_PELEM; aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later @@ -1992,6 +2045,76 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, aParsingStatus = SELECTOR_PARSING_ENDED_OK; } +// +// Parse the argument of a negation pseudo-class :not() +// +void CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask, + nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + // Check if we have the first parenthesis + if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) { + + if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof + REPORT_UNEXPECTED_EOF(); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + aParsingStatus = SELECTOR_PARSING_ENDED_OK; + nsCSSSelector* newSel = new nsCSSSelector(); + if (nsnull == aSelector.mNegations && + ((eCSSToken_ID == mToken.mType) || + mToken.IsSymbol('.') || + mToken.IsSymbol(':') || + mToken.IsSymbol('['))) { + // ID, class and attribute selectors and pseudo-classes are stored in + // the first mNegations attached to a selector + aSelector.mNegations = newSel; + } + if (eCSSToken_ID == mToken.mType) { // #id + ParseIDSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode); + } + else if (mToken.IsSymbol('.')) { // .class + ParseClassSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode); + } + else if (mToken.IsSymbol(':')) { // :pseudo + ParsePseudoSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode, PR_TRUE); + } + else if (mToken.IsSymbol('[')) { // attribute + ParseAttributeSelector(aDataMask, *aSelector.mNegations, aParsingStatus, aErrorCode); + } + else { + // then it should be a type element or universal selector + if (nsnull == aSelector.mNegations) { + aSelector.mNegations = newSel; + } + newSel = new nsCSSSelector(); + 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; + ParseTypeOrUniversalSelector(aDataMask, *newSel, aParsingStatus, aErrorCode, PR_TRUE); + } + if (SELECTOR_PARSING_STOPPED_ERROR == aParsingStatus) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Malformed simple selector as negation pseudo-class argument")); + return; + } + // close the parenthesis + if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing closing ')' in negation pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing argument in negation pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } +} + /** * This is the format for selectors: * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* @@ -2007,7 +2130,7 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, return PR_FALSE; } - ParseTypeOrUniversalSelector(dataMask, aSelector, parsingStatus, aErrorCode); + ParseTypeOrUniversalSelector(dataMask, aSelector, parsingStatus, aErrorCode, PR_FALSE); if (SELECTOR_PARSING_STOPPED_OK == parsingStatus) { return PR_TRUE; } @@ -2024,7 +2147,7 @@ PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode, ParseClassSelector(dataMask, aSelector, parsingStatus, aErrorCode); } else if (mToken.IsSymbol(':')) { // :pseudo - ParsePseudoSelector(dataMask, aSelector, parsingStatus, aErrorCode); + ParsePseudoSelector(dataMask, aSelector, parsingStatus, aErrorCode, PR_FALSE); } else if (mToken.IsSymbol('[')) { // attribute ParseAttributeSelector(dataMask, aSelector, parsingStatus, aErrorCode); diff --git a/mozilla/layout/style/nsCSSStyleRule.cpp b/mozilla/layout/style/nsCSSStyleRule.cpp index a30e198964e..6767e00fdad 100644 --- a/mozilla/layout/style/nsCSSStyleRule.cpp +++ b/mozilla/layout/style/nsCSSStyleRule.cpp @@ -90,6 +90,12 @@ static NS_DEFINE_IID(kCSSXULSID, NS_CSS_XUL_SID); #define NS_IF_DELETE(ptr) \ if (nsnull != ptr) { delete ptr; ptr = nsnull; } +#define NS_IF_NEGATED_START(bool,str) \ + if (bool) { str.Append(NS_LITERAL_STRING(":not(")); } + +#define NS_IF_NEGATED_END(bool,str) \ + if (bool) { str.Append(PRUnichar(')')); } + MOZ_DECL_CTOR_COUNTER(nsAtomList) nsAtomList::nsAtomList(nsIAtom* aAtom) @@ -292,6 +298,7 @@ nsCSSSelector::nsCSSSelector(void) mPseudoClassList(nsnull), mAttrList(nsnull), mOperator(0), + mNegations(nsnull), mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSSelector); @@ -309,6 +316,7 @@ nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) mPseudoClassList(nsnull), mAttrList(nsnull), mOperator(aCopy.mOperator), + mNegations(nsnull), mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSSelector); @@ -317,7 +325,8 @@ nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); - + NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); + #ifdef DEBUG_REFS gSelectorCount++; printf( "nsCSSSelector Instances (cp-ctor): %ld\n", (long)gSelectorCount); @@ -342,7 +351,8 @@ nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); NS_IF_DELETE(mAttrList); - + NS_IF_DELETE(mNegations); + mNameSpace = aCopy.mNameSpace; mTag = aCopy.mTag; NS_IF_COPY(mIDList, aCopy.mIDList, nsAtomList); @@ -350,6 +360,7 @@ nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); mOperator = aCopy.mOperator; + NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); NS_IF_ADDREF(mTag); return *this; @@ -404,6 +415,11 @@ PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const return PR_FALSE; } } + if (nsnull != mNegations) { + if (PR_FALSE == mNegations->Equals(aOther->mNegations)) { + return PR_FALSE; + } + } return PR_TRUE; } } @@ -419,6 +435,7 @@ void nsCSSSelector::Reset(void) NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); NS_IF_DELETE(mAttrList); + NS_IF_DELETE(mNegations); mOperator = PRUnichar(0); } @@ -534,6 +551,9 @@ PRInt32 nsCSSSelector::CalcWeight(void) const weight += 0x000100; attr = attr->mNext; } + if (nsnull != mNegations) { + weight += mNegations->CalcWeight(); + } return weight; } @@ -624,6 +644,13 @@ void nsCSSSelector::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) localSize = 0; mAttrList->SizeOf(aSizeOfHandler, localSize); } + + // don't forget the negated selectors + if(mNegations) { + localSize = 0; + mNegations->SizeOf(aSizeOfHandler, localSize); + } + // finally chain to the next... if(mNext){ localSize = 0; @@ -644,19 +671,36 @@ static PRBool IsPseudoElement(nsIAtom* aAtom) return PR_FALSE; } -nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem ) const +void nsCSSSelector::AppendNegationToString(nsAWritableString& aString) +{ + aString.Append(NS_LITERAL_STRING(":not(")); +} + +// +// Builds the textual representation of a selector. Called by DOM 2 CSS +// StyleRule:selectorText +// +nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem, + PRInt8 aNegatedIndex) const { const PRUnichar* temp; + PRBool aIsNegated = PRBool(0 < aNegatedIndex); // selectors are linked from right-to-left, so the next selector in the linked list // actually precedes this one in the resulting string - if (mNext) { - mNext->ToString(aString, aSheet, IsPseudoElement(mTag)); - if (!IsPseudoElement(mTag)) { + if (mNext) { + mNext->ToString(aString, aSheet, IsPseudoElement(mTag), PR_FALSE); + if (!aIsNegated && !IsPseudoElement(mTag)) { // don't add a leading whitespace if we have a pseudo-element + // or a negated simple selector 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) + } // append the namespace prefix if (mNameSpace > 0) { @@ -672,23 +716,30 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* aString.Append(PRUnichar('|')); } } - // smells like a universal selector if (!mTag && !mIDList && !mClassList) { - aString.Append(PRUnichar('*')); + if (1 != aNegatedIndex) { + aString.Append(PRUnichar('*')); + } + if (1 < aNegatedIndex) { + NS_IF_NEGATED_END(aIsNegated, aString) + } } else { // Append the tag name, if there is one if (mTag) { mTag->GetUnicode(&temp); aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) } // Append the id, if there is one if (mIDList) { nsAtomList* list = mIDList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); + NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(PRUnichar('#')); aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } @@ -697,8 +748,10 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* nsAtomList* list = mClassList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); + NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(PRUnichar('.')); aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } @@ -708,6 +761,7 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* 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) { @@ -748,6 +802,7 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* // Append the value aString.Append(list->mValue); aString.Append(PRUnichar(']')); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } @@ -757,18 +812,28 @@ nsresult nsCSSSelector::ToString( nsAWritableString& aString, nsICSSStyleSheet* nsAtomList* list = mPseudoClassList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); + NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(temp); + NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } } - // Append the operator - if (mOperator && !aIsPseudoElem) { + + if (mNegations) { + // chain all the negated selectors + mNegations->ToString(aString, aSheet, PR_FALSE, aNegatedIndex + 1); + } + + // Append the operator only if the selector is not negated and is not + // a pseudo-element + if (!aIsNegated && mOperator && !aIsPseudoElem) { aString.Append(PRUnichar(' ')); aString.Append(mOperator); } return NS_OK; } + // -- CSSImportantRule ------------------------------- static nscoord CalcLength(const nsCSSValue& aValue, const nsFont& aFont, @@ -1452,7 +1517,8 @@ void CSSStyleRuleImpl::SetSourceSelectorText(const nsString& aSelectorText) void CSSStyleRuleImpl::GetSourceSelectorText(nsString& aSelectorText) const { - mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag) ); + mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag), + 0 ); } PRUint32 CSSStyleRuleImpl::GetLineNumber(void) const @@ -3578,7 +3644,8 @@ CSSStyleRuleImpl::GetType(PRUint16* aType) NS_IMETHODIMP CSSStyleRuleImpl::GetCssText(nsAWritableString& aCssText) { - mSelector.ToString( aCssText, mSheet, IsPseudoElement(mSelector.mTag) ); + mSelector.ToString( aCssText, mSheet, IsPseudoElement(mSelector.mTag), + 0 ); aCssText.Append(PRUnichar(' ')); aCssText.Append(PRUnichar('{')); aCssText.Append(PRUnichar(' ')); @@ -3619,7 +3686,7 @@ CSSStyleRuleImpl::GetParentRule(nsIDOMCSSRule** aParentRule) NS_IMETHODIMP CSSStyleRuleImpl::GetSelectorText(nsAWritableString& aSelectorText) { - mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag) ); + mSelector.ToString( aSelectorText, mSheet, IsPseudoElement(mSelector.mTag), 0 ); return NS_OK; } diff --git a/mozilla/layout/style/nsCSSStyleSheet.cpp b/mozilla/layout/style/nsCSSStyleSheet.cpp index f696824af86..6b803fddb9f 100644 --- a/mozilla/layout/style/nsCSSStyleSheet.cpp +++ b/mozilla/layout/style/nsCSSStyleSheet.cpp @@ -2931,167 +2931,229 @@ static PRBool IsSignificantChild(nsIContent* aChild, PRBool aAcceptNonWhitespace static PRBool SelectorMatches(SelectorMatchesData &data, nsCSSSelector* aSelector, - PRBool aTestState) + PRBool aTestState, + PRInt8 aNegationIndex) { - PRBool result = PR_FALSE; + // if we are dealing with negations, reverse the values of PR_TRUE and PR_FALSE + PRBool localFalse = PRBool(0 < aNegationIndex); + PRBool localTrue = PRBool(0 == aNegationIndex); + PRBool checkType = (0 == aNegationIndex) || (1 < aNegationIndex); + PRBool result = localFalse; + nsAutoString buffer; - // Bail out early if we can - if(kNameSpaceID_Unknown != aSelector->mNameSpace) { - if(data.mNameSpaceID != aSelector->mNameSpace) { + // Bail out early if we can. Do not perform the test if aNegationIndex==1 + // because it then contains only negated IDs, classes, attributes and pseudo- + // classes + if (checkType) { + if (kNameSpaceID_Unknown != aSelector->mNameSpace) { + if (data.mNameSpaceID != aSelector->mNameSpace) { + return result; + } + } + } + if (checkType) { + if (localTrue == ((nsnull != aSelector->mTag) && (aSelector->mTag != data.mContentTag))) { return result; } + if (1 < aNegationIndex) { + // optimization : no other selector to test on negated type or + // universal selector + return localTrue; + } } - if ((nsnull == aSelector->mTag) || (aSelector->mTag == data.mContentTag)) { - - result = PR_TRUE; - // namespace/tag match - if (nsnull != aSelector->mAttrList) { // test for attribute match - // if no attributes on the content, no match - if(!data.mHasAttributes) { - result = PR_FALSE; - } else { - nsAttrSelector* attr = aSelector->mAttrList; - do { - nsAutoString value, partValue; - nsresult attrState = data.mContent->GetAttribute(attr->mNameSpace, attr->mAttr, value); - if (NS_FAILED(attrState) || (NS_CONTENT_ATTR_NOT_THERE == attrState)) { - result = PR_FALSE; + result = localTrue; + // namespace/tag match + if (nsnull != aSelector->mAttrList) { // test for attribute match + // if no attributes on the content, no match + if(!data.mHasAttributes) { + result = localFalse; + } else { + nsAttrSelector* attr = aSelector->mAttrList; + do { + nsAutoString value, partValue; + nsresult attrState = data.mContent->GetAttribute(attr->mNameSpace, attr->mAttr, value); + if (NS_FAILED(attrState) || (NS_CONTENT_ATTR_NOT_THERE == attrState)) { + result = localFalse; + } + else { + PRBool isCaseSensitive = (attr->mCaseSensitive && !data.mIsHTMLContent); // Bug 24390: html attributes should not be case-sensitive + switch (attr->mFunction) { + case NS_ATTR_FUNC_SET: break; + case NS_ATTR_FUNC_EQUALS: + if (isCaseSensitive) { + result = PRBool(localTrue == value.Equals(attr->mValue)); + } + else { + result = PRBool(localTrue == value.EqualsIgnoreCase(attr->mValue)); + } + break; + case NS_ATTR_FUNC_INCLUDES: + result = PRBool(localTrue == ValueIncludes(value, attr->mValue, isCaseSensitive)); + break; + case NS_ATTR_FUNC_DASHMATCH: + result = PRBool(localTrue == ValueDashMatch(value, attr->mValue, isCaseSensitive)); + break; + case NS_ATTR_FUNC_ENDSMATCH: + value.Right(partValue, attr->mValue.Length()); + if (isCaseSensitive) { + result = PRBool(localTrue == partValue.Equals(attr->mValue)); + } + else { + result = PRBool(localTrue == partValue.EqualsIgnoreCase(attr->mValue)); + } + break; + case NS_ATTR_FUNC_BEGINSMATCH: + value.Left(partValue, attr->mValue.Length()); + if (isCaseSensitive) { + result = PRBool(localTrue == partValue.Equals(attr->mValue)); + } + else { + result = PRBool(localTrue == partValue.EqualsIgnoreCase(attr->mValue)); + } + break; + case NS_ATTR_FUNC_CONTAINSMATCH: + result = PRBool(localTrue == (-1 != value.Find(attr->mValue, isCaseSensitive))); + break; } - else { - PRBool isCaseSensitive = (attr->mCaseSensitive && !data.mIsHTMLContent); // Bug 24390: html attributes should not be case-sensitive - switch (attr->mFunction) { - case NS_ATTR_FUNC_SET: break; - case NS_ATTR_FUNC_EQUALS: - if (isCaseSensitive) { - result = value.Equals(attr->mValue); - } - else { - result = value.EqualsIgnoreCase(attr->mValue); - } - break; - case NS_ATTR_FUNC_INCLUDES: - result = ValueIncludes(value, attr->mValue, isCaseSensitive); - break; - case NS_ATTR_FUNC_DASHMATCH: - result = ValueDashMatch(value, attr->mValue, isCaseSensitive); - break; - case NS_ATTR_FUNC_ENDSMATCH: - value.Right(partValue, attr->mValue.Length()); - if (isCaseSensitive) { - result = partValue.Equals(attr->mValue); - } - else { - result = partValue.EqualsIgnoreCase(attr->mValue); - } - break; - case NS_ATTR_FUNC_BEGINSMATCH: - value.Left(partValue, attr->mValue.Length()); - if (isCaseSensitive) { - result = partValue.Equals(attr->mValue); - } - else { - result = partValue.EqualsIgnoreCase(attr->mValue); - } - break; - case NS_ATTR_FUNC_CONTAINSMATCH: - result = (-1 != value.Find(attr->mValue, isCaseSensitive)); - break; - } - } - attr = attr->mNext; - } while ((PR_TRUE == result) && (nsnull != attr)); - } + } + attr = attr->mNext; + } while ((localTrue == result) && (nsnull != attr)); } - if ((PR_TRUE == result) && - ((nsnull != aSelector->mIDList) || (nsnull != aSelector->mClassList))) { // test for ID & class match - result = PR_FALSE; - if (data.mStyledContent) { - nsAtomList* IDList = aSelector->mIDList; - if (nsnull == IDList) { - result = PR_TRUE; - } - else if (nsnull != data.mContentID) { - result = PR_TRUE; - while (nsnull != IDList) { - if (IDList->mAtom != data.mContentID) { - result = PR_FALSE; - break; - } - IDList = IDList->mNext; + } + if ((localTrue == result) && + ((nsnull != aSelector->mIDList) || (nsnull != aSelector->mClassList))) { // test for ID & class match + result = localFalse; + if (data.mStyledContent) { + nsAtomList* IDList = aSelector->mIDList; + if (nsnull == IDList) { + result = localTrue; + } + else if (nsnull != data.mContentID) { + result = localTrue; + while (nsnull != IDList) { + if (IDList->mAtom != data.mContentID) { + result = localFalse; + break; } + IDList = IDList->mNext; } - - if (PR_TRUE == result) { - nsAtomList* classList = aSelector->mClassList; - while (nsnull != classList) { - if (NS_COMFALSE == data.mStyledContent->HasClass(classList->mAtom)) { - result = PR_FALSE; - break; - } - classList = classList->mNext; + } + + if (localTrue == result) { + nsAtomList* classList = aSelector->mClassList; + while (nsnull != classList) { + if (NS_COMFALSE == data.mStyledContent->HasClass(classList->mAtom)) { + result = localFalse; + break; } + classList = classList->mNext; } } } - if ((PR_TRUE == result) && - (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match - // first-child, root, lang, active, focus, hover, link, outOfDate, visited - // XXX disabled, enabled, selected, selection - nsAtomList* pseudoClass = aSelector->mPseudoClassList; + } + if ((localTrue == result) && + (nsnull != aSelector->mPseudoClassList)) { // test for pseudo class match + // first-child, root, lang, active, focus, hover, link, outOfDate, visited + // XXX disabled, enabled, selected, selection + nsAtomList* pseudoClass = aSelector->mPseudoClassList; - while ((PR_TRUE == result) && (nsnull != pseudoClass)) { - if ((nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) || - (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom) ) { - nsIContent* firstChild = nsnull; - nsIContent* parent = data.mParentContent; - if (parent) { - PRInt32 index = -1; - do { - parent->ChildAt(++index, firstChild); - if (firstChild) { // skip text & comments (and whitespace for firstNode as well) - if (IsSignificantChild(firstChild, (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom))) { - break; - } - NS_RELEASE(firstChild); - } - else { + while ((localTrue == result) && (nsnull != pseudoClass)) { + if ((nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) || + (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom) ) { + nsIContent* firstChild = nsnull; + nsIContent* parent = data.mParentContent; + if (parent) { + PRInt32 index = -1; + do { + parent->ChildAt(++index, firstChild); + if (firstChild) { // skip text & comments (and whitespace for firstNode as well) + if (IsSignificantChild(firstChild, (nsCSSAtoms::firstNodePseudo == pseudoClass->mAtom))) { break; } - } while (1 == 1); - } - result = PRBool(data.mContent == firstChild); - NS_IF_RELEASE(firstChild); + NS_RELEASE(firstChild); + } + else { + break; + } + } while (1 == 1); } - else if (nsCSSAtoms::lastNodePseudo == pseudoClass->mAtom) { - nsIContent* lastChild = nsnull; - nsIContent* parent = data.mParentContent; - if (parent) { - PRInt32 index; - parent->ChildCount(index); - do { - parent->ChildAt(--index, lastChild); - if (lastChild) { // skip whitespace text & comments - if (IsSignificantChild(lastChild, PR_TRUE)) { - break; - } - NS_RELEASE(lastChild); - } - else { + result = PRBool(localTrue == (data.mContent == firstChild)); + NS_IF_RELEASE(firstChild); + } + else if (nsCSSAtoms::lastNodePseudo == pseudoClass->mAtom) { + nsIContent* lastChild = nsnull; + nsIContent* parent = data.mParentContent; + if (parent) { + PRInt32 index; + parent->ChildCount(index); + do { + parent->ChildAt(--index, lastChild); + if (lastChild) { // skip whitespace text & comments + if (IsSignificantChild(lastChild, PR_TRUE)) { break; } - } while (1 == 1); - } - result = PRBool(data.mContent == lastChild); - NS_IF_RELEASE(lastChild); + NS_RELEASE(lastChild); + } + else { + break; + } + } while (1 == 1); } - else if (nsCSSAtoms::rootPseudo == pseudoClass->mAtom) { - if (data.mParentContent) { - result = PR_FALSE; + result = PRBool(localTrue == (data.mContent == lastChild)); + NS_IF_RELEASE(lastChild); + } + else if (nsCSSAtoms::rootPseudo == pseudoClass->mAtom) { + if (data.mParentContent) { + result = localFalse; + } + else { + result = localTrue; + } + } + else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { + // XXX not yet implemented + result = localFalse; + } + else if (IsEventPseudo(pseudoClass->mAtom)) { + // check if the element is event-sensitive + + // Quirk Mode: check to see if the element is event-sensitive + // - see if the selector applies to event pseudo classes + // NOTE: we distinguish between global and subjected selectors so + // pass that information on to the determining routine + PRBool isSelectorGlobal = aSelector->mTag==nsnull ? PR_TRUE : PR_FALSE; + if ((data.mIsQuirkMode) && + (!IsEventSensitive(pseudoClass->mAtom, data.mContentTag, isSelectorGlobal))){ + result = localFalse; + } else if (aTestState) { + if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_ACTIVE))); } - else { - result = PR_TRUE; + else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_FOCUS))); + } + else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_HOVER))); + } + else if (nsCSSAtoms::dragOverPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_DRAGOVER))); + } + } + } + else if (IsLinkPseudo(pseudoClass->mAtom)) { + if (data.mIsHTMLLink || data.mIsSimpleXLink) { + if ((localFalse != result) && (aTestState)) { + if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (eLinkState_Unvisited == data.mLinkState)); + } + else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (eLinkState_OutOfDate == data.mLinkState)); + } + else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { + result = PRBool(localTrue == (eLinkState_Visited == data.mLinkState)); + } } } else if (nsCSSAtoms::xblBoundElementPseudo == pseudoClass->mAtom) { @@ -3106,63 +3168,22 @@ static PRBool SelectorMatches(SelectorMatchesData &data, if (data.mStyleRuleSupplier) data.mStyleRuleSupplier->MatchesScopedRoot(data.mContent, &result); else - result = PR_FALSE; - } - else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { - // XXX not yet implemented - result = PR_FALSE; - } - else if (IsEventPseudo(pseudoClass->mAtom)) { - // check if the element is event-sensitive - - // Quirk Mode: check to see if the element is event-sensitive - // - see if the selector applies to event pseudo classes - // NOTE: we distinguish between global and subjected selectors so - // pass that information on to the determining routine - PRBool isSelectorGlobal = aSelector->mTag==nsnull ? PR_TRUE : PR_FALSE; - if ((data.mIsQuirkMode) && - (!IsEventSensitive(pseudoClass->mAtom, data.mContentTag, isSelectorGlobal))){ - result = PR_FALSE; - } else if (aTestState) { - if (nsCSSAtoms::activePseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_ACTIVE)); - } - else if (nsCSSAtoms::focusPseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_FOCUS)); - } - else if (nsCSSAtoms::hoverPseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_HOVER)); - } - else if (nsCSSAtoms::dragOverPseudo == pseudoClass->mAtom) { - result = PRBool(0 != (data.mEventState & NS_EVENT_STATE_DRAGOVER)); - } - } - } - else if (IsLinkPseudo(pseudoClass->mAtom)) { - if (data.mIsHTMLLink || data.mIsSimpleXLink) { - if ((PR_FALSE != result) && (aTestState)) { - if (nsCSSAtoms::linkPseudo == pseudoClass->mAtom) { - result = PRBool(eLinkState_Unvisited == data.mLinkState); - } - else if (nsCSSAtoms::outOfDatePseudo == pseudoClass->mAtom) { - result = PRBool(eLinkState_OutOfDate == data.mLinkState); - } - else if (nsCSSAtoms::visitedPseudo == pseudoClass->mAtom) { - result = PRBool(eLinkState_Visited == data.mLinkState); - } - } - } - else { - result = PR_FALSE; // not a link - } + result = localFalse; } else { - result = PR_FALSE; // unknown pseudo class + result = localFalse; // not a link } - pseudoClass = pseudoClass->mNext; } + else { + result = localFalse; // unknown pseudo class + } + pseudoClass = pseudoClass->mNext; } } + // apply SelectorMatches to the negated selectors in the chain + if ((localTrue == result) && (nsnull != aSelector->mNegations)) { + result = SelectorMatches(data, aSelector->mNegations, aTestState, aNegationIndex+1); + } return result; } @@ -3219,7 +3240,7 @@ static PRBool SelectorMatchesTree(SelectorMatchesData &data, nsCompatibility compat = data.mIsQuirkMode ? eCompatibility_NavQuirks : eCompatibility_Standard; SelectorMatchesData newdata(data.mPresContext, content, data.mParentContext, data.mResults, &compat); - if (SelectorMatches(newdata, selector, PR_TRUE)) { + if (SelectorMatches(newdata, selector, PR_TRUE, 0)) { // to avoid greedy matching, we need to recurse if this is a // descendant combinator and the next combinator is not if ((NS_IS_GREEDY_OPERATOR(selector->mOperator)) && @@ -3262,7 +3283,7 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) ContentEnumData* data = (ContentEnumData*)aData; nsCSSSelector* selector = aRule->FirstSelector(); - if (SelectorMatches(*data, selector, PR_TRUE)) { + if (SelectorMatches(*data, selector, PR_TRUE, 0)) { selector = selector->mNext; if (SelectorMatchesTree(*data, selector)) { nsIStyleRule* iRule; @@ -3368,7 +3389,7 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) if (PRUnichar('+') == selector->mOperator) { return; // not valid here, can't match } - if (SelectorMatches(*data, selector, PR_TRUE)) { + if (SelectorMatches(*data, selector, PR_TRUE, 0)) { selector = selector->mNext; } else { @@ -3452,7 +3473,7 @@ PRBool PR_CALLBACK StateEnumFunc(void* aSelector, void* aData) StateEnumData* data = (StateEnumData*)aData; nsCSSSelector* selector = (nsCSSSelector*)aSelector; - if (SelectorMatches(*data, selector, PR_FALSE)) { + if (SelectorMatches(*data, selector, PR_FALSE, 0)) { selector = selector->mNext; if (SelectorMatchesTree(*data, selector)) { return PR_FALSE; diff --git a/mozilla/layout/style/nsICSSStyleRule.h b/mozilla/layout/style/nsICSSStyleRule.h index 6fb1b739bfc..d1951630e66 100644 --- a/mozilla/layout/style/nsICSSStyleRule.h +++ b/mozilla/layout/style/nsICSSStyleRule.h @@ -103,7 +103,12 @@ public: PRInt32 CalcWeight(void) const; void SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize); - nsresult ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem ) const; + nsresult ToString( nsAWritableString& aString, nsICSSStyleSheet* aSheet, + PRBool aIsPseudoElem, PRInt8 aNegatedIndex ) const; + +private: + + void AppendNegationToString(nsAWritableString& aString); public: PRInt32 mNameSpace; @@ -113,10 +118,12 @@ public: nsAtomList* mPseudoClassList; nsAttrSelector* mAttrList; PRUnichar mOperator; + nsCSSSelector* mNegations; nsCSSSelector* mNext; }; + // IID for the nsICSSStyleRule interface {7c277af0-af19-11d1-8031-006008159b5a} #define NS_ICSS_STYLE_RULE_IID \ {0x7c277af0, 0xaf19, 0x11d1, {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}