Browse Source

LibXML: Set XMLNS namespace for xmlns attribute

This is what Blink and Gecko do, and is required for serialization
(innerHTML etc.) of XML elements to work.
Maciej 1 year ago
parent
commit
cad3b085a8

+ 2 - 0
Tests/LibWeb/Text/expected/DOM/DOMParser-xmlns.txt

@@ -0,0 +1,2 @@
+namespaceURI=http://www.w3.org/2000/xmlns/ prefix=null localName=xmlns value=http://www.w3.org/2000/svg
+namespaceURI=http://www.w3.org/2000/xmlns/ prefix=xmlns localName=test value=foo

+ 11 - 0
Tests/LibWeb/Text/input/DOM/DOMParser-xmlns.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        const svgstr = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:test="foo"></svg>`;
+        const svg = new DOMParser().parseFromString(svgstr, "image/svg+xml").documentElement;
+        for (const attr of svg.attributes) {
+            println(`namespaceURI=${attr.namespaceURI} prefix=${attr.prefix} localName=${attr.localName} value=${attr.value}`);
+        }
+    });
+</script>

+ 8 - 1
Userland/Libraries/LibWeb/XML/XMLDocumentBuilder.cpp

@@ -86,8 +86,15 @@ void XMLDocumentBuilder::element_start(const XML::Name& name, HashMap<XML::Name,
         MUST(m_current_node->append_child(node));
     }
 
-    for (auto const& attribute : attributes)
+    for (auto const& attribute : attributes) {
+        // https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-decl
+        if (attribute.key == "xmlns" || attribute.key.starts_with("xmlns:"sv)) {
+            auto name = attribute.key;
+            // The prefix xmlns is used only to declare namespace bindings and is by definition bound to the namespace name http://www.w3.org/2000/xmlns/.
+            MUST(node->set_attribute_ns(Namespace::XMLNS, MUST(FlyString::from_deprecated_fly_string(name)), MUST(String::from_byte_string(attribute.value))));
+        }
         MUST(node->set_attribute(MUST(FlyString::from_deprecated_fly_string(attribute.key)), MUST(String::from_byte_string(attribute.value))));
+    }
 
     m_current_node = node.ptr();
 }