adds support for :not() css3 pseudo-class; b=71647, r=pierre, sr=hyatt

This commit is contained in:
glazman%netscape.com 2001-03-20 11:49:20 +00:00
Родитель f14a83706d
Коммит 4fc3eb0ce3
9 изменённых файлов: 886 добавлений и 448 удалений

Просмотреть файл

@ -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);

Просмотреть файл

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

Просмотреть файл

@ -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;

Просмотреть файл

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

Просмотреть файл

@ -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")

Просмотреть файл

@ -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);

Просмотреть файл

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

Просмотреть файл

@ -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;

Просмотреть файл

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