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
2024-07-28 15:52:59 +12:00
auto const & html_element = 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 ) ;
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Break ;
2022-03-01 21:10:48 +00:00
}
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2022-03-01 21:10:48 +00:00
} ) ;
}
// 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 ) ;
}
}
2024-07-28 15:55:09 +12:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionstart
WebIDL : : UnsignedLong FormAssociatedElement : : selection_start ( ) const
{
2024-07-28 16:41:45 +12:00
// 1. If this element is an input element, and selectionStart does not apply to this element, return null.
// NOTE: This is done by HTMLInputElement before calling this function
2024-07-28 15:55:09 +12:00
// 2. If there is no selection, return the code unit offset within the relevant value to the character that
// immediately follows the text entry cursor.
2024-08-02 07:31:40 -04:00
if ( auto cursor = form_associated_element_to_html_element ( ) . document ( ) . cursor_position ( ) )
return cursor - > offset ( ) ;
2024-07-28 15:55:09 +12:00
// FIXME: 3. Return the code unit offset within the relevant value to the character that immediately follows the start of
// the selection.
return 0 ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionstart-2
2024-07-28 16:41:45 +12:00
WebIDL : : ExceptionOr < void > FormAssociatedElement : : set_selection_start ( Optional < WebIDL : : UnsignedLong > const & )
2024-07-28 15:55:09 +12:00
{
2024-07-28 16:41:45 +12:00
// 1. If this element is an input element, and selectionStart does not apply to this element, throw an
2024-07-28 15:55:09 +12:00
// "InvalidStateError" DOMException.
2024-07-28 16:41:45 +12:00
// NOTE: This is done by HTMLInputElement before calling this function
2024-07-28 15:55:09 +12:00
// FIXME: 2. Let end be the value of this element's selectionEnd attribute.
// FIXME: 3. If end is less than the given value, set end to the given value.
// FIXME: 4. Set the selection range with the given value, end, and the value of this element's selectionDirection attribute.
return { } ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea/input-selectionend
WebIDL : : UnsignedLong FormAssociatedElement : : selection_end ( ) const
{
2024-07-28 16:41:45 +12:00
// 1. If this element is an input element, and selectionEnd does not apply to this element, return null.
// NOTE: This is done by HTMLInputElement before calling this function
2024-07-28 15:55:09 +12:00
// 2. If there is no selection, return the code unit offset within the relevant value to the character that
// immediately follows the text entry cursor.
2024-08-02 07:31:40 -04:00
if ( auto cursor = form_associated_element_to_html_element ( ) . document ( ) . cursor_position ( ) )
return cursor - > offset ( ) ;
2024-07-28 15:55:09 +12:00
// FIXME: 3. Return the code unit offset within the relevant value to the character that immediately follows the end of
// the selection.
return 0 ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection:dom-textarea/input-selectionend-3
2024-07-28 16:41:45 +12:00
WebIDL : : ExceptionOr < void > FormAssociatedElement : : set_selection_end ( Optional < WebIDL : : UnsignedLong > const & )
2024-07-28 15:55:09 +12:00
{
2024-07-28 16:41:45 +12:00
// 1. If this element is an input element, and selectionEnd does not apply to this element, throw an
2024-07-28 15:55:09 +12:00
// "InvalidStateError" DOMException.
2024-07-28 16:41:45 +12:00
// NOTE: This is done by HTMLInputElement before calling this function
2024-07-28 15:55:09 +12:00
// FIXME: 2. Set the selection range with the value of this element's selectionStart attribute, the given value, and the
// value of this element's selectionDirection attribute.
return { } ;
}
2021-04-20 11:50:29 +02:00
}