Explorar o código

LibWeb: Reset form association when the element's form attribute changes

Timothy Flynn hai 1 ano
pai
achega
960dcf0e56

+ 7 - 1
Tests/LibWeb/Text/expected/HTML/Form-named-property-access.txt

@@ -1,4 +1,4 @@
-                                                          == Elements and Names ==
+                                                            == Elements and Names ==
 formy.length: 12
 elements.length: 12
 elements[0] === form.foo
@@ -32,3 +32,9 @@ Can we still use the same name?: true
 new hello is goodbye? false
 new hello is old hello? false
 new hello is newInput? true
+== Changing form attribute ==
+elements in changeForFormAttribute: 0
+elements in changeForFormAttribute: 1
+elements in changeForFormAttribute: 0
+elements in changeForFormAttribute: 1
+elements in changeForFormAttribute: 0

+ 21 - 0
Tests/LibWeb/Text/input/HTML/Form-named-property-access.html

@@ -60,6 +60,9 @@
     <input type="text" name="hello">
 </form>
 
+<form id="changeForFormAttribute"></form>
+<input id="changeForFormAttributeInput" type="text" name="changeForFormAttribute" />
+
 <script src="../include.js"></script>
 <script>
     test(() => {
@@ -117,5 +120,23 @@
         println(`new hello is goodbye? ${changy.hello === goodbye}`);
         println(`new hello is old hello? ${changy.hello === hello}`);
         println(`new hello is newInput? ${changy.hello === newInput}`);
+
+        println("== Changing form attribute ==");
+        let changeForFormAttribute = document.getElementById("changeForFormAttribute");
+        let changeForFormAttributeInput = document.getElementById("changeForFormAttributeInput");
+
+        println(`elements in changeForFormAttribute: ${changeForFormAttribute.elements.length}`);
+
+        changeForFormAttributeInput.setAttribute("form", "changeForFormAttribute");
+        println(`elements in changeForFormAttribute: ${changeForFormAttribute.elements.length}`);
+
+        changeForFormAttributeInput.setAttribute("form", "hakuna matata");
+        println(`elements in changeForFormAttribute: ${changeForFormAttribute.elements.length}`);
+
+        changeForFormAttributeInput.setAttribute("form", "changeForFormAttribute");
+        println(`elements in changeForFormAttribute: ${changeForFormAttribute.elements.length}`);
+
+        changeForFormAttributeInput.removeAttribute("form");
+        println(`elements in changeForFormAttribute: ${changeForFormAttribute.elements.length}`);
     });
 </script>

+ 10 - 1
Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp

@@ -72,13 +72,22 @@ void FormAssociatedElement::form_node_was_removed()
         reset_form_owner();
 }
 
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#association-of-controls-and-forms:category-listed-3
+void FormAssociatedElement::form_node_attribute_changed(FlyString const& name, Optional<String> const&)
+{
+    // When a listed form-associated element's form attribute is set, changed, or removed, then the user agent must
+    // reset the form owner of that element.
+    if (name == HTML::AttributeNames::form) {
+        reset_form_owner();
+    }
+}
+
 // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#reset-the-form-owner
 void FormAssociatedElement::reset_form_owner()
 {
     auto& html_element = form_associated_element_to_html_element();
 
     // Although these aren't in the "reset form owner" algorithm, these here as they are triggers for this algorithm:
-    // FIXME: When a listed form-associated element's form attribute is set, changed, or removed, then the user agent must reset the form owner of that element.
     // FIXME: When a listed form-associated element has a form attribute and the ID of any of the elements in the tree changes, then the user agent must reset the form owner of that form-associated element.
     // FIXME: When a listed form-associated element has a form attribute and an element with an ID is inserted into or removed from the Document, then the user agent must reset the form owner of that form-associated element.
 

+ 29 - 20
Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h

@@ -19,26 +19,33 @@ namespace Web::HTML {
 //    HTMLElement::inserted() -> Use form_associated_element_was_inserted()
 //    HTMLElement::removed_from() -> Use form_associated_element_was_removed()
 //
-#define FORM_ASSOCIATED_ELEMENT(ElementBaseClass, ElementClass)             \
-private:                                                                    \
-    virtual HTMLElement& form_associated_element_to_html_element() override \
-    {                                                                       \
-        static_assert(IsBaseOf<HTMLElement, ElementClass>);                 \
-        return *this;                                                       \
-    }                                                                       \
-                                                                            \
-    virtual void inserted() override                                        \
-    {                                                                       \
-        ElementBaseClass::inserted();                                       \
-        form_node_was_inserted();                                           \
-        form_associated_element_was_inserted();                             \
-    }                                                                       \
-                                                                            \
-    virtual void removed_from(DOM::Node* node) override                     \
-    {                                                                       \
-        ElementBaseClass::removed_from(node);                               \
-        form_node_was_removed();                                            \
-        form_associated_element_was_removed(node);                          \
+#define FORM_ASSOCIATED_ELEMENT(ElementBaseClass, ElementClass)                                   \
+private:                                                                                          \
+    virtual HTMLElement& form_associated_element_to_html_element() override                       \
+    {                                                                                             \
+        static_assert(IsBaseOf<HTMLElement, ElementClass>);                                       \
+        return *this;                                                                             \
+    }                                                                                             \
+                                                                                                  \
+    virtual void inserted() override                                                              \
+    {                                                                                             \
+        ElementBaseClass::inserted();                                                             \
+        form_node_was_inserted();                                                                 \
+        form_associated_element_was_inserted();                                                   \
+    }                                                                                             \
+                                                                                                  \
+    virtual void removed_from(DOM::Node* node) override                                           \
+    {                                                                                             \
+        ElementBaseClass::removed_from(node);                                                     \
+        form_node_was_removed();                                                                  \
+        form_associated_element_was_removed(node);                                                \
+    }                                                                                             \
+                                                                                                  \
+    virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override \
+    {                                                                                             \
+        ElementBaseClass::attribute_changed(name, value);                                         \
+        form_node_attribute_changed(name, value);                                                 \
+        form_associated_element_attribute_changed(name, value);                                   \
     }
 
 class FormAssociatedElement {
@@ -83,9 +90,11 @@ protected:
 
     virtual void form_associated_element_was_inserted() { }
     virtual void form_associated_element_was_removed(DOM::Node*) { }
+    virtual void form_associated_element_attribute_changed(FlyString const&, Optional<String> const&) { }
 
     void form_node_was_inserted();
     void form_node_was_removed();
+    void form_node_attribute_changed(FlyString const&, Optional<String> const&);
 
 private:
     WeakPtr<HTMLFormElement> m_form;

+ 1 - 3
Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp

@@ -97,10 +97,8 @@ void HTMLImageElement::apply_presentational_hints(CSS::StyleProperties& style) c
     });
 }
 
-void HTMLImageElement::attribute_changed(FlyString const& name, Optional<String> const& value)
+void HTMLImageElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value)
 {
-    HTMLElement::attribute_changed(name, value);
-
     if (name == HTML::AttributeNames::crossorigin) {
         m_cors_setting = cors_setting_attribute_from_keyword(value);
     }

+ 1 - 1
Userland/Libraries/LibWeb/HTML/HTMLImageElement.h

@@ -37,7 +37,7 @@ class HTMLImageElement final
 public:
     virtual ~HTMLImageElement() override;
 
-    virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
+    virtual void form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value) override;
 
     String alt() const { return get_attribute_value(HTML::AttributeNames::alt); }
     String src() const { return get_attribute_value(HTML::AttributeNames::src); }

