} else if (nsCSSPseudoClasses::enabled == pseudoClass->mAtom) { result = STATE_CHECK(NS_EVENT_STATE_ENABLED); } else if (nsCSSPseudoClasses::disabled == pseudoClass->mAtom) { result = STATE_CHECK(NS_EVENT_STATE_DISABLED);
static PRBool SelectorMatches(RuleProcessorData &data,                              nsCSSSelector* aSelector,                              PRInt32 aStateMask, // states NOT to test                              nsIAtom* aAttribute, // attribute NOT to test                              PRInt8 aNegationIndex) {  // if we are dealing with negations, reverse the values of PR_TRUE and PR_FALSE  PRBool localFalse = 0 < aNegationIndex;  PRBool localTrue = 0 == aNegationIndex;  PRBool result = localTrue;  // Do not perform the test if aNegationIndex==1  // because it then contains only negated IDs, classes, attributes and pseudo-  // classes  if (1 != aNegationIndex) {    if (kNameSpaceID_Unknown != aSelector->mNameSpace) {      if (data.mNameSpaceID != aSelector->mNameSpace) {        result = localFalse;      }    }    if (localTrue == result) {      if ((nsnull != aSelector->mTag) && (aSelector->mTag != data.mContentTag)) {        result = localFalse;      }    }    // optimization : bail out early if we can    if (!result) {      return PR_FALSE;    }  }  result = PR_TRUE;  // test for pseudo class match  // first-child, root, lang, active, focus, hover, link, visited...  // XXX disabled, enabled, selected, selection  for (nsAtomStringList* pseudoClass = aSelector->mPseudoClassList;       pseudoClass && result; pseudoClass = pseudoClass->mNext) {    if ((nsCSSPseudoClasses::firstChild == pseudoClass->mAtom) ||        (nsCSSPseudoClasses::firstNode == pseudoClass->mAtom) ) {      nsIContent *firstChild = nsnull;      nsIContent *parent = data.mParentContent;      if (parent) {        PRBool acceptNonWhitespace =          nsCSSPseudoClasses::firstNode == pseudoClass->mAtom;        PRInt32 index = -1;        do {          firstChild = parent->GetChildAt(++index);          // stop at first non-comment and non-whitespace node (and          // non-text node for firstChild)        } while (firstChild &&                 !IsSignificantChild(firstChild, acceptNonWhitespace, PR_FALSE));      }      result = localTrue == (data.mContent == firstChild);    }    else if ((nsCSSPseudoClasses::lastChild == pseudoClass->mAtom) ||             (nsCSSPseudoClasses::lastNode == pseudoClass->mAtom)) {      nsIContent *lastChild = nsnull;      nsIContent *parent = data.mParentContent;      if (parent) {        PRBool acceptNonWhitespace =          nsCSSPseudoClasses::lastNode == pseudoClass->mAtom;        PRUint32 index = parent->GetChildCount();        do {          lastChild = parent->GetChildAt(--index);          // stop at first non-comment and non-whitespace node (and          // non-text node for lastChild)        } while (lastChild &&                 !IsSignificantChild(lastChild, acceptNonWhitespace, PR_FALSE));      }      result = localTrue == (data.mContent == lastChild);    }    else if (nsCSSPseudoClasses::onlyChild == pseudoClass->mAtom) {      nsIContent *onlyChild = nsnull;      nsIContent *moreChild = nsnull;      nsIContent *parent = data.mParentContent;      if (parent) {        PRInt32 index = -1;        do {          onlyChild = parent->GetChildAt(++index);          // stop at first non-comment, non-whitespace and non-text node        } while (onlyChild &&                 !IsSignificantChild(onlyChild, PR_FALSE, PR_FALSE));        if (data.mContent == onlyChild) {          // see if there's any more          do {            moreChild = parent->GetChildAt(++index);          } while (moreChild && !IsSignificantChild(moreChild, PR_FALSE, PR_FALSE));        }      }      result = localTrue == (data.mContent == onlyChild &&                             moreChild == nsnull);    }    else if (nsCSSPseudoClasses::empty == pseudoClass->mAtom ||             nsCSSPseudoClasses::mozOnlyWhitespace == pseudoClass->mAtom) {      nsIContent *child = nsnull;      nsIContent *element = data.mContent;      PRBool isWhitespaceSignificant =        nsCSSPseudoClasses::empty == pseudoClass->mAtom;      PRInt32 index = -1;      do {        child = element->GetChildAt(++index);        // stop at first non-comment (and non-whitespace for        // :-moz-only-whitespace) node              } while (child && !IsSignificantChild(child, PR_TRUE, isWhitespaceSignificant));      result = localTrue == (child == nsnull);    }    else if (nsCSSPseudoClasses::root == pseudoClass->mAtom) {      if (data.mParentContent) {        result = localFalse;      }      else {        result = localTrue;      }    }    else if (nsCSSPseudoClasses::mozBoundElement == pseudoClass->mAtom) {      // XXXldb How do we know where the selector came from?  And what      // if there are multiple bindings, and we should be matching the      // outer one?      result = (data.mScopedRoot && data.mScopedRoot == data.mContent)                 ? localTrue : localFalse;    }    else if (nsCSSPseudoClasses::lang == pseudoClass->mAtom) {      NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter");      result = localFalse;      if (pseudoClass->mString && *pseudoClass->mString) {        // We have to determine the language of the current element.  Since        // this is currently no property and since the language is inherited        // from the parent we have to be prepared to look at all parent        // nodes.  The language itself is encoded in the LANG attribute.        const nsString* lang = data.GetLang();        if (lang && !lang->IsEmpty()) { // null check for out-of-memory          result = localTrue == nsStyleUtil::DashMatchCompare(*lang,                                    nsDependentString(pseudoClass->mString),                                     nsCaseInsensitiveStringComparator());        }        else {          nsIDocument* doc = data.mContent->GetDocument();          if (doc) {            // Try to get the language from the HTTP header or if this            // is missing as well from the preferences.            // The content language can be a comma-separated list of            // language codes.            nsAutoString language;            doc->GetContentLanguage(language);            nsDependentString langString(pseudoClass->mString);            language.StripWhitespace();            PRInt32 begin = 0;            PRInt32 len = language.Length();            while (begin < len) {              PRInt32 end = language.FindChar(PRUnichar(','), begin);              if (end == kNotFound) {                end = len;              }              if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end-begin),                                   langString,                                   nsCaseInsensitiveStringComparator())) {                result = localTrue;                break;              }              begin = end + 1;            }          }        }      }    }    else if (IsEventPseudo(pseudoClass->mAtom)) {      // check if the element is event-sensitive      if (data.mCompatMode == eCompatibility_NavQuirks &&          // global selector (but don't check .class):          !aSelector->mTag && !aSelector->mIDList && !aSelector->mAttrList &&          // This (or the other way around) both make :not() asymmetric          // in quirks mode (and it's hard to work around since we're          // testing the current mNegations, not the first          // (unnegated)). This at least makes it closer to the spec.          aNegationIndex == 0 &&          // :hover or :active          (nsCSSPseudoClasses::active == pseudoClass->mAtom ||           nsCSSPseudoClasses::hover == pseudoClass->mAtom) &&          // important for |IsQuirkEventSensitive|:          data.mIsHTMLContent && !data.mIsHTMLLink &&          !IsQuirkEventSensitive(data.mContentTag)) {        // In quirks mode, only make certain elements sensitive to        // selectors ":hover" and ":active".        result = localFalse;      } else {        if (nsCSSPseudoClasses::active == pseudoClass->mAtom) {          result = STATE_CHECK(NS_EVENT_STATE_ACTIVE);        }        else if (nsCSSPseudoClasses::focus == pseudoClass->mAtom) {          result = STATE_CHECK(NS_EVENT_STATE_FOCUS);        }        else if (nsCSSPseudoClasses::hover == pseudoClass->mAtom) {          result = STATE_CHECK(NS_EVENT_STATE_HOVER);        }        else if (nsCSSPseudoClasses::mozDragOver == pseudoClass->mAtom) {          result = STATE_CHECK(NS_EVENT_STATE_DRAGOVER);        }        else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {          result = STATE_CHECK(NS_EVENT_STATE_URLTARGET);        }      }     }    else if (IsLinkPseudo(pseudoClass->mAtom)) {      if (data.mIsHTMLLink || data.mIsSimpleXLink) {        if (nsCSSPseudoClasses::mozAnyLink == pseudoClass->mAtom) {          result = localTrue;        }        else if (nsCSSPseudoClasses::link == pseudoClass->mAtom) {          result = (aStateMask & NS_EVENT_STATE_VISITED) ||            localTrue == (eLinkState_Unvisited == data.mLinkState);        }        else if (nsCSSPseudoClasses::visited == pseudoClass->mAtom) {          result = (aStateMask & NS_EVENT_STATE_VISITED) ||            localTrue == (eLinkState_Visited == data.mLinkState);        }      }      else {        result = localFalse;  // not a link      }    }    else if (nsCSSPseudoClasses::checked == pseudoClass->mAtom) {      // This pseudoclass matches the selected state on the following elements:      //  <option>      //  <input type=checkbox>      //  <input type=radio>      result = STATE_CHECK(NS_EVENT_STATE_CHECKED);    }    else if (nsCSSPseudoClasses::required == pseudoClass->mAtom) {      result = STATE_CHECK(NS_EVENT_STATE_REQUIRED);    }    else if (nsCSSPseudoClasses::optional == pseudoClass->mAtom) {      result = STATE_CHECK(NS_EVENT_STATE_OPTIONAL);    }    else {      NS_ERROR("CSS parser parsed a pseudo-class that we do not handle");      result = PR_FALSE;  // unknown pseudo class    }  }  // namespace/tag match  if (result && aSelector->mAttrList) {    // test for attribute match    if (!data.mHasAttributes && !aAttribute) {      // if no attributes on the content, no match      result = localFalse;    } else {      result = localTrue;      nsAttrSelector* attr = aSelector->mAttrList;      do {        if (attr->mAttr == aAttribute) {          // XXX we should really have a namespace, not just an attr          // name, in HasAttributeDependentStyle!          result = PR_TRUE;        }        else if (attr->mNameSpace == kNameSpaceID_Unknown) {          // Attr selector with a wildcard namespace.  We have to examine all          // the attributes on our content node....  This sort of selector is          // essentially a boolean OR, over all namespaces, of equivalent attr          // selectors with those namespaces.  So to evaluate whether it          // matches, evaluate for each namespace (the only namespaces that          // have a chance at matching, of course, are ones that the element          // actually has attributes in), short-circuiting if we ever match.          // Then deal with the localFalse/localTrue stuff.          PRUint32 attrCount = data.mContent->GetAttrCount();          PRInt32 nameSpaceID;          nsCOMPtr<nsIAtom> name;          nsCOMPtr<nsIAtom> prefix;          PRBool attrSelectorMatched = PR_FALSE;          for (PRUint32 i = 0; i < attrCount; ++i) {#ifdef DEBUG            nsresult attrState =#endif              data.mContent->GetAttrNameAt(i, &nameSpaceID,                                           getter_AddRefs(name),                                           getter_AddRefs(prefix));            NS_ASSERTION(NS_SUCCEEDED(attrState),                         "GetAttrCount lied or GetAttrNameAt failed");            if (name != attr->mAttr) {              continue;            }            if (attr->mFunction == NS_ATTR_FUNC_SET) {              attrSelectorMatched = PR_TRUE;            } else {              nsAutoString value;#ifdef DEBUG              attrState =#endif                data.mContent->GetAttr(nameSpaceID, name, value);              NS_ASSERTION(NS_SUCCEEDED(attrState) &&                           NS_CONTENT_ATTR_NOT_THERE != attrState,                           "GetAttrNameAt lied or GetAttr failed");              attrSelectorMatched = AttrMatchesValue(attr, value);            }            // At this point |attrSelectorMatched| has been set by us            // explicitly in this loop.  If it's PR_FALSE, we may still match            // -- the content may have another attribute with the same name but            // in a different namespace.  But if it's PR_TRUE, we are done (we            // can short-circuit the boolean OR described above).            if (attrSelectorMatched) {              break;            }          }                    // Now adjust for a possible negation          result = localTrue == attrSelectorMatched;        }        else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) {          result = localFalse;        }        else if (attr->mFunction != NS_ATTR_FUNC_SET) {          nsAutoString value;#ifdef DEBUG          nsresult attrState =#endif              data.mContent->GetAttr(attr->mNameSpace, attr->mAttr, value);          NS_ASSERTION(NS_SUCCEEDED(attrState) &&                       NS_CONTENT_ATTR_NOT_THERE != attrState,                       "HasAttr lied or GetAttr failed");          result = localTrue == AttrMatchesValue(attr, value);        }                attr = attr->mNext;      } while (attr && result);    }  }  if (result && (aSelector->mIDList || aSelector->mClassList)) {    // test for ID & class match    result = localFalse;    if (data.mStyledContent) {      // case sensitivity: bug 93371      PRBool isCaseSensitive = data.mCompatMode != eCompatibility_NavQuirks;      nsAtomList* IDList = aSelector->mIDList;      if (nsnull == IDList ||          (aAttribute && aAttribute == data.mContent->GetIDAttributeName())) {        result = PR_TRUE;      }      else if (nsnull != data.mContentID) {        result = PR_TRUE;        if (isCaseSensitive) {          do {            if (localTrue == (IDList->mAtom != data.mContentID)) {              result = PR_FALSE;              break;            }            IDList = IDList->mNext;          } while (IDList);        } else {          const char* id1Str;          data.mContentID->GetUTF8String(&id1Str);          nsDependentCString id1(id1Str);          do {            const char* id2Str;            IDList->mAtom->GetUTF8String(&id2Str);            nsDependentCString id2(id2Str);            if (localTrue !=                id1.Equals(id2, nsCaseInsensitiveCStringComparator())) {              result = PR_FALSE;              break;            }            IDList = IDList->mNext;          } while (IDList);        }      }            if (result &&          (!aAttribute || aAttribute != data.mStyledContent->GetClassAttributeName())) {        nsAtomList* classList = aSelector->mClassList;        while (nsnull != classList) {          if (localTrue == (!data.mStyledContent->HasClass(classList->mAtom, isCaseSensitive))) {            result = PR_FALSE;            break;          }          classList = classList->mNext;        }      }    }  }    // apply SelectorMatches to the negated selectors in the chain  if (result && (nsnull != aSelector->mNegations)) {    result = SelectorMatches(data, aSelector->mNegations, aStateMask,                             aAttribute, aNegationIndex+1);  }  return result;}