void nsCSSSelector::ToStringInternal(nsAString& aString, nsICSSStyleSheet* aSheet, PRBool aIsPseudoElem, PRBool aIsNegated) const { nsAutoString temp; PRBool isPseudoElement = IsPseudoElement(mTag); // 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->ToStringInternal(aString, aSheet, IsPseudoElement(mTag), 0); if (!aIsNegated && !isPseudoElement) { // don't add a leading whitespace if we have a pseudo-element // or a negated simple selector aString.Append(PRUnichar(' ')); } } // For non-pseudo-element selectors or for lone pseudo-elements, deal with // namespace prefixes. PRBool wroteNamespace = PR_FALSE; if (!isPseudoElement || !mNext) { // append the namespace prefix if needed if (mNameSpace == kNameSpaceID_None) { // The only way to do this in CSS is to have an explicit namespace // of "none" specified in the sheet by having a '|' with nothing // before it. aString.Append(PRUnichar('|')); wroteNamespace = PR_TRUE; } else { if (aSheet) { nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap(); // sheetNS is non-null if and only if we had an @namespace rule. If it's // null, that means that the only namespaces we could have are the // wildcard namespace (which can be implicit in this case) and the "none" // namespace, which we handled above. So no need to output anything when // sheetNS is null. if (sheetNS) { if (mNameSpace != kNameSpaceID_Unknown) { if (sheetNS->FindNameSpaceID(nsnull) != mNameSpace) { nsIAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace); NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace " "without a prefix?"); nsAutoString prefix; prefixAtom->ToString(prefix); aString.Append(prefix); aString.Append(PRUnichar('|')); wroteNamespace = PR_TRUE; } // otherwise it must be the default namespace } else { // A selector for an element in any namespace. if (// Use explicit "*|" only when it's not implied sheetNS->FindNameSpaceID(nsnull) != kNameSpaceID_None && // :not() is special in that the default namespace is // not implied for non-type selectors (!aIsNegated || (!mIDList && !mClassList && !mPseudoClassList && !mAttrList))) { aString.AppendLiteral("*|"); wroteNamespace = PR_TRUE; } } } } } } if (!mTag) { // Universal selector: avoid writing the universal selector when we // can avoid it, especially since we're required to avoid it for the // inside of :not() if (wroteNamespace || (!mIDList && !mClassList && !mPseudoClassList && !mAttrList && (aIsNegated || !mNegations))) { aString.Append(PRUnichar('*')); } } else { // Append the tag name if (isPseudoElement) { if (!mNext) { // Lone pseudo-element selector -- toss in a wildcard type selector // XXXldb Why? aString.Append(PRUnichar('*')); } if (!nsCSSPseudoElements::IsCSS2PseudoElement(mTag)) { aString.Append(PRUnichar(':')); } } nsAutoString prefix; mTag->ToString(prefix); aString.Append(prefix); } // Append the id, if there is one if (mIDList) { nsAtomList* list = mIDList; while (list != nsnull) { list->mAtom->ToString(temp); aString.Append(PRUnichar('#')); aString.Append(temp); list = list->mNext; } } // Append each class in the linked list if (mClassList) { nsAtomList* list = mClassList; while (list != nsnull) { list->mAtom->ToString(temp); aString.Append(PRUnichar('.')); aString.Append(temp); list = list->mNext; } } // Append each attribute selector in the linked list if (mAttrList) { nsAttrSelector* list = mAttrList; while (list != nsnull) { aString.Append(PRUnichar('[')); // Append the namespace prefix if (list->mNameSpace > 0) { if (aSheet) { nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap(); // will return null if namespace was the default nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace); if (prefixAtom) { nsAutoString prefix; prefixAtom->ToString(prefix); aString.Append(prefix); aString.Append(PRUnichar('|')); } } } // Append the attribute name list->mAttr->ToString(temp); aString.Append(temp); if (list->mFunction != NS_ATTR_FUNC_SET) { // Append the function if (list->mFunction == NS_ATTR_FUNC_INCLUDES) aString.Append(PRUnichar('~')); else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH) aString.Append(PRUnichar('|')); else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH) aString.Append(PRUnichar('^')); else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH) aString.Append(PRUnichar('$')); else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH) aString.Append(PRUnichar('*')); aString.Append(PRUnichar('=')); // Append the value nsAutoString escaped; nsStyleUtil::EscapeCSSString(list->mValue, escaped); aString.Append(PRUnichar('\"')); aString.Append(escaped); aString.Append(PRUnichar('\"')); } aString.Append(PRUnichar(']')); list = list->mNext; } } // Append each pseudo-class in the linked list if (mPseudoClassList) { nsAtomStringList* list = mPseudoClassList; while (list != nsnull) { list->mAtom->ToString(temp); aString.Append(temp); if (nsnull != list->mString) { aString.Append(PRUnichar('(')); aString.Append(list->mString); aString.Append(PRUnichar(')')); } list = list->mNext; } } if (!aIsNegated) { for (nsCSSSelector* negation = mNegations; negation; negation = negation->mNegations) { aString.AppendLiteral(":not("); negation->ToStringInternal(aString, aSheet, PR_FALSE, PR_TRUE); aString.Append(PRUnichar(')')); } } // Append the operator only if the selector is not negated and is not // a pseudo-element if (!aIsNegated && mOperator && !aIsPseudoElem) { aString.Append(PRUnichar(' ')); aString.Append(mOperator); } }
void nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations (nsAString& aString, nsCSSStyleSheet* aSheet, PRBool aIsNegated) const { nsAutoString temp; PRBool isPseudoElement = IsPseudoElement(); // For non-pseudo-element selectors or for lone pseudo-elements, deal with // namespace prefixes. PRBool wroteNamespace = PR_FALSE; if (!isPseudoElement || !mNext) { // append the namespace prefix if needed nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nsnull; // sheetNS is non-null if and only if we had an @namespace rule. If it's // null, that means that the only namespaces we could have are the // wildcard namespace (which can be implicit in this case) and the "none" // namespace, which then needs to be explicitly specified. if (!sheetNS) { NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || mNameSpace == kNameSpaceID_None, "How did we get this namespace?"); if (mNameSpace == kNameSpaceID_None) { aString.Append(PRUnichar('|')); wroteNamespace = PR_TRUE; } } else if (sheetNS->FindNameSpaceID(nsnull) == mNameSpace) { // We have the default namespace (possibly including the wildcard // namespace). Do nothing. NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || CanBeNamespaced(aIsNegated), "How did we end up with this namespace?"); } else if (mNameSpace == kNameSpaceID_None) { NS_ASSERTION(CanBeNamespaced(aIsNegated), "How did we end up with this namespace?"); aString.Append(PRUnichar('|')); wroteNamespace = PR_TRUE; } else if (mNameSpace != kNameSpaceID_Unknown) { NS_ASSERTION(CanBeNamespaced(aIsNegated), "How did we end up with this namespace?"); nsIAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace); NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace " "without a prefix?"); nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom), aString); aString.Append(PRUnichar('|')); wroteNamespace = PR_TRUE; } else { // A selector for an element in any namespace, while the default // namespace is something else. :not() is special in that the default // namespace is not implied for non-type selectors, so if this is a // negated non-type selector we don't need to output an explicit wildcard // namespace here, since those default to a wildcard namespace. if (CanBeNamespaced(aIsNegated)) { aString.AppendLiteral("*|"); wroteNamespace = PR_TRUE; } } } if (!mLowercaseTag) { // Universal selector: avoid writing the universal selector when we // can avoid it, especially since we're required to avoid it for the // inside of :not() if (wroteNamespace || (!mIDList && !mClassList && !mPseudoClassList && !mAttrList && (aIsNegated || !mNegations))) { aString.Append(PRUnichar('*')); } } else { // Append the tag name nsAutoString tag; (isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag); if (isPseudoElement) { if (!mNext) { // Lone pseudo-element selector -- toss in a wildcard type selector // XXXldb Why? aString.Append(PRUnichar('*')); } if (!nsCSSPseudoElements::IsCSS2PseudoElement(mLowercaseTag)) { aString.Append(PRUnichar(':')); } // This should not be escaped since (a) the pseudo-element string // has a ":" that can't be escaped and (b) all pseudo-elements at // this point are known, and therefore we know they don't need // escaping. aString.Append(tag); } else { nsStyleUtil::AppendEscapedCSSIdent(tag, aString); } } // Append the id, if there is one if (mIDList) { nsAtomList* list = mIDList; while (list != nsnull) { list->mAtom->ToString(temp); aString.Append(PRUnichar('#')); nsStyleUtil::AppendEscapedCSSIdent(temp, aString); list = list->mNext; } } // Append each class in the linked list if (mClassList) { if (isPseudoElement) { #ifdef MOZ_XUL NS_ABORT_IF_FALSE(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag), "must be tree pseudo-element"); aString.Append(PRUnichar('(')); for (nsAtomList* list = mClassList; list; list = list->mNext) { nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString); aString.Append(PRUnichar(',')); } // replace the final comma with a close-paren aString.Replace(aString.Length() - 1, 1, PRUnichar(')')); #else NS_ERROR("Can't happen"); #endif } else { nsAtomList* list = mClassList; while (list != nsnull) { list->mAtom->ToString(temp); aString.Append(PRUnichar('.')); nsStyleUtil::AppendEscapedCSSIdent(temp, aString); list = list->mNext; } } } // Append each attribute selector in the linked list if (mAttrList) { nsAttrSelector* list = mAttrList; while (list != nsnull) { aString.Append(PRUnichar('[')); // Append the namespace prefix if (list->mNameSpace == kNameSpaceID_Unknown) { aString.Append(PRUnichar('*')); aString.Append(PRUnichar('|')); } else if (list->mNameSpace != kNameSpaceID_None) { if (aSheet) { nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap(); nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace); // Default namespaces don't apply to attribute selectors, so // we must have a useful prefix. NS_ASSERTION(prefixAtom, "How did we end up with a namespace if the prefix " "is unknown?"); nsAutoString prefix; prefixAtom->ToString(prefix); nsStyleUtil::AppendEscapedCSSIdent(prefix, aString); aString.Append(PRUnichar('|')); } } // Append the attribute name list->mCasedAttr->ToString(temp); nsStyleUtil::AppendEscapedCSSIdent(temp, aString); if (list->mFunction != NS_ATTR_FUNC_SET) { // Append the function if (list->mFunction == NS_ATTR_FUNC_INCLUDES) aString.Append(PRUnichar('~')); else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH) aString.Append(PRUnichar('|')); else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH) aString.Append(PRUnichar('^')); else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH) aString.Append(PRUnichar('$')); else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH) aString.Append(PRUnichar('*')); aString.Append(PRUnichar('=')); // Append the value nsStyleUtil::AppendEscapedCSSString(list->mValue, aString); } aString.Append(PRUnichar(']')); list = list->mNext; } } // Append each pseudo-class in the linked list for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) { nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp); // This should not be escaped since (a) the pseudo-class string // has a ":" that can't be escaped and (b) all pseudo-classes at // this point are known, and therefore we know they don't need // escaping. aString.Append(temp); if (list->u.mMemory) { aString.Append(PRUnichar('(')); if (nsCSSPseudoClasses::HasStringArg(list->mType)) { nsStyleUtil::AppendEscapedCSSIdent( nsDependentString(list->u.mString), aString); } else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) { PRInt32 a = list->u.mNumbers[0], b = list->u.mNumbers[1]; temp.Truncate(); if (a != 0) { if (a == -1) { temp.Append(PRUnichar('-')); } else if (a != 1) { temp.AppendInt(a); } temp.Append(PRUnichar('n')); } if (b != 0 || a == 0) { if (b >= 0 && a != 0) // check a != 0 for whether we printed above temp.Append(PRUnichar('+')); temp.AppendInt(b); } aString.Append(temp); } else { NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType), "unexpected pseudo-class"); nsString tmp; list->u.mSelectors->ToString(tmp, aSheet); aString.Append(tmp); } aString.Append(PRUnichar(')')); } } }