From 960dcf0e56b7aa27fefc00932c5bf43a0a130e60 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 3 Feb 2024 09:33:33 -0500 Subject: [PATCH] LibWeb: Reset form association when the element's form attribute changes --- .../HTML/Form-named-property-access.txt | 8 ++- .../HTML/Form-named-property-access.html | 21 ++++++++ .../LibWeb/HTML/FormAssociatedElement.cpp | 11 ++++- .../LibWeb/HTML/FormAssociatedElement.h | 49 +++++++++++-------- .../LibWeb/HTML/HTMLImageElement.cpp | 4 +- .../Libraries/LibWeb/HTML/HTMLImageElement.h | 2 +- .../LibWeb/HTML/HTMLInputElement.cpp | 3 +- .../Libraries/LibWeb/HTML/HTMLInputElement.h | 4 +- .../LibWeb/HTML/HTMLObjectElement.cpp | 4 +- .../Libraries/LibWeb/HTML/HTMLObjectElement.h | 2 +- .../LibWeb/HTML/HTMLTextAreaElement.cpp | 3 +- .../LibWeb/HTML/HTMLTextAreaElement.h | 4 +- 12 files changed, 75 insertions(+), 40 deletions(-) diff --git a/Tests/LibWeb/Text/expected/HTML/Form-named-property-access.txt b/Tests/LibWeb/Text/expected/HTML/Form-named-property-access.txt index 1344a3d714..dee9ba8cbd 100644 --- a/Tests/LibWeb/Text/expected/HTML/Form-named-property-access.txt +++ b/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 diff --git a/Tests/LibWeb/Text/input/HTML/Form-named-property-access.html b/Tests/LibWeb/Text/input/HTML/Form-named-property-access.html index 17abba5682..8b492768e7 100644 --- a/Tests/LibWeb/Text/input/HTML/Form-named-property-access.html +++ b/Tests/LibWeb/Text/input/HTML/Form-named-property-access.html @@ -60,6 +60,9 @@ +
+ + diff --git a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp index c1b4bfd0df..333d424830 100644 --- a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp +++ b/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 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. diff --git a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h index b15d052033..90a69f7610 100644 --- a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.h +++ b/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); \ - 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); \ + 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 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 const&) { } void form_node_was_inserted(); void form_node_was_removed(); + void form_node_attribute_changed(FlyString const&, Optional const&); private: WeakPtr m_form; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp index 78fb99253b..9f5018bc3f 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp +++ b/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 const& value) +void HTMLImageElement::form_associated_element_attribute_changed(FlyString const& name, Optional const& value) { - HTMLElement::attribute_changed(name, value); - if (name == HTML::AttributeNames::crossorigin) { m_cors_setting = cors_setting_attribute_from_keyword(value); } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h index 364096f368..5a2821fbff 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h +++ b/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 const& value) override; + virtual void form_associated_element_attribute_changed(FlyString const& name, Optional const& value) override; String alt() const { return get_attribute_value(HTML::AttributeNames::alt); } String src() const { return get_attribute_value(HTML::AttributeNames::src); } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 38e2d38667..b421f77b33 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/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 const& value) +void HTMLInputElement::form_associated_element_attribute_changed(FlyString const& name, Optional 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, diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index 5f4004addc..236a3b68d4 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/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 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 const&) override; // ^HTMLElement // https://html.spec.whatwg.org/multipage/forms.html#category-label diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp index 22197ccd8d..3aa48c5184 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp +++ b/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 const& value) +void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional 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 ( diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h index 88118a0cf5..4849df3e34 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h +++ b/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 const& value) override; + virtual void form_associated_element_attribute_changed(FlyString const& name, Optional const& value) override; String data() const; void set_data(String const& data) { MUST(set_attribute(HTML::AttributeNames::data, data)); } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index b5e613e7e7..b41eb4990e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -240,9 +240,8 @@ void HTMLTextAreaElement::children_changed() } } -void HTMLTextAreaElement::attribute_changed(FlyString const& name, Optional const& value) +void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString const& name, Optional 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 {})); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h index 6d38ff9ef7..df311814b2 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h +++ b/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 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 const&) override; virtual void children_changed() override;