Jelajahi Sumber

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

Timothy Flynn 1 tahun lalu
induk
melakukan
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
 formy.length: 12
 elements.length: 12
 elements.length: 12
 elements[0] === form.foo
 elements[0] === form.foo
@@ -32,3 +32,9 @@ Can we still use the same name?: true
 new hello is goodbye? false
 new hello is goodbye? false
 new hello is old hello? false
 new hello is old hello? false
 new hello is newInput? true
 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">
     <input type="text" name="hello">
 </form>
 </form>
 
 
+<form id="changeForFormAttribute"></form>
+<input id="changeForFormAttributeInput" type="text" name="changeForFormAttribute" />
+
 <script src="../include.js"></script>
 <script src="../include.js"></script>
 <script>
 <script>
     test(() => {
     test(() => {
@@ -117,5 +120,23 @@
         println(`new hello is goodbye? ${changy.hello === goodbye}`);
         println(`new hello is goodbye? ${changy.hello === goodbye}`);
         println(`new hello is old hello? ${changy.hello === hello}`);
         println(`new hello is old hello? ${changy.hello === hello}`);
         println(`new hello is newInput? ${changy.hello === newInput}`);
         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>
 </script>

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

@@ -72,13 +72,22 @@ void FormAssociatedElement::form_node_was_removed()
         reset_form_owner();
         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
 // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#reset-the-form-owner
 void FormAssociatedElement::reset_form_owner()
 void FormAssociatedElement::reset_form_owner()
 {
 {
     auto& html_element = form_associated_element_to_html_element();
     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:
     // 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 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.
     // 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::inserted() -> Use form_associated_element_was_inserted()
 //    HTMLElement::removed_from() -> Use form_associated_element_was_removed()
 //    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 {
 class FormAssociatedElement {
@@ -83,9 +90,11 @@ protected:
 
 
     virtual void form_associated_element_was_inserted() { }
     virtual void form_associated_element_was_inserted() { }
     virtual void form_associated_element_was_removed(DOM::Node*) { }
     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_inserted();
     void form_node_was_removed();
     void form_node_was_removed();
+    void form_node_attribute_changed(FlyString const&, Optional<String> const&);
 
 
 private:
 private:
     WeakPtr<HTMLFormElement> m_form;
     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) {
     if (name == HTML::AttributeNames::crossorigin) {
         m_cors_setting = cors_setting_attribute_from_keyword(value);
         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:
 public:
     virtual ~HTMLImageElement() override;
     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 alt() const { return get_attribute_value(HTML::AttributeNames::alt); }
     String src() const { return get_attribute_value(HTML::AttributeNames::src); }
     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();
     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 (name == HTML::AttributeNames::checked) {
         if (!value.has_value()) {
         if (!value.has_value()) {
             // When the checked content attribute is removed, if the control does not have dirty checkedness,
             // 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
     // 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; }
     virtual bool is_focusable() const override { return m_type != TypeAttributeState::Hidden; }
 
 
-    // ^HTMLElement
-    virtual void attribute_changed(FlyString const&, Optional<String> const&) override;
-
     // ^FormAssociatedElement
     // ^FormAssociatedElement
     // https://html.spec.whatwg.org/multipage/forms.html#category-listed
     // https://html.spec.whatwg.org/multipage/forms.html#category-listed
     virtual bool is_listed() const override { return true; }
     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_inserted() override;
     virtual void form_associated_element_was_removed(DOM::Node*) 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
     // ^HTMLElement
     // https://html.spec.whatwg.org/multipage/forms.html#category-label
     // 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);
     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
     // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
     // Whenever one of the following conditions occur:
     // Whenever one of the following conditions occur:
     if (
     if (

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

@@ -35,7 +35,7 @@ class HTMLObjectElement final
 public:
 public:
     virtual ~HTMLObjectElement() override;
     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;
     String data() const;
     void set_data(String const& data) { MUST(set_attribute(HTML::AttributeNames::data, data)); }
     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 (name == HTML::AttributeNames::placeholder) {
         if (m_placeholder_text_node)
         if (m_placeholder_text_node)
             m_placeholder_text_node->set_data(value.value_or(String {}));
             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
     // https://html.spec.whatwg.org/multipage/forms.html#category-autocapitalize
     virtual bool is_auto_capitalize_inheriting() const override { return true; }
     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
     // https://html.spec.whatwg.org/multipage/forms.html#category-label
     virtual bool is_labelable() const override { return true; }
     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_inserted() override;
     virtual void form_associated_element_was_removed(DOM::Node*) 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;
     virtual void children_changed() override;