From e2c71b67253fa90a59fdb93b4b2aecf1bed561e9 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 5 Feb 2022 18:21:49 +0100 Subject: [PATCH] LibWeb: Add a partial implementation of Element.setAttributeNS() This implementation does some of the required validation and then passes through the localName and value to Element.setAttribute(). --- Userland/Libraries/LibWeb/DOM/Element.cpp | 57 +++++++++++++++++++++++ Userland/Libraries/LibWeb/DOM/Element.h | 1 + Userland/Libraries/LibWeb/DOM/Element.idl | 1 + 3 files changed, 59 insertions(+) diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 0df7eb3fd0..f21f5f4661 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -97,6 +97,63 @@ ExceptionOr Element::set_attribute(const FlyString& name, const String& va return {}; } +// https://dom.spec.whatwg.org/#validate-and-extract +static ExceptionOr validate_and_extract(FlyString namespace_, FlyString qualified_name) +{ + // 1. If namespace is the empty string, then set it to null. + if (namespace_.is_empty()) + namespace_ = {}; + + // FIXME: 2. Validate qualifiedName. + + // 3. Let prefix be null. + FlyString prefix = {}; + + // 4. Let localName be qualifiedName. + auto local_name = qualified_name; + + // 5. If qualifiedName contains a U+003A (:), then strictly split the string on it and set prefix to the part before and localName to the part after. + if (qualified_name.view().contains(':')) { + auto parts = qualified_name.view().split_view(':'); + // FIXME: Handle parts > 2 + prefix = parts[0]; + local_name = parts[1]; + } + + // 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException. + if (!prefix.is_null() && namespace_.is_null()) + return NamespaceError::create("Prefix is non-null and namespace is null."); + + // 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException. + if (prefix == "xml"sv && namespace_ != Namespace::XML) + return NamespaceError::create("Prefix is 'xml' and namespace is not the XML namespace."); + + // 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException. + if ((qualified_name == "xmlns"sv || prefix == "xmlns"sv) && namespace_ != Namespace::XMLNS) + return NamespaceError::create("Either qualifiedName or prefix is 'xmlns' and namespace is not the XMLNS namespace."); + + // 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException. + if (namespace_ == Namespace::XMLNS && !(qualified_name == "xmlns"sv || prefix == "xmlns"sv)) + return NamespaceError::create("Namespace is the XMLNS namespace and neither qualifiedName nor prefix is 'xmlns'."); + + // 10. Return namespace, prefix, and localName. + return QualifiedName { namespace_, prefix, local_name }; +} + +// https://dom.spec.whatwg.org/#dom-element-setattributens +ExceptionOr Element::set_attribute_ns(FlyString const& namespace_, FlyString const& qualified_name, String const& value) +{ + // 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract. + auto result = validate_and_extract(namespace_, qualified_name); + if (result.is_exception()) + return result.exception(); + + // FIXME: 2. Set an attribute value for this using localName, value, and also prefix and namespace. + + // FIXME: Don't just call through to setAttribute() here. + return set_attribute(result.value().local_name(), value); +} + // https://dom.spec.whatwg.org/#dom-element-removeattribute void Element::remove_attribute(const FlyString& name) { diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 116df2c299..6c982b0b7e 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -54,6 +54,7 @@ public: String attribute(const FlyString& name) const { return get_attribute(name); } String get_attribute(const FlyString& name) const; ExceptionOr set_attribute(const FlyString& name, const String& value); + ExceptionOr set_attribute_ns(FlyString const& namespace_, FlyString const& qualified_name, String const& value); void remove_attribute(const FlyString& name); size_t attribute_list_size() const { return m_attributes->length(); } NonnullRefPtr const& attributes() const { return m_attributes; } diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl index 7fb2ea6381..e11a33dcd6 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.idl +++ b/Userland/Libraries/LibWeb/DOM/Element.idl @@ -6,6 +6,7 @@ interface Element : Node { DOMString? getAttribute(DOMString qualifiedName); undefined setAttribute(DOMString qualifiedName, DOMString value); + [CEReactions] undefined setAttributeNS(DOMString? namespace , DOMString qualifiedName , DOMString value); undefined removeAttribute(DOMString qualifiedName); boolean hasAttribute(DOMString qualifiedName); boolean hasAttributes();