1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 05:48:12 +00:00

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

This commit is contained in:
Timothy Flynn 2024-02-03 09:33:33 -05:00 committed by Andrew Kaster
parent 23fb1209af
commit 960dcf0e56
12 changed files with 75 additions and 40 deletions

View file

@ -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

View file

@ -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>

View file

@ -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.

View file

@ -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) \ #define FORM_ASSOCIATED_ELEMENT(ElementBaseClass, ElementClass) \
private: \ private: \
virtual HTMLElement& form_associated_element_to_html_element() override \ virtual HTMLElement& form_associated_element_to_html_element() override \
{ \ { \
static_assert(IsBaseOf<HTMLElement, ElementClass>); \ static_assert(IsBaseOf<HTMLElement, ElementClass>); \
return *this; \ return *this; \
} \ } \
\ \
virtual void inserted() override \ virtual void inserted() override \
{ \ { \
ElementBaseClass::inserted(); \ ElementBaseClass::inserted(); \
form_node_was_inserted(); \ form_node_was_inserted(); \
form_associated_element_was_inserted(); \ form_associated_element_was_inserted(); \
} \ } \
\ \
virtual void removed_from(DOM::Node* node) override \ virtual void removed_from(DOM::Node* node) override \
{ \ { \
ElementBaseClass::removed_from(node); \ ElementBaseClass::removed_from(node); \
form_node_was_removed(); \ form_node_was_removed(); \
form_associated_element_was_removed(node); \ 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;

View file

@ -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);
} }

View file

@ -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); }

View file

@ -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,

View file

@ -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

View file

@ -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 (

View file

@ -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)); }

View file

@ -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 {}));

View file

@ -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;