2021-04-20 11:50:29 +02:00
/*
* Copyright ( c ) 2021 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-04-20 11:50:29 +02:00
*/
2024-02-03 10:29:26 -05:00
# include <LibWeb/DOM/Document.h>
2021-04-20 11:50:29 +02:00
# include <LibWeb/HTML/FormAssociatedElement.h>
2022-03-01 20:59:46 +00:00
# include <LibWeb/HTML/HTMLButtonElement.h>
# include <LibWeb/HTML/HTMLFieldSetElement.h>
2021-04-20 11:50:29 +02:00
# include <LibWeb/HTML/HTMLFormElement.h>
2022-03-01 20:59:46 +00:00
# include <LibWeb/HTML/HTMLInputElement.h>
# include <LibWeb/HTML/HTMLLegendElement.h>
# include <LibWeb/HTML/HTMLSelectElement.h>
# include <LibWeb/HTML/HTMLTextAreaElement.h>
2022-03-23 18:55:54 -04:00
# include <LibWeb/HTML/Parser/HTMLParser.h>
2021-04-20 11:50:29 +02:00
namespace Web : : HTML {
void FormAssociatedElement : : set_form ( HTMLFormElement * form )
{
2021-04-20 23:34:49 +02:00
if ( m_form )
2022-03-23 18:55:54 -04:00
m_form - > remove_associated_element ( { } , form_associated_element_to_html_element ( ) ) ;
2021-04-20 11:50:29 +02:00
m_form = form ;
2021-04-20 23:34:49 +02:00
if ( m_form )
2022-03-23 18:55:54 -04:00
m_form - > add_associated_element ( { } , form_associated_element_to_html_element ( ) ) ;
2021-04-20 11:50:29 +02:00
}
2022-03-01 20:59:46 +00:00
bool FormAssociatedElement : : enabled ( ) const
{
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled
2022-03-23 18:55:54 -04:00
auto const & html_element = const_cast < FormAssociatedElement & > ( * this ) . form_associated_element_to_html_element ( ) ;
2022-03-01 20:59:46 +00:00
// A form control is disabled if any of the following conditions are met:
// 1. The element is a button, input, select, textarea, or form-associated custom element, and the disabled attribute is specified on this element (regardless of its value).
// FIXME: This doesn't check for form-associated custom elements.
2022-03-23 18:55:54 -04:00
if ( ( is < HTMLButtonElement > ( html_element ) | | is < HTMLInputElement > ( html_element ) | | is < HTMLSelectElement > ( html_element ) | | is < HTMLTextAreaElement > ( html_element ) ) & & html_element . has_attribute ( HTML : : AttributeNames : : disabled ) )
2022-03-01 20:59:46 +00:00
return false ;
// 2. The element is a descendant of a fieldset element whose disabled attribute is specified, and is not a descendant of that fieldset element's first legend element child, if any.
2022-09-30 16:19:01 +01:00
for ( auto * fieldset_ancestor = html_element . first_ancestor_of_type < HTMLFieldSetElement > ( ) ; fieldset_ancestor ; fieldset_ancestor = fieldset_ancestor - > first_ancestor_of_type < HTMLFieldSetElement > ( ) ) {
if ( fieldset_ancestor - > has_attribute ( HTML : : AttributeNames : : disabled ) ) {
auto * first_legend_element_child = fieldset_ancestor - > first_child_of_type < HTMLLegendElement > ( ) ;
if ( ! first_legend_element_child | | ! html_element . is_descendant_of ( * first_legend_element_child ) )
return false ;
}
2022-03-01 20:59:46 +00:00
}
return true ;
}
2022-03-23 18:55:54 -04:00
void FormAssociatedElement : : set_parser_inserted ( Badge < HTMLParser > )
2022-03-01 21:10:48 +00:00
{
2022-03-23 18:55:54 -04:00
m_parser_inserted = true ;
}
2022-03-01 21:10:48 +00:00
2022-03-23 18:55:54 -04:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#association-of-controls-and-forms:nodes-are-inserted
void FormAssociatedElement : : form_node_was_inserted ( )
{
2022-03-01 21:10:48 +00:00
// 1. If the form-associated element's parser inserted flag is set, then return.
if ( m_parser_inserted )
return ;
// 2. Reset the form owner of the form-associated element.
reset_form_owner ( ) ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#association-of-controls-and-forms:nodes-are-removed
2022-03-23 18:55:54 -04:00
void FormAssociatedElement : : form_node_was_removed ( )
2022-03-01 21:10:48 +00:00
{
// 1. If the form-associated element has a form owner and the form-associated element and its form owner are no longer in the same tree, then reset the form owner of the form-associated element.
2022-03-23 18:55:54 -04:00
if ( m_form & & & form_associated_element_to_html_element ( ) . root ( ) ! = & m_form - > root ( ) )
2022-03-01 21:10:48 +00:00
reset_form_owner ( ) ;
}
2024-02-03 09:33:33 -05:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#association-of-controls-and-forms:category-listed-3
2024-02-03 10:29:26 -05:00
void FormAssociatedElement : : form_node_attribute_changed ( FlyString const & name , Optional < String > const & value )
2024-02-03 09:33:33 -05:00
{
// 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 ) {
2024-02-03 10:29:26 -05:00
auto & html_element = form_associated_element_to_html_element ( ) ;
if ( value . has_value ( ) )
html_element . document ( ) . add_form_associated_element_with_form_attribute ( * this ) ;
else
html_element . document ( ) . remove_form_associated_element_with_form_attribute ( * this ) ;
2024-02-03 09:33:33 -05:00
reset_form_owner ( ) ;
}
}
2024-02-03 10:29:26 -05:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#association-of-controls-and-forms:category-listed-4
void FormAssociatedElement : : element_id_changed ( Badge < DOM : : Document > )
{
// 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.
VERIFY ( form_associated_element_to_html_element ( ) . has_attribute ( HTML : : AttributeNames : : form ) ) ;
reset_form_owner ( ) ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#association-of-controls-and-forms:category-listed-5
void FormAssociatedElement : : element_with_id_was_added_or_removed ( Badge < DOM : : Document > )
{
// 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.
VERIFY ( form_associated_element_to_html_element ( ) . has_attribute ( HTML : : AttributeNames : : form ) ) ;
reset_form_owner ( ) ;
}
2022-03-01 21:10:48 +00:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#reset-the-form-owner
void FormAssociatedElement : : reset_form_owner ( )
{
2022-03-23 18:55:54 -04:00
auto & html_element = form_associated_element_to_html_element ( ) ;
2022-03-01 21:10:48 +00:00
// 1. Unset element's parser inserted flag.
m_parser_inserted = false ;
// 2. If all of the following conditions are true
// - element's form owner is not null
// - element is not listed or its form content attribute is not present
// - element's form owner is its nearest form element ancestor after the change to the ancestor chain
// then do nothing, and return.
if ( m_form
2022-03-23 18:55:54 -04:00
& & ( ! is_listed ( ) | | ! html_element . has_attribute ( HTML : : AttributeNames : : form ) )
& & html_element . first_ancestor_of_type < HTMLFormElement > ( ) = = m_form . ptr ( ) ) {
2022-03-01 21:10:48 +00:00
return ;
}
// 3. Set element's form owner to null.
set_form ( nullptr ) ;
// 4. If element is listed, has a form content attribute, and is connected, then:
2022-03-23 18:55:54 -04:00
if ( is_listed ( ) & & html_element . has_attribute ( HTML : : AttributeNames : : form ) & & html_element . is_connected ( ) ) {
2022-03-01 21:10:48 +00:00
// 1. If the first element in element's tree, in tree order, to have an ID that is identical to element's form content attribute's value, is a form element, then associate the element with that form element.
2023-10-13 22:50:34 +03:30
auto form_value = html_element . attribute ( HTML : : AttributeNames : : form ) ;
2022-11-19 01:09:53 +00:00
html_element . root ( ) . for_each_in_inclusive_subtree_of_type < HTMLFormElement > ( [ this , & form_value ] ( HTMLFormElement & form_element ) {
2024-01-13 20:12:25 +13:00
if ( form_element . id ( ) = = form_value ) {
2022-03-01 21:10:48 +00:00
set_form ( & form_element ) ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
}
// 5. Otherwise, if element has an ancestor form element, then associate element with the nearest such ancestor form element.
else {
2022-03-23 18:55:54 -04:00
auto * form_ancestor = html_element . first_ancestor_of_type < HTMLFormElement > ( ) ;
2022-03-01 21:10:48 +00:00
if ( form_ancestor )
set_form ( form_ancestor ) ;
}
}
2021-04-20 11:50:29 +02:00
}