Diff
Modified: trunk/LayoutTests/ChangeLog (154778 => 154779)
--- trunk/LayoutTests/ChangeLog 2013-08-28 22:19:25 UTC (rev 154778)
+++ trunk/LayoutTests/ChangeLog 2013-08-28 22:25:19 UTC (rev 154779)
@@ -1,3 +1,27 @@
+2013-08-28 Rob Buis <[email protected]>
+
+ Namespace prefix is blindly followed when serializing
+ https://bugs.webkit.org/show_bug.cgi?id=19121
+ Serializer doesn't handling inconsistent prefixes properly
+ https://bugs.webkit.org/show_bug.cgi?id=117764
+ Attribute namespaces are serialized as if they were element ones
+ https://bugs.webkit.org/show_bug.cgi?id=22958
+
+ Reviewed by Ryosuke Niwa.
+
+ Add tests to make sure unique prefixes and namespace declarations are generated for the
+ case when the same prefix is used to map to different namespaces. All testcases are based
+ on the testcases attached to the bugs.
+
+ * fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts-expected.txt: Added.
+ * fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts.html: Added.
+ * fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict-expected.txt: Added.
+ * fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict.html: Added.
+ * fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix-expected.txt: Added.
+ * fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix.html: Added.
+ * svg/custom/xlink-prefix-generation-in-attributes-expected.txt: Added.
+ * svg/custom/xlink-prefix-generation-in-attributes.html: Added.
+
2013-08-28 Sam White <[email protected]>
AX: Cancel button in search field not accessible.
Added: trunk/LayoutTests/fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts-expected.txt (0 => 154779)
--- trunk/LayoutTests/fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts-expected.txt 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1 @@
+<doc xmlns="http://www.example.com/ns1" xmlns:p="http://www.example.com/p" xmlns:NS2="http://www.example.com/ns2"><test xmlns="http://www.example.com/ns2" NS1:a="I am q" xmlns:NS1="http://www.example.com/q" p:b="I am p" xmlns:xlink="http://www.example.com/xlink" NS3:href="" xmlns:NS3="http://www.w3.org/1999/xlink"/></doc>
Added: trunk/LayoutTests/fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts.html (0 => 154779)
--- trunk/LayoutTests/fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts.html (rev 0)
+++ trunk/LayoutTests/fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts.html 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script type="text/_javascript_">
+ function runTest()
+ {
+ if (window.testRunner)
+ testRunner.dumpAsText();
+
+ var xmlns = "http://www.w3.org/XML/1998/namespace";
+
+ var serializer = new XMLSerializer();
+
+ var doc = document.implementation.createDocument("http://www.example.com/ns1","doc",null);
+ doc.documentElement.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:p","http://www.example.com/p");
+ doc.documentElement.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:NS2","http://www.example.com/ns2");
+ var test = doc.createElementNS("http://www.example.com/ns2","test");
+ doc.documentElement.appendChild(test);
+ test.setAttributeNS("http://www.example.com/q","p:a","I am q");
+ test.setAttributeNS("http://www.example.com/p","p:b","I am p");
+ test.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.example.com/xlink");
+ test.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#foo");
+
+ var xmlString = serializer.serializeToString(doc);
+
+ var outputText = document.getElementById("output");
+ outputText.textContent = xmlString;
+ }
+ </script>
+</head>
+ <body _onload_="runTest()">
+ <div id="target"/>
+ <div id="output"/>
+ </body>
+</html>
Added: trunk/LayoutTests/fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict-expected.txt (0 => 154779)
--- trunk/LayoutTests/fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict-expected.txt 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1 @@
+<x:y xmlns:x="ns1" NS1:z="val" xmlns:NS1="ns2" NS2:w="val" xmlns:NS2="ns3"/>
Added: trunk/LayoutTests/fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict.html (0 => 154779)
--- trunk/LayoutTests/fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict.html (rev 0)
+++ trunk/LayoutTests/fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict.html 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script type="text/_javascript_">
+ function runTest()
+ {
+ if (window.testRunner)
+ testRunner.dumpAsText();
+
+ var doc = document.implementation.createDocument("", "", null);
+ var parent = doc.firstChild;
+ var element = doc.createElementNS("ns1", "x:y");
+ element.setAttributeNS("ns2", "x:z", "val");
+ element.setAttributeNS("ns3", "w", "val");
+ doc.appendChild(element);
+ var serializer = new XMLSerializer();
+ var xmlString = serializer.serializeToString(doc);
+
+ var outputText = document.getElementById("output");
+ outputText.textContent = xmlString;
+ }
+ </script>
+</head>
+ <body _onload_="runTest()">
+ <div id="target"/>
+ <div id="output"/>
+ </body>
+</html>
Added: trunk/LayoutTests/fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix-expected.txt (0 => 154779)
--- trunk/LayoutTests/fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix-expected.txt 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1 @@
+PASS
Added: trunk/LayoutTests/fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix.html (0 => 154779)
--- trunk/LayoutTests/fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix.html (rev 0)
+++ trunk/LayoutTests/fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix.html 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script type="text/_javascript_">
+ function runTest()
+ {
+ if (window.testRunner)
+ testRunner.dumpAsText();
+
+ var outputText = document.getElementById("output");
+
+ document.getElementById('target').setAttributeNS('ns', 'title', 'value');
+ if (document.getElementById('target').outerHTML.match('xmlns="ns"'))
+ outputText.textContent = 'FAIL: ' + document.getElementById('target').outerHTML;
+ else
+ outputText.textContent = "PASS";
+ }
+ </script>
+</head>
+ <body _onload_="runTest()">
+ <div id="target"/>
+ <div id="output"/>
+ </body>
+</html>
Added: trunk/LayoutTests/svg/custom/xlink-prefix-generation-in-attributes-expected.txt (0 => 154779)
--- trunk/LayoutTests/svg/custom/xlink-prefix-generation-in-attributes-expected.txt (rev 0)
+++ trunk/LayoutTests/svg/custom/xlink-prefix-generation-in-attributes-expected.txt 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" NS1:href="" xmlns:NS1="http://www.w3.org/1999/xlink"/>
+<svg xmlns="http://www.w3.org/2000/svg" NS1:href="" xmlns:NS1="http://www.w3.org/1999/xlink"/>
+<svg xmlns="http://www.w3.org/2000/svg" xlink:href="" xmlns:xlink="http://www.w3.org/1999/xlink"/>
+<svg xmlns="http://www.w3.org/2000/svg" xlink:href="" xmlns:xlink="http://www.w3.org/1999/xlink"/>
Added: trunk/LayoutTests/svg/custom/xlink-prefix-generation-in-attributes.html (0 => 154779)
--- trunk/LayoutTests/svg/custom/xlink-prefix-generation-in-attributes.html (rev 0)
+++ trunk/LayoutTests/svg/custom/xlink-prefix-generation-in-attributes.html 2013-08-28 22:25:19 UTC (rev 154779)
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script type="text/_javascript_">
+ function runTest()
+ {
+ if (window.testRunner)
+ testRunner.dumpAsText();
+
+ var svgns = "http://www.w3.org/2000/svg";
+ var xlinkns = "http://www.w3.org/1999/xlink";
+
+ var serializer = new XMLSerializer();
+
+ // Example 1: Create a document with a namespaced attribute but via the DOM API but do not specific a prefix.
+ var doc = document.implementation.createDocument(svgns, "svg", null);
+ doc.documentElement.setAttributeNS(xlinkns, "href", "#foo");
+ var xml = serializer.serializeToString(doc);
+ document.getElementById("svgoutput").textContent = xml + "\n";
+
+ // Example 2: Attempt to fix the document by setting the attribute. The Node.prefix property should now contain "xlink".
+ doc = document.implementation.createDocument(svgns, "svg", null);
+ doc.documentElement.setAttributeNS(xlinkns, "href", "#foo");
+ doc.documentElement.setAttributeNS(xlinkns, "xlink:href", "#foo");
+ xml = serializer.serializeToString(doc);
+ document.getElementById("svgoutput2").textContent = xml + "\n";
+
+ // Example 3: Attempt to fix the document by setting Node.prefix. The Node.prefix property should now contain "xlink".
+ doc = document.implementation.createDocument(svgns, "svg", null);
+ doc.documentElement.setAttributeNS(xlinkns, "href", "#foo");
+ doc.documentElement.attributes[0].prefix = "xlink";
+ xml = serializer.serializeToString(doc);
+ document.getElementById("svgoutput3").textContent = xml + "\n";
+
+ // Example 4: Create the document with prefixes specified. The Node.prefix property should now contain "xlink".
+ doc = document.implementation.createDocument(svgns, "svg", null);
+ doc.documentElement.setAttributeNS(xlinkns, "xlink:href", "#foo");
+ xml = serializer.serializeToString(doc);
+ document.getElementById("svgoutput4").textContent = xml + "\n";
+ }
+ </script>
+</head>
+ <body _onload_="runTest()">
+ <div id="svgoutput"></div>
+ <div id="svgoutput2"></div>
+ <div id="svgoutput3"></div>
+ <div id="svgoutput4"></div>
+ </body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (154778 => 154779)
--- trunk/Source/WebCore/ChangeLog 2013-08-28 22:19:25 UTC (rev 154778)
+++ trunk/Source/WebCore/ChangeLog 2013-08-28 22:25:19 UTC (rev 154779)
@@ -1,3 +1,43 @@
+2013-08-28 Rob Buis <[email protected]>
+
+ Namespace prefix is blindly followed when serializing
+ https://bugs.webkit.org/show_bug.cgi?id=19121
+ Serializer doesn't handling inconsistent prefixes properly
+ https://bugs.webkit.org/show_bug.cgi?id=117764
+ Attribute namespaces are serialized as if they were element ones
+ https://bugs.webkit.org/show_bug.cgi?id=22958
+
+ Reviewed by Ryosuke Niwa.
+
+ Add code to make sure unique prefixes and namespace declarations are generated.
+ Unique prefix generation happens when:
+ - the same prefix is used to map to different namespaces or
+ - no prefix is given but the attribute is in a namespace.
+
+ This is done in order to not violate constraints listed in http://www.w3.org/TR/xml-names11/. In general
+ the pseudo code listed in http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#normalizeDocumentAlgo
+ is used, doing the following for attributes:
+ if the attribute has a namespace then
+ if the attribute has no prefix OR prefix is not declared OR conflicts with existing prefix mapping to different NS then
+ try to find the matching in-scope declaration by looking up the prefix in the namespace -> prefix mapping, if found use that prefix
+ else if the attribute prefix is not null AND not mapped in-scope, declare the prefix
+ else generate a unique prefix for the namespace
+
+ To keep track of in-scope namespaces a prefix to namespace mapping is used.
+
+ Tests: fast/dom/XMLSerializer-attribute-namespace-prefix-conflicts.html
+ fast/dom/XMLSerializer-same-prefix-different-namespaces-conflict.html
+ fast/dom/XMLSerializer-setAttributeNS-namespace-no-prefix.html
+ svg/custom/xlink-prefix-generation-in-attributes.html
+
+ * editing/MarkupAccumulator.cpp:
+ (WebCore::MarkupAccumulator::MarkupAccumulator):
+ (WebCore::MarkupAccumulator::shouldAddNamespaceAttribute):
+ (WebCore::MarkupAccumulator::appendNamespace):
+ (WebCore::MarkupAccumulator::generateUniquePrefix):
+ (WebCore::MarkupAccumulator::appendAttribute):
+ * editing/MarkupAccumulator.h:
+
2013-08-28 Sam White <[email protected]>
AX: Cancel button in search field not accessible.
Modified: trunk/Source/WebCore/editing/MarkupAccumulator.cpp (154778 => 154779)
--- trunk/Source/WebCore/editing/MarkupAccumulator.cpp 2013-08-28 22:19:25 UTC (rev 154778)
+++ trunk/Source/WebCore/editing/MarkupAccumulator.cpp 2013-08-28 22:25:19 UTC (rev 154779)
@@ -105,6 +105,7 @@
, m_range(range)
, m_resolveURLsMethod(resolveUrlsMethod)
, m_fragmentSerialization(fragmentSerialization)
+ , m_prefixLevel(0)
{
}
@@ -287,6 +288,7 @@
QualifiedName xmlnsPrefixAttr(xmlnsAtom, attribute.localName(), XMLNSNames::xmlnsNamespaceURI);
if (attribute.name() == xmlnsPrefixAttr) {
namespaces.set(attribute.localName().impl(), attribute.value().impl());
+ namespaces.set(attribute.value().impl(), attribute.localName().impl());
return false;
}
@@ -309,8 +311,14 @@
// Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key
AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl();
AtomicStringImpl* foundNS = namespaces.get(pre);
- if (foundNS != namespaceURI.impl() && !namespaces.get(namespaceURI.impl())) {
+ if (foundNS != namespaceURI.impl()) {
namespaces.set(pre, namespaceURI.impl());
+ // Add namespace to prefix pair so we can do constraint checking later.
+ if (inXMLFragmentSerialization() && !prefix.isEmpty())
+ namespaces.set(namespaceURI.impl(), pre);
+ // Make sure xml prefix and namespace are always known to uphold the constraints listed at http://www.w3.org/TR/xml-names11/#xmlReserved.
+ if (namespaceURI.impl() == XMLNames::xmlNamespaceURI.impl())
+ return;
result.append(' ');
result.append(xmlnsAtom.string());
if (!prefix.isEmpty()) {
@@ -466,6 +474,24 @@
|| attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI;
}
+void MarkupAccumulator::generateUniquePrefix(QualifiedName& prefixedName, const Namespaces& namespaces)
+{
+ // http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#normalizeDocumentAlgo
+ // Find a prefix following the pattern "NS" + index (starting at 1) and make sure this
+ // prefix is not declared in the current scope.
+ StringBuilder builder;
+ do {
+ builder.clear();
+ builder.append("NS");
+ builder.appendNumber(++m_prefixLevel);
+ const AtomicString& name = builder.toAtomicString();
+ if (!namespaces.get(name.impl())) {
+ prefixedName.setPrefix(name);
+ return;
+ }
+ } while (true);
+}
+
void MarkupAccumulator::appendAttribute(StringBuilder& result, Element* element, const Attribute& attribute, Namespaces* namespaces)
{
bool documentIsHTML = element->document()->isHTMLDocument();
@@ -476,15 +502,18 @@
if (documentIsHTML && !attributeIsInSerializedNamespace(attribute))
result.append(attribute.name().localName());
else {
- if (attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI) {
- if (!attribute.prefix())
- prefixedName.setPrefix(xlinkAtom);
- } else if (attribute.namespaceURI() == XMLNames::xmlNamespaceURI) {
- if (!attribute.prefix())
- prefixedName.setPrefix(xmlAtom);
- } else if (attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI) {
- if (attribute.name() != XMLNSNames::xmlnsAttr && !attribute.prefix())
- prefixedName.setPrefix(xmlnsAtom);
+ if (!attribute.namespaceURI().isEmpty()) {
+ AtomicStringImpl* foundNS = namespaces && attribute.prefix().impl() ? namespaces->get(attribute.prefix().impl()) : 0;
+ bool prefixIsAlreadyMappedToOtherNS = foundNS && foundNS != attribute.namespaceURI().impl();
+ if (attribute.prefix().isEmpty() || !foundNS || prefixIsAlreadyMappedToOtherNS) {
+ if (AtomicStringImpl* prefix = namespaces ? namespaces->get(attribute.namespaceURI().impl()) : 0)
+ prefixedName.setPrefix(AtomicString(prefix));
+ else {
+ bool shouldBeDeclaredUsingAppendNamespace = !attribute.prefix().isEmpty() && !foundNS;
+ if (!shouldBeDeclaredUsingAppendNamespace && attribute.localName() != xmlnsAtom && namespaces)
+ generateUniquePrefix(prefixedName, *namespaces);
+ }
+ }
}
result.append(prefixedName.toString());
}
Modified: trunk/Source/WebCore/editing/MarkupAccumulator.h (154778 => 154779)
--- trunk/Source/WebCore/editing/MarkupAccumulator.h 2013-08-28 22:19:25 UTC (rev 154778)
+++ trunk/Source/WebCore/editing/MarkupAccumulator.h 2013-08-28 22:25:19 UTC (rev 154779)
@@ -112,10 +112,12 @@
void appendQuotedURLAttributeValue(StringBuilder&, const Element*, const Attribute&);
void serializeNodesWithNamespaces(Node* targetNode, Node* nodeToSkip, EChildrenOnly, const Namespaces*, Vector<QualifiedName>* tagNamesToSkip);
bool inXMLFragmentSerialization() const { return m_fragmentSerialization == XMLFragmentSerialization; }
+ void generateUniquePrefix(QualifiedName&, const Namespaces&);
StringBuilder m_markup;
const EAbsoluteURLs m_resolveURLsMethod;
EFragmentSerialization m_fragmentSerialization;
+ unsigned m_prefixLevel;
};
}