NS_IMETHODIMP nsXMLContentSerializer::AppendElementEnd(nsIDOMElement *aElement, nsAString& aStr) { NS_ENSURE_ARG(aElement); // We don't output a separate end tag for empty element nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aElement)); PRBool hasChildren; if (NS_SUCCEEDED(node->HasChildNodes(&hasChildren)) && !hasChildren) { PopNameSpaceDeclsFor(aElement); return NS_OK; } nsCOMPtr<nsIContent> content(do_QueryInterface(aElement)); if (!content) return NS_ERROR_FAILURE; nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; aElement->GetPrefix(tagPrefix); aElement->GetLocalName(tagLocalName); aElement->GetNamespaceURI(tagNamespaceURI); #ifdef DEBUG PRBool debugNeedToPushNamespace = #endif ConfirmPrefix(tagPrefix, tagNamespaceURI, aElement, PR_FALSE); NS_ASSERTION(!debugNeedToPushNamespace, "Can't push namespaces in closing tag!"); AppendToString(NS_LITERAL_STRING("</"), aStr); if (!tagPrefix.IsEmpty()) { AppendToString(tagPrefix, aStr); AppendToString(NS_LITERAL_STRING(":"), aStr); } AppendToString(tagLocalName, aStr); AppendToString(NS_LITERAL_STRING(">"), aStr); MaybeFlagNewline(aElement); PopNameSpaceDeclsFor(aElement); return NS_OK; }
bool nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent, nsIContent *aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsIAtom* aTagName, nsAString& aStr, uint32_t aSkipAttr, bool aAddNSAttr) { nsresult rv; uint32_t index, count; nsAutoString prefixStr, uriStr, valueStr; nsAutoString xmlnsStr; xmlnsStr.AssignLiteral(kXMLNS); int32_t contentNamespaceID = aContent->GetNameSpaceID(); // this method is not called by nsHTMLContentSerializer // so we don't have to check HTML element, just XHTML if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) { // Need to keep track of OL and LI elements in order to get ordinal number // for the LI. if (aTagName == nsGkAtoms::ol) { // We are copying and current node is an OL; // Store its start attribute value in olState->startVal. nsAutoString start; int32_t startAttrVal = 0; aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start); if (!start.IsEmpty()) { nsresult rv = NS_OK; startAttrVal = start.ToInteger(&rv); //If OL has "start" attribute, first LI element has to start with that value //Therefore subtracting 1 as all the LI elements are incrementing it before using it; //In failure of ToInteger(), default StartAttrValue to 0. if (NS_SUCCEEDED(rv)) --startAttrVal; else startAttrVal = 0; } olState state (startAttrVal, true); mOLStateStack.AppendElement(state); } else if (aTagName == nsGkAtoms::li) { mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement); if (mIsFirstChildOfOL) { // If OL is parent of this LI, serialize attributes in different manner. NS_ENSURE_TRUE(SerializeLIValueAttribute(aContent, aStr), false); } } } // If we had to add a new namespace declaration, serialize // and push it on the namespace stack if (aAddNSAttr) { if (aTagPrefix.IsEmpty()) { // Serialize default namespace decl NS_ENSURE_TRUE(SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true), false); } else { // Serialize namespace decl NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true), false); } PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement); } NS_NAMED_LITERAL_STRING(_mozStr, "_moz"); count = aContent->GetAttrCount(); // Now serialize each of the attributes // XXX Unfortunately we need a namespace manager to get // attribute URIs. for (index = 0; index < count; index++) { if (aSkipAttr == index) { continue; } mozilla::dom::BorrowedAttrInfo info = aContent->GetAttrInfoAt(index); const nsAttrName* name = info.mName; int32_t namespaceID = name->NamespaceID(); nsIAtom* attrName = name->LocalName(); nsIAtom* attrPrefix = name->GetPrefix(); // Filter out any attribute starting with [-|_]moz nsDependentAtomString attrNameStr(attrName); if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) || StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) { continue; } if (attrPrefix) { attrPrefix->ToString(prefixStr); } else { prefixStr.Truncate(); } bool addNSAttr = false; if (kNameSpaceID_XMLNS != namespaceID) { nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr); addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true); } info.mValue->ToString(valueStr); nsDependentAtomString nameStr(attrName); bool isJS = false; if (kNameSpaceID_XHTML == contentNamespaceID) { // // Filter out special case of <br type="_moz"> or <br _moz*>, // used by the editor. Bug 16988. Yuck. // if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br && attrName == nsGkAtoms::type && StringBeginsWith(valueStr, _mozStr)) { continue; } if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li) && (attrName == nsGkAtoms::value)) { // This is handled separately in SerializeLIValueAttribute() continue; } isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); if (namespaceID == kNameSpaceID_None && ((attrName == nsGkAtoms::href) || (attrName == nsGkAtoms::src))) { // Make all links absolute when converting only the selection: if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) { // Would be nice to handle OBJECT tags, // but that gets more complicated since we have to // search the tag list for CODEBASE as well. // For now, just leave them relative. nsCOMPtr<nsIURI> uri = aContent->GetBaseURI(); if (uri) { nsAutoString absURI; rv = NS_MakeAbsoluteURI(absURI, valueStr, uri); if (NS_SUCCEEDED(rv)) { valueStr = absURI; } } } } if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta && attrName == nsGkAtoms::content) { // If we're serializing a <meta http-equiv="content-type">, // use the proper value, rather than what's in the document. nsAutoString header; aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header); if (header.LowerCaseEqualsLiteral("content-type")) { valueStr = NS_LITERAL_STRING("text/html; charset=") + NS_ConvertASCIItoUTF16(mCharset); } } // Expand shorthand attribute. if (namespaceID == kNameSpaceID_None && IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) { valueStr = nameStr; } } else { isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); } NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false); if (addNSAttr) { NS_ASSERTION(!prefixStr.IsEmpty(), "Namespaced attributes must have a prefix"); NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true), false); PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement); } } return true; }
NS_IMETHODIMP nsXMLContentSerializer::AppendElementStart(nsIDOMElement *aElement, nsIDOMElement *aOriginalElement, nsAString& aStr) { NS_ENSURE_ARG(aElement); nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; nsAutoString xmlnsStr; xmlnsStr.AssignLiteral(kXMLNS); nsCOMPtr<nsIContent> content(do_QueryInterface(aElement)); if (!content) return NS_ERROR_FAILURE; aElement->GetPrefix(tagPrefix); aElement->GetLocalName(tagLocalName); aElement->GetNamespaceURI(tagNamespaceURI); PRUint32 index, count; nsAutoString nameStr, prefixStr, uriStr, valueStr; count = content->GetAttrCount(); // First scan for namespace declarations, pushing each on the stack PRUint32 skipAttr = count; for (index = 0; index < count; index++) { const nsAttrName* name = content->GetAttrNameAt(index); PRInt32 namespaceID = name->NamespaceID(); nsIAtom *attrName = name->LocalName(); if (namespaceID == kNameSpaceID_XMLNS || // Also push on the stack attrs named "xmlns" in the null // namespace... because once we serialize those out they'll look like // namespace decls. :( // XXXbz what if we have both "xmlns" in the null namespace and "xmlns" // in the xmlns namespace? (namespaceID == kNameSpaceID_None && attrName == nsGkAtoms::xmlns)) { content->GetAttr(namespaceID, attrName, uriStr); if (!name->GetPrefix()) { if (tagNamespaceURI.IsEmpty() && !uriStr.IsEmpty()) { // If the element is in no namespace we need to add a xmlns // attribute to declare that. That xmlns attribute must not have a // prefix (see http://www.w3.org/TR/REC-xml-names/#dt-prefix), ie it // must declare the default namespace. We just found an xmlns // attribute that declares the default namespace to something // non-empty. We're going to ignore this attribute, for children we // will detect that we need to add it again and attributes aren't // affected by the default namespace. skipAttr = index; } else { // Default NS attribute does not have prefix (and the name is "xmlns") PushNameSpaceDecl(EmptyString(), uriStr, aOriginalElement); } } else { attrName->ToString(nameStr); PushNameSpaceDecl(nameStr, uriStr, aOriginalElement); } } } PRBool addNSAttr; MaybeAddNewline(aStr); addNSAttr = ConfirmPrefix(tagPrefix, tagNamespaceURI, aOriginalElement, PR_FALSE); // Serialize the qualified name of the element AppendToString(NS_LITERAL_STRING("<"), aStr); if (!tagPrefix.IsEmpty()) { AppendToString(tagPrefix, aStr); AppendToString(NS_LITERAL_STRING(":"), aStr); } AppendToString(tagLocalName, aStr); // If we had to add a new namespace declaration, serialize // and push it on the namespace stack if (addNSAttr) { if (tagPrefix.IsEmpty()) { // Serialize default namespace decl SerializeAttr(EmptyString(), xmlnsStr, tagNamespaceURI, aStr, PR_TRUE); } else { // Serialize namespace decl SerializeAttr(xmlnsStr, tagPrefix, tagNamespaceURI, aStr, PR_TRUE); } PushNameSpaceDecl(tagPrefix, tagNamespaceURI, aOriginalElement); } // Now serialize each of the attributes // XXX Unfortunately we need a namespace manager to get // attribute URIs. for (index = 0; index < count; index++) { if (skipAttr == index) { continue; } const nsAttrName* name = content->GetAttrNameAt(index); PRInt32 namespaceID = name->NamespaceID(); nsIAtom* attrName = name->LocalName(); nsIAtom* attrPrefix = name->GetPrefix(); if (attrPrefix) { attrPrefix->ToString(prefixStr); } else { prefixStr.Truncate(); } addNSAttr = PR_FALSE; if (kNameSpaceID_XMLNS != namespaceID) { nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr); addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, PR_TRUE); } content->GetAttr(namespaceID, attrName, valueStr); attrName->ToString(nameStr); // XXX Hack to get around the fact that MathML can add // attributes starting with '-', which makes them // invalid XML. if (!nameStr.IsEmpty() && nameStr.First() == '-') continue; if (namespaceID == kNameSpaceID_None) { if (content->GetNameSpaceID() == kNameSpaceID_XHTML) { if (IsShorthandAttr(attrName, content->Tag()) && valueStr.IsEmpty()) { valueStr = nameStr; } } } SerializeAttr(prefixStr, nameStr, valueStr, aStr, PR_TRUE); if (addNSAttr) { NS_ASSERTION(!prefixStr.IsEmpty(), "Namespaced attributes must have a prefix"); SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, PR_TRUE); PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement); } } // We don't output a separate end tag for empty element PRBool hasChildren; if (NS_FAILED(aOriginalElement->HasChildNodes(&hasChildren)) || !hasChildren) { AppendToString(NS_LITERAL_STRING("/>"), aStr); MaybeFlagNewline(aElement); } else { AppendToString(NS_LITERAL_STRING(">"), aStr); } return NS_OK; }
PRBool nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix, const nsAString& aURI, nsIDOMElement* aElement, PRBool aIsAttribute) { if (aPrefix.EqualsLiteral(kXMLNS)) { return PR_FALSE; } if (aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace")) { // The prefix must be xml for this namespace. We don't need to declare it, // so always just set the prefix to xml. aPrefix.AssignLiteral("xml"); return PR_FALSE; } PRBool mustHavePrefix; if (aIsAttribute) { if (aURI.IsEmpty()) { // Attribute in the null namespace. This just shouldn't have a prefix. // And there's no need to push any namespace decls aPrefix.Truncate(); return PR_FALSE; } // Attribute not in the null namespace -- must have a prefix mustHavePrefix = PR_TRUE; } else { // Not an attribute, so doesn't _have_ to have a prefix mustHavePrefix = PR_FALSE; } // Keep track of the closest prefix that's bound to aURI and whether we've // found such a thing. closestURIMatch holds the prefix, and uriMatch // indicates whether we actually have one. nsAutoString closestURIMatch; PRBool uriMatch = PR_FALSE; // Also keep track of whether we've seen aPrefix already. If we have, that // means that it's already bound to a URI different from aURI, so even if we // later (so in a more outer scope) see it bound to aURI we can't reuse it. PRBool haveSeenOurPrefix = PR_FALSE; PRInt32 count = mNameSpaceStack.Count(); PRInt32 index = count - 1; while (index >= 0) { NameSpaceDecl* decl = (NameSpaceDecl*)mNameSpaceStack.ElementAt(index); // Check if we've found a prefix match if (aPrefix.Equals(decl->mPrefix)) { // If the URIs match and aPrefix is not bound to any other URI, we can // use aPrefix if (!haveSeenOurPrefix && aURI.Equals(decl->mURI)) { // Just use our uriMatch stuff. That will deal with an empty aPrefix // the right way. We can break out of the loop now, though. uriMatch = PR_TRUE; closestURIMatch = aPrefix; break; } haveSeenOurPrefix = PR_TRUE; // If they don't, and either: // 1) We have a prefix (so we'd be redeclaring this prefix to point to a // different namespace) or // 2) We're looking at an existing default namespace decl on aElement (so // we can't create a new default namespace decl for this URI) // then generate a new prefix. Note that we do NOT generate new prefixes // if we happen to have aPrefix == decl->mPrefix == "" and mismatching // URIs when |decl| doesn't have aElement as its owner. In that case we // can simply push the new namespace URI as the default namespace for // aElement. if (!aPrefix.IsEmpty() || decl->mOwner == aElement) { NS_ASSERTION(!aURI.IsEmpty(), "Not allowed to add a xmlns attribute with an empty " "namespace name unless it declares the default " "namespace."); GenerateNewPrefix(aPrefix); // Now we need to validate our new prefix/uri combination; check it // against the full namespace stack again. Note that just restarting // the while loop is ok, since we haven't changed aURI, so the // closestURIMatch and uriMatch state is not affected. index = count - 1; haveSeenOurPrefix = PR_FALSE; continue; } } // If we've found a URI match, then record the first one if (!uriMatch && aURI.Equals(decl->mURI)) { // Need to check that decl->mPrefix is not declared anywhere closer to // us. If it is, we can't use it. PRBool prefixOK = PR_TRUE; PRInt32 index2; for (index2 = count-1; index2 > index && prefixOK; --index2) { NameSpaceDecl* decl2 = (NameSpaceDecl*)mNameSpaceStack.ElementAt(index2); prefixOK = (decl2->mPrefix != decl->mPrefix); } if (prefixOK) { uriMatch = PR_TRUE; closestURIMatch.Assign(decl->mPrefix); } } --index; } // At this point the following invariants hold: // 1) The prefix in closestURIMatch is mapped to aURI in our scope if // uriMatch is set. // 2) There is nothing on the namespace stack that has aPrefix as the prefix // and a _different_ URI, except for the case aPrefix.IsEmpty (and // possible default namespaces on ancestors) // So if uriMatch is set it's OK to use the closestURIMatch prefix. The one // exception is when closestURIMatch is actually empty (default namespace // decl) and we must have a prefix. if (uriMatch && (!mustHavePrefix || !closestURIMatch.IsEmpty())) { aPrefix.Assign(closestURIMatch); return PR_FALSE; } if (aPrefix.IsEmpty()) { // At this point, aPrefix is empty (which means we never had a prefix to // start with). If we must have a prefix, just generate a new prefix and // then send it back through the namespace stack checks to make sure it's // OK. if (mustHavePrefix) { GenerateNewPrefix(aPrefix); return ConfirmPrefix(aPrefix, aURI, aElement, aIsAttribute); } // One final special case. If aPrefix is empty and we never saw an empty // prefix (default namespace decl) on the namespace stack and we're in the // null namespace there is no reason to output an |xmlns=""| here. It just // makes the output less readable. if (!haveSeenOurPrefix && aURI.IsEmpty()) { return PR_FALSE; } } // Now just set aURI as the new default namespace URI. Indicate that we need // to create a namespace decl for the final prefix return PR_TRUE; }