Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome 22 outputs invalid XML when attributes have an xlink namespace

Tags:

I have the following minimal JavaScript fragment:

var xml = '<El a:title="T" a:href="H" xmlns:a="http://www.w3.org/1999/xlink" />'; var dom = new DOMParser().parseFromString(xml, 'text/xml'); xml = new XMLSerializer().serializeToString(dom); 

When I execute the code in most browsers (just paste it into your browser's JavaScript console), the parsed-then-serialized XML is equivalent to the original. For example on Chrome 8 I get:

<El xmlns:a="http://www.w3.org/1999/xlink" a:title="T" a:href="H"/> 

However on Chrome 22 the same code fragment changes the XML to:

<El xmlns:a="http://www.w3.org/1999/xlink" xlink:title="T" xlink:href="H"/> 

Note that the namespace prefix xlink used by the title and href attributes is not defined anywhere, so the XML is now invalid. As you probably can imagine this causes all kinds of problems for code that tries to subsequently use the XML.

Is this a bug in the XMLSerializer or am I missing some intricacies about how the DOM should be serialized?

Also did anyone find a workaround that I can put in code, as opposed to make the XML match the apparent preference to use xlink as the prefix for the XLink namespace?

Update

I did some additional testing and the problem seems to be caused by the fact that the XMLSerializer recognizes the XLink namespace and insists on outputting an xlink prefix for it, without properly registering that prefix.

So this fragment work fine:

var xml = '<El a:title="T" a:href="H" xmlns:a="any-other-namespace-uri" />'; var dom = new DOMParser().parseFromString(xml, 'text/xml'); xml = new XMLSerializer().serializeToString(dom); 

So here I changed the Namespace URL to something less well-known and the output is now valid:

<El xmlns:a="any-other-namespace-uri" a:title="T" a:href="H"/> 

The following fragment also works fine:

var xml = '<El a:title="T" a:href="H" xmlns:a="http://www.w3.org/2000/xlink" />'; var dom = new DOMParser().parseFromString(xml, 'text/xml'); xml = new XMLSerializer().serializeToString(dom); 

So in this case we use the "expected" prefix for the XLink namespace and it then serializes without problems:

<El xmlns:a="http://www.w3.org/2000/xlink" a:title="T" a:href="H"/> 
like image 493
Frank van Puffelen Avatar asked Oct 09 '12 15:10

Frank van Puffelen


People also ask

What are the attributes of an XML element?

XML elements can have attributes, just like HTML. Attributes are designed to contain data related to a specific element. Attribute values must always be quoted.

Why is Google Chrome rendering this XML file as HTML?

Try searching or browse recent questions. Why is Google Chrome rendering this XML file as HTML? The browser should render the content as XML since the content type of the request is application/xml. I think the browser thinks it's an HTML document because the content contains <xhtml:link /> tags, so it tries to guess but guesses it wrong.

When attributes are not in a namespace?

It is almost a rule that attributes are not in a namespace.   1. Attribute names must be unique   2. Attributes are not children   3. XML namespace and XML application   4. When attributes need a namespace   5. Attribute with prefix   6. Navigating XML in a namespace 1. Attribute names must be unique

Do attribute values need to be quoted in XML?

XML Attributes Must be Quoted Attribute values must always be quoted. Either single or double quotes can be used. For a person's gender, the <person> element can be written like this:


1 Answers

I am still quite certain that there is a bug in Chrome's XMLSerializer, most likely introduced while addressing the SVG handling of XLink attributes that Barbarrosa pointed to. But given the lack of response to the bug report I made for it, we've had to move forward and work around the problem.

We work around the problem by calling this function on the documentElement:

function EnsureXLinkNamespaceOnElement(element) {   if (element.nodeType == 1)   {     var usesXLinkNamespaceUri = false;     var hasXLinkNamespacePrefixDefined = false;     for (var i = 0; i < element.attributes.length; i++)      {       var attribute = element.attributes[i];       if (attribute.specified)       {         if (attribute.name.indexOf("xmlns:xlink") == 0)          {           hasXLinkNamespacePrefixDefined = true;         }          else if (attribute.namespaceURI == "http://www.w3.org/1999/xlink")         {           usesXLinkNamespaceUri = true;         }       }     }     if (usesXLinkNamespaceUri && !hasXLinkNamespacePrefixDefined)     {       element.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');     }      for (i = 0; i < element.childNodes.length; i++)     {       EnsureXLinkNamespaceOnElement(element.childNodes[i]);     }   } } 

The function simply ensures that the xmlns:xlink attribute is declared on any element that has attributed in the XLink namespace. Since the function traverses the tree and thus can be quite time-consuming, I only invoke it for Chrome versions 22 and higher.

Note that in most cases you could also get away with simply adding the xmlns:xlink namespace on the document element, since it will be inherited from there. But in our case there was some other code that strips the document element using a regular expression, so we decided to play it safe and simply add the attribute everywhere it may be needed.

Update (20130324):

The bug was fixed and verified in Chrome Canary 26. I have been able to verify it myself on version 25.0.1364.172 m too.

like image 107
Frank van Puffelen Avatar answered Sep 24 '22 01:09

Frank van Puffelen