Переглянути джерело

LibWeb: Implement all "attributes" mutation records for MutationObserver

Luke Wilde 3 роки тому
батько
коміт
a718c62c01

+ 36 - 0
Userland/Libraries/LibWeb/DOM/Attribute.cpp

@@ -7,6 +7,8 @@
 #include <LibWeb/DOM/Attribute.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/MutationType.h>
+#include <LibWeb/DOM/StaticNodeList.h>
 
 namespace Web::DOM {
 
@@ -23,6 +25,11 @@ Attribute::Attribute(Document& document, FlyString local_name, String value, Ele
 {
 }
 
+Element* Attribute::owner_element()
+{
+    return m_owner_element;
+}
+
 Element const* Attribute::owner_element() const
 {
     return m_owner_element;
@@ -33,4 +40,33 @@ void Attribute::set_owner_element(Element const* owner_element)
     m_owner_element = owner_element;
 }
 
+// https://dom.spec.whatwg.org/#set-an-existing-attribute-value
+void Attribute::set_value(String value)
+{
+    // 1. If attribute’s element is null, then set attribute’s value to value.
+    if (!owner_element()) {
+        m_value = move(value);
+        return;
+    }
+
+    // 2. Otherwise, change attribute to value.
+    // https://dom.spec.whatwg.org/#concept-element-attributes-change
+    // 1. Handle attribute changes for attribute with attribute’s element, attribute’s value, and value.
+    handle_attribute_changes(*owner_element(), m_value, value);
+
+    // 2. Set attribute’s value to value.
+    m_value = move(value);
+}
+
+// https://dom.spec.whatwg.org/#handle-attribute-changes
+void Attribute::handle_attribute_changes(Element& element, String const& old_value, [[maybe_unused]] String const& new_value)
+{
+    // 1. Queue a mutation record of "attributes" for element with attribute’s local name, attribute’s namespace, oldValue, « », « », null, and null.
+    element.queue_mutation_record(MutationType::attributes, local_name(), namespace_uri(), old_value, StaticNodeList::create({}), StaticNodeList::create({}), nullptr, nullptr);
+
+    // FIXME: 2. If element is custom, then enqueue a custom element callback reaction with element, callback name "attributeChangedCallback", and an argument list containing attribute’s local name, oldValue, newValue, and attribute’s namespace.
+
+    // FIXME: 3. Run the attribute change steps with element, attribute’s local name, oldValue, newValue, and attribute’s namespace.
+}
+
 }

+ 4 - 1
Userland/Libraries/LibWeb/DOM/Attribute.h

@@ -30,14 +30,17 @@ public:
     String const& name() const { return m_qualified_name.as_string(); }
 
     String const& value() const { return m_value; }
-    void set_value(String value) { m_value = move(value); }
+    void set_value(String value);
 
+    Element* owner_element();
     Element const* owner_element() const;
     void set_owner_element(Element const* owner_element);
 
     // Always returns true: https://dom.spec.whatwg.org/#dom-attr-specified
     constexpr bool specified() const { return true; }
 
+    void handle_attribute_changes(Element&, String const& old_value, String const& new_value);
+
 private:
     Attribute(Document&, FlyString local_name, String value, Element const*);
 

+ 20 - 5
Userland/Libraries/LibWeb/DOM/NamedNodeMap.cpp

@@ -154,8 +154,8 @@ ExceptionOr<Attribute const*> NamedNodeMap::set_attribute(Attribute& attribute)
 void NamedNodeMap::replace_attribute(Attribute& old_attribute, Attribute& new_attribute, size_t old_attribute_index)
 {
     // 1. Handle attribute changes for oldAttr with oldAttr’s element, oldAttr’s value, and newAttr’s value.
-    // FIXME: The steps to handle an attribute change deal with mutation records and custom element states.
-    //        Once those are supported, implement these steps: https://dom.spec.whatwg.org/#handle-attribute-changes
+    VERIFY(old_attribute.owner_element());
+    old_attribute.handle_attribute_changes(*old_attribute.owner_element(), old_attribute.value(), new_attribute.value());
 
     // 2. Replace oldAttr by newAttr in oldAttr’s element’s attribute list.
     m_attributes.remove(old_attribute_index);
@@ -172,8 +172,7 @@ void NamedNodeMap::replace_attribute(Attribute& old_attribute, Attribute& new_at
 void NamedNodeMap::append_attribute(Attribute& attribute)
 {
     // 1. Handle attribute changes for attribute with element, null, and attribute’s value.
-    // FIXME: The steps to handle an attribute change deal with mutation records and custom element states.
-    //        Once those are supported, implement these steps: https://dom.spec.whatwg.org/#handle-attribute-changes
+    attribute.handle_attribute_changes(associated_element(), {}, attribute.value());
 
     // 2. Append attribute to element’s attribute list.
     m_attributes.append(attribute);
@@ -182,6 +181,22 @@ void NamedNodeMap::append_attribute(Attribute& attribute)
     attribute.set_owner_element(&associated_element());
 }
 
+// https://dom.spec.whatwg.org/#concept-element-attributes-remove
+void NamedNodeMap::remove_attribute_at_index(size_t attribute_index)
+{
+    NonnullRefPtr<Attribute> attribute = m_attributes.at(attribute_index);
+
+    // 1. Handle attribute changes for attribute with attribute’s element, attribute’s value, and null.
+    VERIFY(attribute->owner_element());
+    attribute->handle_attribute_changes(*attribute->owner_element(), attribute->value(), {});
+
+    // 2. Remove attribute from attribute’s element’s attribute list.
+    m_attributes.remove(attribute_index);
+
+    // 3. Set attribute’s element to null.
+    attribute->set_owner_element(nullptr);
+}
+
 // https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-name
 Attribute const* NamedNodeMap::remove_attribute(StringView qualified_name)
 {
@@ -192,7 +207,7 @@ Attribute const* NamedNodeMap::remove_attribute(StringView qualified_name)
 
     // 2. If attr is non-null, then remove attr.
     if (attribute)
-        m_attributes.remove(item_index);
+        remove_attribute_at_index(item_index);
 
     // 3. Return attr.
     return attribute;

+ 2 - 0
Userland/Libraries/LibWeb/DOM/NamedNodeMap.h

@@ -55,6 +55,8 @@ private:
     Element& associated_element() { return ref_count_target(); }
     Element const& associated_element() const { return ref_count_target(); }
 
+    void remove_attribute_at_index(size_t attribute_index);
+
     NonnullRefPtrVector<Attribute> m_attributes;
 };