1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 09:04:59 +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
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

View file

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

View file

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

View file

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

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) {
m_cors_setting = cors_setting_attribute_from_keyword(value);
}

View file

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

View file

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

View file

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

View file

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

View file

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

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 (m_placeholder_text_node)
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
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;