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}}