LibWeb/XML: Treat declared namespaces as valid for attributes

Previously if we encountered any attributes with a namespace other than
`xml` or `xmlns`, we treated it as a parse error. Now, allow it as long
as it's been declared in the current context.

We also handle errors more gracefully - instead of exploding if setting
the namespace fails, treat it as an error and carry on.
This commit is contained in:
Sam Atkins 2025-04-11 15:37:52 +01:00
commit 336ccdb5f1
Notes: github-actions[bot] 2025-04-15 08:40:13 +00:00
3 changed files with 36 additions and 11 deletions

View file

@ -152,21 +152,27 @@ void XMLDocumentBuilder::element_start(const XML::Name& name, HashMap<XML::Name,
}
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;
if (!name.is_one_of("xmlns:"sv, "xmlns:xmlns"sv)) {
// 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(String::from_byte_string(name)), MUST(String::from_byte_string(attribute.value))));
} else {
m_has_error = true;
// 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/.
if (!attribute.key.is_one_of("xmlns:"sv, "xmlns:xmlns"sv)) {
if (!node->set_attribute_ns(Namespace::XMLNS, MUST(String::from_byte_string(attribute.key)), MUST(String::from_byte_string(attribute.value))).is_error())
continue;
}
} else if (attribute.key.contains(":"sv)) {
if (!attribute.key.starts_with("xml:"sv)) {
m_has_error = true;
m_has_error = true;
} else if (attribute.key.contains(':')) {
if (auto ns = namespace_for_name(attribute.key); ns.has_value()) {
if (!node->set_attribute_ns(ns.value(), MUST(String::from_byte_string(attribute.key)), MUST(String::from_byte_string(attribute.value))).is_error())
continue;
} else if (attribute.key.starts_with("xml:"sv)) {
if (auto maybe_error = node->set_attribute_ns(Namespace::XML, MUST(String::from_byte_string(attribute.key)), MUST(String::from_byte_string(attribute.value))); !maybe_error.is_error())
continue;
}
m_has_error = true;
} else {
if (!node->set_attribute(MUST(String::from_byte_string(attribute.key)), MUST(String::from_byte_string(attribute.value))).is_error())
continue;
m_has_error = true;
}
MUST(node->set_attribute(MUST(String::from_byte_string(attribute.key)), MUST(String::from_byte_string(attribute.value))));
}
m_current_node = node.ptr();

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg:svg
width="200"
height="200"
xmlns:svg="http://www.w3.org/2000/svg"
>
<svg:circle cx="100" cy="100" r="50" fill="#ff0000"/>
</svg:svg>

After

Width:  |  Height:  |  Size: 212 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg:svg
width="200"
height="200"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:test="test"
>
<link rel="match" href="../../expected/svg/custom-namespaced-attributes-ref.svg" />
<svg:circle cx="100" cy="100" r="50" fill="#ff0000"/>
</svg:svg>

After

Width:  |  Height:  |  Size: 391 B