Title: [154779] trunk
Revision
154779
Author
[email protected]
Date
2013-08-28 15:25:19 -0700 (Wed, 28 Aug 2013)

Log Message

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.

Source/WebCore:

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:

LayoutTests:

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.

Modified Paths

Added Paths

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;
 };
 
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to