Serialize an xmlns="" as needed if nodes are in the null namespace but their

ancestors have a default namespace.  Bug 301260, r+sr=peterv
This commit is contained in:
bzbarsky%mit.edu 2005-11-29 16:23:27 +00:00
parent 537e3c4829
commit 14107143f5
2 changed files with 64 additions and 31 deletions

View file

@ -335,33 +335,60 @@ PRBool
nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix, nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix,
const nsAString& aURI, const nsAString& aURI,
nsIDOMElement* aElement, nsIDOMElement* aElement,
PRBool aMustHavePrefix) PRBool aIsAttribute)
{ {
if (aPrefix.EqualsLiteral(kXMLNS) || if (aPrefix.EqualsLiteral(kXMLNS) ||
(aPrefix.EqualsLiteral("xml") && (aPrefix.EqualsLiteral("xml") &&
aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace"))) { aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace"))) {
return PR_FALSE; return PR_FALSE;
} }
if (aURI.IsEmpty()) {
aPrefix.Truncate(); PRBool mustHavePrefix;
return PR_FALSE; 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; nsAutoString closestURIMatch;
PRBool uriMatch = PR_FALSE; 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 count = mNameSpaceStack.Count();
PRInt32 index = count - 1; PRInt32 index = count - 1;
while (index >= 0) { while (index >= 0) {
NameSpaceDecl* decl = (NameSpaceDecl*)mNameSpaceStack.ElementAt(index); NameSpaceDecl* decl = (NameSpaceDecl*)mNameSpaceStack.ElementAt(index);
// Check if we've found a prefix match // Check if we've found a prefix match
if (aPrefix.Equals(decl->mPrefix)) { if (aPrefix.Equals(decl->mPrefix)) {
// If the URI's match, we don't have to add a namespace decl // If the URIs match and aPrefix is not bound to any other URI, we can
if (aURI.Equals(decl->mURI)) { // use aPrefix
return PR_FALSE; 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: // If they don't, and either:
// 1) We have a prefix (so we'd be redeclaring this prefix to point to a // 1) We have a prefix (so we'd be redeclaring this prefix to point to a
// different namespace) or // different namespace) or
@ -372,14 +399,14 @@ nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix,
// URIs when |decl| doesn't have aElement as its owner. In that case we // 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 // can simply push the new namespace URI as the default namespace for
// aElement. // aElement.
if (!aPrefix.IsEmpty() || if (!aPrefix.IsEmpty() || decl->mOwner == aElement) {
(decl->mPrefix.IsEmpty() && decl->mOwner == aElement)) {
GenerateNewPrefix(aPrefix); GenerateNewPrefix(aPrefix);
// Now we need to validate our new prefix/uri combination; check it // Now we need to validate our new prefix/uri combination; check it
// against the full namespace stack again. Note that just restarting // against the full namespace stack again. Note that just restarting
// the while loop is ok, since we haven't changed aURI, so the // the while loop is ok, since we haven't changed aURI, so the
// closestURIMatch state is not affected. // closestURIMatch and uriMatch state is not affected.
index = count - 1; index = count - 1;
haveSeenOurPrefix = PR_FALSE;
continue; continue;
} }
} }
@ -406,33 +433,41 @@ nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix,
} }
// At this point the following invariants hold: // At this point the following invariants hold:
// 1) There is nothing on the namespace stack that matches the pair // 1) The prefix in closestURIMatch is mapped to aURI in our scope if
// (aPrefix, aURI) // uriMatch is set.
// 2) There is nothing on the namespace stack that has aPrefix as the prefix // 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 // and a _different_ URI, except for the case aPrefix.IsEmpty (and
// possible default namespaces on ancestors) // possible default namespaces on ancestors)
// 3) The prefix in closestURIMatch is mapped to aURI in our scope if
// uriMatch is set.
// So if uriMatch is set it's OK to use the closestURIMatch prefix. The one // So if uriMatch is set it's OK to use the closestURIMatch prefix. The one
// exception is when closestURIMatch is actually empty (default namespace // exception is when closestURIMatch is actually empty (default namespace
// decl) and we must have a prefix. // decl) and we must have a prefix.
if (uriMatch && (!aMustHavePrefix || !closestURIMatch.IsEmpty())) { if (uriMatch && (!mustHavePrefix || !closestURIMatch.IsEmpty())) {
aPrefix.Assign(closestURIMatch); aPrefix.Assign(closestURIMatch);
return PR_FALSE; return PR_FALSE;
} }
// At this point, if aPrefix is empty (which means we never had a prefix to if (aPrefix.IsEmpty()) {
// start with) and we must have a prefix, just generate a new prefix and then // At this point, aPrefix is empty (which means we never had a prefix to
// send it back through the namespace stack checks to make sure it's OK. // start with). If we must have a prefix, just generate a new prefix and
if (aPrefix.IsEmpty() && aMustHavePrefix) { // then send it back through the namespace stack checks to make sure it's
GenerateNewPrefix(aPrefix); // OK.
return ConfirmPrefix(aPrefix, aURI, aElement, aMustHavePrefix); if (mustHavePrefix) {
} GenerateNewPrefix(aPrefix);
// else we will just set aURI as the new default namespace URI return ConfirmPrefix(aPrefix, aURI, aElement, aIsAttribute);
}
// Indicate that we need to create a namespace decl for the // One final special case. If aPrefix is empty and we never saw an empty
// final prefix // 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; return PR_TRUE;
} }
@ -626,8 +661,7 @@ nsXMLContentSerializer::AppendElementStart(nsIDOMElement *aElement,
addNSAttr = PR_FALSE; addNSAttr = PR_FALSE;
if (kNameSpaceID_XMLNS != namespaceID) { if (kNameSpaceID_XMLNS != namespaceID) {
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr); nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
addNSAttr = ConfirmPrefix(prefixStr, uriStr, aElement, addNSAttr = ConfirmPrefix(prefixStr, uriStr, aElement, PR_TRUE);
namespaceID != kNameSpaceID_None);
} }
content->GetAttr(namespaceID, attrName, valueStr); content->GetAttr(namespaceID, attrName, valueStr);

View file

@ -118,8 +118,7 @@ class nsXMLContentSerializer : public nsIContentSerializer {
* @param aURI the namespace URI we want aPrefix to point to * @param aURI the namespace URI we want aPrefix to point to
* @param aElement the element we're working with (needed for proper default * @param aElement the element we're working with (needed for proper default
* namespace handling) * namespace handling)
* @param aMustHavePrefix PR_TRUE if we the output prefix must be nonempty * @param aIsAttribute PR_TRUE if we're confirming a prefix for an attribute.
* whenever a new namespace decl is needed.
* @return PR_TRUE if we need to push the (prefix, uri) pair on the namespace * @return PR_TRUE if we need to push the (prefix, uri) pair on the namespace
* stack (note that this can happen even if the prefix is * stack (note that this can happen even if the prefix is
* empty). * empty).
@ -127,7 +126,7 @@ class nsXMLContentSerializer : public nsIContentSerializer {
PRBool ConfirmPrefix(nsAString& aPrefix, PRBool ConfirmPrefix(nsAString& aPrefix,
const nsAString& aURI, const nsAString& aURI,
nsIDOMElement* aElement, nsIDOMElement* aElement,
PRBool aMustHavePrefix); PRBool aIsAttribute);
/** /**
* GenerateNewPrefix generates a new prefix and writes it to aPrefix * GenerateNewPrefix generates a new prefix and writes it to aPrefix
*/ */