From c62b70d88fabbe744b7e056eeb9017920a8b1b26 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 16 Oct 2021 16:16:57 -0400 Subject: [PATCH] LibWeb: Reimplement Element attribute related methods with NamedNodeMap --- Userland/Libraries/LibWeb/DOM/Element.cpp | 60 +++++++++++++---------- Userland/Libraries/LibWeb/DOM/Element.h | 20 ++++---- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 5bfdb5084f..58c476de80 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -38,6 +38,7 @@ namespace Web::DOM { Element::Element(Document& document, QualifiedName qualified_name) : ParentNode(document, NodeType::ELEMENT_NODE) , m_qualified_name(move(qualified_name)) + , m_attributes(NamedNodeMap::create(*this)) { make_html_uppercased_qualified_name(); } @@ -46,53 +47,60 @@ Element::~Element() { } -Attribute* Element::find_attribute(const FlyString& name) +// https://dom.spec.whatwg.org/#dom-element-getattribute +String Element::get_attribute(const FlyString& name) const { - for (auto& attribute : m_attributes) { - if (attribute->name() == name) - return attribute; - } - return nullptr; -} - -const Attribute* Element::find_attribute(const FlyString& name) const -{ - for (auto& attribute : m_attributes) { - if (attribute->name() == name) - return attribute; - } - return nullptr; -} - -String Element::attribute(const FlyString& name) const -{ - if (auto* attribute = find_attribute(name)) - return attribute->value(); - return {}; + // 1. Let attr be the result of getting an attribute given qualifiedName and this. + auto const* attribute = m_attributes->get_attribute(name); + + // 2. If attr is null, return null. + if (!attribute) + return {}; + + // 3. Return attr’s value. + return attribute->value(); } +// https://dom.spec.whatwg.org/#dom-element-setattribute ExceptionOr Element::set_attribute(const FlyString& name, const String& value) { + // 1. If qualifiedName does not match the Name production in XML, then throw an "InvalidCharacterError" DOMException. // FIXME: Proper name validation if (name.is_empty()) return InvalidCharacterError::create("Attribute name must not be empty"); CSS::StyleInvalidator style_invalidator(document()); - if (auto* attribute = find_attribute(name)) + // 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase. + // 3. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise. + auto* attribute = m_attributes->get_attribute(name); + + // 4. If attribute is null, create an attribute whose local name is qualifiedName, value is value, and node document is this’s node document, then append this attribute to this, and then return. + if (!attribute) { + auto new_attribute = Attribute::create(document(), name, value); + m_attributes->append_attribute(new_attribute); + } + + // 5. Change attribute to value. + else { attribute->set_value(value); - else - m_attributes.append(Attribute::create(document(), name, value, this)); + } parse_attribute(name, value); return {}; } +// https://dom.spec.whatwg.org/#dom-element-removeattribute void Element::remove_attribute(const FlyString& name) { CSS::StyleInvalidator style_invalidator(document()); + m_attributes->remove_attribute(name); +} - m_attributes.remove_first_matching([&](auto& attribute) { return attribute->name() == name; }); +// https://dom.spec.whatwg.org/#dom-element-hasattribute +bool Element::has_attribute(const FlyString& name) const +{ + return m_attributes->get_attribute(name) != nullptr; } bool Element::has_class(const FlyString& class_name, CaseSensitivity case_sensitivity) const diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index cc829ff53d..6c2bc5b838 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -48,13 +49,13 @@ public: // NOTE: This is for the JS bindings const FlyString& namespace_uri() const { return namespace_(); } - bool has_attribute(const FlyString& name) const { return find_attribute(name) != nullptr; } - bool has_attributes() const { return !m_attributes.is_empty(); } - String attribute(const FlyString& name) const; - String get_attribute(const FlyString& name) const { return attribute(name); } + bool has_attribute(const FlyString& name) const; + bool has_attributes() const { return !m_attributes->is_empty(); } + 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); void remove_attribute(const FlyString& name); - size_t attribute_list_size() const { return m_attributes.size(); } + size_t attribute_list_size() const { return m_attributes->length(); } DOM::ExceptionOr matches(StringView selectors) const; @@ -66,8 +67,10 @@ public: template void for_each_attribute(Callback callback) const { - for (auto& attribute : m_attributes) + for (size_t i = 0; i < m_attributes->length(); ++i) { + auto const* attribute = m_attributes->item(i); callback(attribute->name(), attribute->value()); + } } bool has_class(const FlyString&, CaseSensitivity = CaseSensitivity::CaseSensitive) const; @@ -125,14 +128,11 @@ protected: virtual void children_changed() override; private: - Attribute* find_attribute(const FlyString& name); - const Attribute* find_attribute(const FlyString& name) const; - void make_html_uppercased_qualified_name(); QualifiedName m_qualified_name; String m_html_uppercased_qualified_name; - Vector> m_attributes; + NonnullRefPtr m_attributes; RefPtr m_inline_style;