XMLSerializer doesn't include namespaces on nodes in HTML documents
authorrwlbuis@webkit.org <rwlbuis@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Jul 2013 00:37:02 +0000 (00:37 +0000)
committerrwlbuis@webkit.org <rwlbuis@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 Jul 2013 00:37:02 +0000 (00:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=16496

Patch by Rob Buis <rwlbuis@webkit.org> on 2013-07-15
Reviewed by Ryosuke Niwa.

Source/WebCore:

Introduce a xml fragment serialization mode as indicated by
http://html5.org/specs/dom-parsing.html#xmlserializer (commit 00b84d2). In this mode
the XML fragment serialization algorithm is respected, the changes in this patch do the following:

- elements are self-closing if otherwise this would lead to invalid xml syntax.
- the resulting xml is XML namespace-well-formed (http://www.w3.org/TR/xml-names11/#Conformance).
This is achieved by always, when in xml fragment serialization mode, attempting to write out the element/attribute namespace,
preventing using the XML namespace as a default namespace and special casing the use of element's in XML namespace by using the xml prefix.

The chosen approach matches FireFox 25 behavior.

Test: fast/dom/dom-serialize-namespace.html

* WebCore.order: Adapt to changed createMarkup signature.
* editing/MarkupAccumulator.cpp:
(WebCore::MarkupAccumulator::MarkupAccumulator):
(WebCore::MarkupAccumulator::serializeNodesWithNamespaces): makes sure xml namespace/prefix is known so it is never used in namespace declarations.
(WebCore::MarkupAccumulator::appendNamespace): Avoid adding namespace declarations that do not differ from current default namespace.
(WebCore::MarkupAccumulator::appendOpenTag): Print xml prefix if the element's namespace is XML to avoid conflicts.
(WebCore::MarkupAccumulator::appendAttribute):
(WebCore::MarkupAccumulator::shouldAddNamespaceAttribute): Also take into account xmlns attributes with no namespace.
(WebCore::MarkupAccumulator::shouldSelfClose): Force self-closing to create well-formed XML elements.
* editing/MarkupAccumulator.h: Use EFragmentSerialization.
(WebCore::MarkupAccumulator::inXMLFragmentSerialization):
* editing/markup.cpp:
(WebCore::createMarkup):
* editing/markup.h: Add EFragmentSerialization enum.
* xml/XMLSerializer.cpp:
(WebCore::XMLSerializer::serializeToString):

LayoutTests:

The updated tests are progressions and match FF.

* fast/dom/Element/getAttribute-check-case-sensitivity-expected.txt:
* fast/dom/Element/script-tests/getAttribute-check-case-sensitivity.js:
* fast/dom/XMLSerializer-xml-namespace-expected.txt:
* fast/dom/dom-serialize-namespace-expected.txt: Added.
* fast/dom/dom-serialize-namespace.html: Added.
* fast/xsl/xslt-processor-expected.txt:
* inspector/elements/set-outer-html-expected.txt:
* svg/custom/xlink-prefix-in-attributes-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@152685 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/Element/getAttribute-check-case-sensitivity-expected.txt
LayoutTests/fast/dom/Element/script-tests/getAttribute-check-case-sensitivity.js
LayoutTests/fast/dom/XMLSerializer-xml-namespace-expected.txt
LayoutTests/fast/dom/dom-serialize-namespace-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/dom-serialize-namespace.html [new file with mode: 0644]
LayoutTests/fast/xsl/xslt-processor-expected.txt
LayoutTests/inspector/elements/set-outer-html-for-xhtml-expected.txt
LayoutTests/svg/custom/xlink-prefix-in-attributes-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.order
Source/WebCore/editing/MarkupAccumulator.cpp
Source/WebCore/editing/MarkupAccumulator.h
Source/WebCore/editing/markup.cpp
Source/WebCore/editing/markup.h
Source/WebCore/xml/XMLSerializer.cpp

index c0ec05a..c293d03 100644 (file)
@@ -1,3 +1,21 @@
+2013-07-15  Rob Buis  <rwlbuis@webkit.org>
+
+        XMLSerializer doesn't include namespaces on nodes in HTML documents
+        https://bugs.webkit.org/show_bug.cgi?id=16496
+
+        Reviewed by Ryosuke Niwa.
+
+        The updated tests are progressions and match FF.
+
+        * fast/dom/Element/getAttribute-check-case-sensitivity-expected.txt:
+        * fast/dom/Element/script-tests/getAttribute-check-case-sensitivity.js:
+        * fast/dom/XMLSerializer-xml-namespace-expected.txt:
+        * fast/dom/dom-serialize-namespace-expected.txt: Added.
+        * fast/dom/dom-serialize-namespace.html: Added.
+        * fast/xsl/xslt-processor-expected.txt:
+        * inspector/elements/set-outer-html-expected.txt:
+        * svg/custom/xlink-prefix-in-attributes-expected.txt:
+
 2013-07-15  Ryosuke Niwa  <rniwa@webkit.org>
 
         compositionstart event should contain the text to be replaced
index 3f9a3c0..8cde726 100644 (file)
@@ -13,7 +13,7 @@ PASS testAttribNodeNamePreservesCase() is "A,A"
 PASS testAttribNodeNamePreservesCaseGetNode() is "A,A"
 PASS testAttribNodeNamePreservesCaseGetNode2() is "B,B"
 PASS testAttribNodeNameGetMutate() is "1"
-PASS (new XMLSerializer).serializeToString(node) is "<div myAttrib=\"XXX\"></div>"
+PASS (new XMLSerializer).serializeToString(node) is "<div xmlns=\"http://www.w3.org/1999/xhtml\" myAttrib=\"XXX\"></div>"
 PASS node.getAttributeNode('myAttrib').name is "myAttrib"
 PASS node.getAttributeNode('myattrib').name is "myAttrib"
 PASS attrib.name is "myAttrib"
index bc52298..06f6cf7 100644 (file)
@@ -135,7 +135,7 @@ var attrib = document.createAttribute("myAttrib");
 attrib.nodeValue = "XXX";
 node.setAttributeNode(attrib);
 
-shouldBe("(new XMLSerializer).serializeToString(node)", '"<div myAttrib=\\"XXX\\"></div>"');
+shouldBe("(new XMLSerializer).serializeToString(node)", '"<div xmlns=\\"http://www.w3.org/1999/xhtml\\" myAttrib=\\"XXX\\"></div>"');
 shouldBe("node.getAttributeNode('myAttrib').name", '"myAttrib"');
 shouldBe("node.getAttributeNode('myattrib').name", '"myAttrib"');
 shouldBe("attrib.name", '"myAttrib"');
index 0c7fcb9..104d3b7 100644 (file)
@@ -1,2 +1,2 @@
-<div id="target"> <div id="output"> </div><foo xml:space="preserve"></foo><bar xml:space="default"></bar></div>
+<div xmlns="http://www.w3.org/1999/xhtml" id="target"> <div id="output"> </div><xml:foo xml:space="preserve"/><xml:bar xml:space="default"/></div>
 
diff --git a/LayoutTests/fast/dom/dom-serialize-namespace-expected.txt b/LayoutTests/fast/dom/dom-serialize-namespace-expected.txt
new file mode 100644 (file)
index 0000000..20ce8f5
--- /dev/null
@@ -0,0 +1,11 @@
+This test verifies that the namespace on a node gets serialized if present even if the node is not in the DOM tree.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS s.serializeToString(node).replace(/"/g,"'") is "<test xmlns='http://example.com/'/>"
+PASS s.serializeToString(doc).replace(/"/g,"'") is "<test xmlns='http://example.com/'/>"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/dom-serialize-namespace.html b/LayoutTests/fast/dom/dom-serialize-namespace.html
new file mode 100644 (file)
index 0000000..cc8c85a
--- /dev/null
@@ -0,0 +1,16 @@
+<html>
+    <script src="../js/resources/js-test-pre.js"></script>
+    <script type="text/javascript">
+        description("This test verifies that the namespace on a node gets serialized if present even if the node is not in the DOM tree.");
+
+        var s = new XMLSerializer();
+        var node = document.createElementNS("http://example.com/", "test")
+        shouldBeEqualToString("s.serializeToString(node).replace(/\"/g,\"'\")", "<test xmlns='http://example.com/'/>");
+
+        var doc = document.implementation.createDocument("", "", null);
+        doc.appendChild(document.createElementNS("http://example.com/", "test"))
+        shouldBeEqualToString("s.serializeToString(doc).replace(/\"/g,\"'\")", "<test xmlns='http://example.com/'/>");
+
+    </script>
+    <script src="../js/resources/js-test-post.js"></script>
+</html>
index 8517f99..32bd47f 100644 (file)
@@ -36,8 +36,8 @@
 1.0 void importStylesheet(in DOMNode style):
 
 1.1 Import two different stylesheets:
-<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body>
-              CHARACTERS IN XSLT: ééééééééééé <br><br>SOURCE XML: &lt;&lt;&lt;&amp;тест&amp;&gt;&gt;&gt;</body></html>
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>
+              CHARACTERS IN XSLT: ééééééééééé <br /><br />SOURCE XML: &lt;&lt;&lt;&amp;тест&amp;&gt;&gt;&gt;</body></html>
 1.2 Import same stylesheet twice:
 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
 <head><title></title></head>
index 778de88..51e9e3f 100644 (file)
@@ -15,9 +15,9 @@ Event AttrRemoved: div
 Event CharacterDataModified: 
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting not involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting not involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -27,9 +27,9 @@ Wrapper identity: identity
 Event CharacterDataModified: 
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -46,9 +46,9 @@ Event AttrModified: a
 Event AttrRemoved: a
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a foo="bar" href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a foo="bar" href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -60,9 +60,9 @@ Event AttrRemoved: a
 Event AttrRemoved: a
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -77,9 +77,9 @@ Wrapper identity: identity
 Event NodeRemoved: 
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml"></h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2></h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -89,9 +89,9 @@ Wrapper identity: identity
 Event NodeInserted: 
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -108,9 +108,9 @@ Event NodeInserted: h2
 Event NodeRemoved: h2
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting</h2><h2 xmlns="http://www.w3.org/1999/xhtml">involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting</h2><h2>involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -122,9 +122,9 @@ Event NodeRemoved: h2
 Event NodeRemoved: h2
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -140,9 +140,9 @@ Event NodeInserted: h3
 Event NodeRemoved: h2
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h3 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h3>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h3>Getting involved</h3>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
@@ -153,9 +153,9 @@ Event NodeInserted: h2
 Event NodeRemoved: h3
 ==========8<==========
 <div xmlns="http://www.w3.org/1999/xhtml" id="container" style="display:none">
-<p xmlns="http://www.w3.org/1999/xhtml">WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
-<h2 xmlns="http://www.w3.org/1999/xhtml">Getting involved</h2>
-<p xmlns="http://www.w3.org/1999/xhtml" id="identity">There are many ways to get involved. You can:</p>
+<p>WebKit is used by <a href="http://www.apple.com/safari/">Safari</a>, Dashboard, etc.</p>
+<h2>Getting involved</h2>
+<p id="identity">There are many ways to get involved. You can:</p>
 </div>
 ==========>8==========
 
index 7968f19..a43cd20 100644 (file)
@@ -1,2 +1,2 @@
-<div id="target"> <div id="svgoutput"> </div><svg xmlns="http://www.w3.org/2000/svg" svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><image width="20" height="20" xlink:href="resources/green-checker.png"></image><image x="0" y="30" width="20" height="20" xlink:href="resources/green-checker.png"></image></svg></div> <El xmlns:a="http://www.w3.org/1999/xlink" a:title="C" a:href="H"/>
+<div xmlns="http://www.w3.org/1999/xhtml" id="target"> <div id="svgoutput"> </div><svg xmlns="http://www.w3.org/2000/svg" svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><image width="20" height="20" xlink:href="resources/green-checker.png"/><image x="0" y="30" width="20" height="20" xlink:href="resources/green-checker.png"/></svg></div> <El xmlns:a="http://www.w3.org/1999/xlink" a:title="C" a:href="H"/>
 
index b7911bd..105cdca 100644 (file)
@@ -1,3 +1,40 @@
+2013-07-15  Rob Buis  <rwlbuis@webkit.org>
+
+        XMLSerializer doesn't include namespaces on nodes in HTML documents
+        https://bugs.webkit.org/show_bug.cgi?id=16496
+
+        Reviewed by Ryosuke Niwa.
+
+        Introduce a xml fragment serialization mode as indicated by
+        http://html5.org/specs/dom-parsing.html#xmlserializer (commit 00b84d2). In this mode
+        the XML fragment serialization algorithm is respected, the changes in this patch do the following:
+
+        - elements are self-closing if otherwise this would lead to invalid xml syntax.
+        - the resulting xml is XML namespace-well-formed (http://www.w3.org/TR/xml-names11/#Conformance).
+        This is achieved by always, when in xml fragment serialization mode, attempting to write out the element/attribute namespace,
+        preventing using the XML namespace as a default namespace and special casing the use of element's in XML namespace by using the xml prefix.
+
+        The chosen approach matches FireFox 25 behavior.
+
+        Test: fast/dom/dom-serialize-namespace.html
+
+        * WebCore.order: Adapt to changed createMarkup signature.
+        * editing/MarkupAccumulator.cpp:
+        (WebCore::MarkupAccumulator::MarkupAccumulator):
+        (WebCore::MarkupAccumulator::serializeNodesWithNamespaces): makes sure xml namespace/prefix is known so it is never used in namespace declarations.
+        (WebCore::MarkupAccumulator::appendNamespace): Avoid adding namespace declarations that do not differ from current default namespace.
+        (WebCore::MarkupAccumulator::appendOpenTag): Print xml prefix if the element's namespace is XML to avoid conflicts.
+        (WebCore::MarkupAccumulator::appendAttribute):
+        (WebCore::MarkupAccumulator::shouldAddNamespaceAttribute): Also take into account xmlns attributes with no namespace.
+        (WebCore::MarkupAccumulator::shouldSelfClose): Force self-closing to create well-formed XML elements.
+        * editing/MarkupAccumulator.h: Use EFragmentSerialization.
+        (WebCore::MarkupAccumulator::inXMLFragmentSerialization):
+        * editing/markup.cpp:
+        (WebCore::createMarkup):
+        * editing/markup.h: Add EFragmentSerialization enum.
+        * xml/XMLSerializer.cpp:
+        (WebCore::XMLSerializer::serializeToString):
+
 2013-07-15  Ryosuke Niwa  <rniwa@webkit.org>
 
         MediaFragmentURIParser::parseFragments shouldn't upconvert 8-bit string
index 7b380a0..409f6f2 100644 (file)
@@ -8046,7 +8046,7 @@ __ZN7WebCore19CSSStyleDeclaration14isPropertyNameERKN3WTF6StringE
 __ZN7WebCore11RenderLayer20setHasVisibleContentEb
 __ZN7WebCore22jsHTMLElementInnerHTMLEPN3JSC9ExecStateENS0_7JSValueERKNS0_10IdentifierE
 __ZNK7WebCore11HTMLElement9innerHTMLEv
-__ZN7WebCore12createMarkupEPKNS_4NodeENS_13EChildrenOnlyEPN3WTF6VectorIPS0_Lm0EEENS_13EAbsoluteURLsE
+__ZN7WebCore12createMarkupEPKNS_4NodeENS_13EChildrenOnlyEPN3WTF6VectorIPS0_Lm0EEENS_13EAbsoluteURLsENS_22EFragmentSerializationE
 __ZN7WebCore17MarkupAccumulatorC1EPN3WTF6VectorIPNS_4NodeELm0EEENS_13EAbsoluteURLsEPKNS_5RangeE
 __ZN7WebCore17MarkupAccumulator14serializeNodesEPNS_4NodeES2_NS_13EChildrenOnlyE
 __ZN7WebCore17MarkupAccumulator28serializeNodesWithNamespacesEPNS_4NodeES2_NS_13EChildrenOnlyEPKN3WTF7HashMapIPNS4_16AtomicStringImplES7_NS4_7PtrHashIS7_EENS4_10HashTraitsIS7_EESB_EE
index a8571c7..fced6ca 100644 (file)
@@ -100,10 +100,11 @@ void MarkupAccumulator::appendCharactersReplacingEntities(StringBuilder& result,
     }
 }
 
-MarkupAccumulator::MarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs resolveUrlsMethod, const Range* range)
+MarkupAccumulator::MarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs resolveUrlsMethod, const Range* range, EFragmentSerialization fragmentSerialization)
     : m_nodes(nodes)
     , m_range(range)
     , m_resolveURLsMethod(resolveUrlsMethod)
+    , m_fragmentSerialization(fragmentSerialization)
 {
 }
 
@@ -137,6 +138,11 @@ void MarkupAccumulator::serializeNodesWithNamespaces(Node* targetNode, Node* nod
     Namespaces namespaceHash;
     if (namespaces)
         namespaceHash = *namespaces;
+    else if (inXMLFragmentSerialization()) {
+        // Make sure xml prefix and namespace are always known to uphold the constraints listed at http://www.w3.org/TR/xml-names11/#xmlReserved.
+        namespaceHash.set(xmlAtom.impl(), XMLNames::xmlNamespaceURI.impl());
+        namespaceHash.set(XMLNames::xmlNamespaceURI.impl(), xmlAtom.impl());
+    }
 
     if (!childrenOnly)
         appendStartTag(targetNode, &namespaceHash);
@@ -272,7 +278,8 @@ bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute& attribute,
     namespaces.checkConsistency();
 
     // Don't add namespace attributes twice
-    if (attribute.name() == XMLNSNames::xmlnsAttr) {
+    // HTML Parser will create xmlns attributes without namespace for HTML elements, allow those as well.
+    if (attribute.name().localName() == xmlnsAtom && (attribute.namespaceURI().isEmpty() || attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI)) {
         namespaces.set(emptyAtom.impl(), attribute.value().impl());
         return false;
     }
@@ -295,7 +302,7 @@ void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicStrin
     // 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()) {
+    if (foundNS != namespaceURI.impl() && !namespaces.get(namespaceURI.impl())) {
         namespaces.set(pre, namespaceURI.impl());
         result.append(' ');
         result.append(xmlnsAtom.string());
@@ -421,8 +428,17 @@ void MarkupAccumulator::appendElement(StringBuilder& result, Element* element, N
 void MarkupAccumulator::appendOpenTag(StringBuilder& result, Element* element, Namespaces* namespaces)
 {
     result.append('<');
+    if (inXMLFragmentSerialization() && namespaces && element->prefix().isEmpty()) {
+        // According to http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#normalizeDocumentAlgo we now should create
+        // a default namespace declaration to make this namespace well-formed. However, http://www.w3.org/TR/xml-names11/#xmlReserved states
+        // "The prefix xml MUST NOT be declared as the default namespace.", so use the xml prefix explicitly.
+        if (element->namespaceURI() == XMLNames::xmlNamespaceURI) {
+            result.append(xmlAtom);
+            result.append(':');
+        }
+    }
     result.append(element->nodeNamePreservingCase());
-    if (!element->document()->isHTMLDocument() && namespaces && shouldAddNamespaceElement(element))
+    if ((inXMLFragmentSerialization() || !element->document()->isHTMLDocument()) && namespaces && shouldAddNamespaceElement(element))
         appendNamespace(result, element->prefix(), element->namespaceURI(), *namespaces);
 }
 
@@ -476,7 +492,7 @@ void MarkupAccumulator::appendAttribute(StringBuilder& result, Element* element,
         result.append('"');
     }
 
-    if (!documentIsHTML && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces))
+    if ((inXMLFragmentSerialization() || !documentIsHTML) && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces))
         appendNamespace(result, attribute.prefix(), attribute.namespaceURI(), *namespaces);
 }
 
@@ -534,7 +550,7 @@ void MarkupAccumulator::appendStartMarkup(StringBuilder& result, const Node* nod
 // 4. Other elements self-close.
 bool MarkupAccumulator::shouldSelfClose(const Node* node)
 {
-    if (node->document()->isHTMLDocument())
+    if (!inXMLFragmentSerialization() && node->document()->isHTMLDocument())
         return false;
     if (node->hasChildNodes())
         return false;
index 2b43a93..c259cd1 100644 (file)
@@ -66,7 +66,7 @@ struct EntityDescription {
 // FIXME: Noncopyable?
 class MarkupAccumulator {
 public:
-    MarkupAccumulator(Vector<Node*>*, EAbsoluteURLs, const Range* = 0);
+    MarkupAccumulator(Vector<Node*>*, EAbsoluteURLs, const Range* = 0, EFragmentSerialization = HTMLFragmentSerialization);
     virtual ~MarkupAccumulator();
 
     String serializeNodes(Node* targetNode, Node* nodeToSkip, EChildrenOnly);
@@ -111,9 +111,11 @@ private:
     String resolveURLIfNeeded(const Element*, const String&) const;
     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; }
 
     StringBuilder m_markup;
     const EAbsoluteURLs m_resolveURLsMethod;
+    EFragmentSerialization m_fragmentSerialization;
 };
 
 }
index 2222a72..ba9eaa3 100644 (file)
@@ -755,7 +755,7 @@ PassRefPtr<DocumentFragment> createFragmentFromMarkupWithContext(Document* docum
     return fragment;
 }
 
-String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip)
+String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip, EFragmentSerialization fragmentSerialization)
 {
     if (!node)
         return "";
@@ -768,7 +768,7 @@ String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>*
             return "";
     }
 #endif
-    MarkupAccumulator accumulator(nodes, shouldResolveURLs);
+    MarkupAccumulator accumulator(nodes, shouldResolveURLs, 0, fragmentSerialization);
     return accumulator.serializeNodes(const_cast<Node*>(node), deleteButtonContainerElement, childrenOnly, tagNamesToSkip);
 }
 
index 6c0d2d7..2dbc715 100644 (file)
@@ -47,6 +47,7 @@ typedef int ExceptionCode;
 
 enum EChildrenOnly { IncludeNode, ChildrenOnly };
 enum EAbsoluteURLs { DoNotResolveURLs, ResolveAllURLs, ResolveNonLocalURLs };
+enum EFragmentSerialization { HTMLFragmentSerialization, XMLFragmentSerialization };
 
 PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text);
 PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String& markup, const String& baseURL, ParserContentPolicy = AllowScriptingContent);