+ 1 - 2
Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp

@@ -833,9 +833,8 @@ void HTMLInputElement::did_lose_focus()
     commit_pending_changes();
 }
 
-void HTMLInputElement::attribute_changed(FlyString const& name, Optional<String> const& value)
+void HTMLInputElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value)
 {
-    HTMLElement::attribute_changed(name, value);
     if (name == HTML::AttributeNames::checked) {
         if (!value.has_value()) {
             // When the checked content attribute is removed, if the control does not have dirty checkedness,

+ 1 - 3
Userland/Libraries/LibWeb/HTML/HTMLInputElement.h

@@ -126,9 +126,6 @@ public:
     // https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-input-element
     virtual bool is_focusable() const override { return m_type != TypeAttributeState::Hidden; }
 
-    // ^HTMLElement
-    virtual void attribute_changed(FlyString const&, Optional<String> const&) override;
-
     // ^FormAssociatedElement
     // https://html.spec.whatwg.org/multipage/forms.html#category-listed
     virtual bool is_listed() const override { return true; }
@@ -152,6 +149,7 @@ public:
 
     virtual void form_associated_element_was_inserted() override;
     virtual void form_associated_element_was_removed(DOM::Node*) override;
+    virtual void form_associated_element_attribute_changed(FlyString const&, Optional<String> const&) override;
 
     // ^HTMLElement
     // https://html.spec.whatwg.org/multipage/forms.html#category-label

+ 1 - 3
Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp

@@ -48,10 +48,8 @@ void HTMLObjectElement::visit_edges(Cell::Visitor& visitor)
     visitor.visit(m_image_request);
 }
 
-void HTMLObjectElement::attribute_changed(FlyString const& name, Optional<String> const& value)
+void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const&)
 {
-    NavigableContainer::attribute_changed(name, value);
-
     // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
     // Whenever one of the following conditions occur:
     if (

+ 1 - 1
Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h

@@ -35,7 +35,7 @@ class HTMLObjectElement final
 public:
     virtual ~HTMLObjectElement() override;
 
-    virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
+    virtual void form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value) override;
 
     String data() const;
     void set_data(String const& data) { MUST(set_attribute(HTML::AttributeNames::data, data)); }

+ 1 - 2
Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp

@@ -240,9 +240,8 @@ void HTMLTextAreaElement::children_changed()
     }
 }
 
-void HTMLTextAreaElement::attribute_changed(FlyString const& name, Optional<String> const& value)
+void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& value)
 {
-    HTMLElement::attribute_changed(name, value);
     if (name == HTML::AttributeNames::placeholder) {
         if (m_placeholder_text_node)
             m_placeholder_text_node->set_data(value.value_or(String {}));

+ 1 - 3
Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h

@@ -55,9 +55,6 @@ public:
     // https://html.spec.whatwg.org/multipage/forms.html#category-autocapitalize
     virtual bool is_auto_capitalize_inheriting() const override { return true; }
 
-    // ^HTMLElement
-    virtual void attribute_changed(FlyString const&, Optional<String> const&) override;
-
     // https://html.spec.whatwg.org/multipage/forms.html#category-label
     virtual bool is_labelable() const override { return true; }
 
@@ -65,6 +62,7 @@ public:
 
     virtual void form_associated_element_was_inserted() override;
     virtual void form_associated_element_was_removed(DOM::Node*) override;
+    virtual void form_associated_element_attribute_changed(FlyString const&, Optional<String> const&) override;
 
     virtual void children_changed() override;