@@ -64,7 +65,7 @@ void replaceChildrenWithFragment(ContainerNode*, PassRefPtr<DocumentFragment>, E
 void replaceChildrenWithText(ContainerNode*, const String&, ExceptionCode&);
 
 String createMarkup(const Range*, Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange, bool convertBlocksToInlines = false, EAbsoluteURLs = DoNotResolveURLs);
-String createMarkup(const Node*, EChildrenOnly = IncludeNode, Vector<Node*>* = 0, EAbsoluteURLs = DoNotResolveURLs, Vector<QualifiedName>* tagNamesToSkip = 0);
+String createMarkup(const Node*, EChildrenOnly = IncludeNode, Vector<Node*>* = 0, EAbsoluteURLs = DoNotResolveURLs, Vector<QualifiedName>* tagNamesToSkip = 0, EFragmentSerialization = HTMLFragmentSerialization);
 
 String createFullMarkup(const Node*);
 String createFullMarkup(const Range*);
index 8dde67e..dfc0f56 100644 (file)
@@ -42,7 +42,7 @@ String XMLSerializer::serializeToString(Node* node, ExceptionCode& ec)
         return String();
     }
 
-    return createMarkup(node);
+    return createMarkup(node, IncludeNode, 0, DoNotResolveURLs, 0, XMLFragmentSerialization);
 }
 
 } // namespace WebCore