2020-01-18 09:38:21 +01:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < andreas @ ladybird . org >
2023-02-03 21:50:14 +01:00
* Copyright ( c ) 2023 , Kenneth Myhra < kennethmyhra @ serenityos . org >
2023-06-18 16:30:23 +01:00
* Copyright ( c ) 2023 , Luke Wilde < lukew @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2024-01-10 10:45:28 -07:00
# include <AK/QuickSort.h>
2019-11-25 21:21:55 +01:00
# include <AK/StringBuilder.h>
2023-06-18 16:30:23 +01:00
# include <LibTextCodec/Decoder.h>
# include <LibWeb/Bindings/ExceptionOrUtils.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/HTMLFormElementPrototype.h>
2024-05-16 06:02:56 +01:00
# include <LibWeb/DOM/DOMTokenList.h>
2021-08-24 16:28:08 +02:00
# include <LibWeb/DOM/Document.h>
2022-12-22 19:32:20 -05:00
# include <LibWeb/DOM/Event.h>
2024-02-11 19:48:56 +13:00
# include <LibWeb/DOMURL/DOMURL.h>
2021-11-18 15:01:28 +01:00
# include <LibWeb/HTML/BrowsingContext.h>
2020-11-21 21:53:18 +00:00
# include <LibWeb/HTML/EventNames.h>
2025-05-03 19:52:01 +02:00
# include <LibWeb/HTML/Focus.h>
2023-06-18 16:30:23 +01:00
# include <LibWeb/HTML/FormControlInfrastructure.h>
2022-02-25 21:19:06 +01:00
# include <LibWeb/HTML/HTMLButtonElement.h>
2024-02-13 22:37:16 +00:00
# include <LibWeb/HTML/HTMLDialogElement.h>
2022-02-25 21:19:06 +01:00
# include <LibWeb/HTML/HTMLFieldSetElement.h>
2024-11-02 09:22:13 -04:00
# include <LibWeb/HTML/HTMLFormControlsCollection.h>
2020-07-26 15:08:16 +02:00
# include <LibWeb/HTML/HTMLFormElement.h>
2024-01-10 10:45:28 -07:00
# include <LibWeb/HTML/HTMLImageElement.h>
2020-07-26 15:08:16 +02:00
# include <LibWeb/HTML/HTMLInputElement.h>
2022-02-25 21:19:06 +01:00
# include <LibWeb/HTML/HTMLObjectElement.h>
# include <LibWeb/HTML/HTMLOutputElement.h>
# include <LibWeb/HTML/HTMLSelectElement.h>
# include <LibWeb/HTML/HTMLTextAreaElement.h>
2024-11-02 09:22:13 -04:00
# include <LibWeb/HTML/RadioNodeList.h>
2020-11-21 21:53:18 +00:00
# include <LibWeb/HTML/SubmitEvent.h>
2023-06-18 16:30:23 +01:00
# include <LibWeb/Infra/CharacterTypes.h>
# include <LibWeb/Infra/Strings.h>
2021-08-24 16:28:08 +02:00
# include <LibWeb/Page/Page.h>
2019-11-25 21:21:55 +01:00
2020-07-28 18:20:36 +02:00
namespace Web : : HTML {
2020-03-07 10:27:02 +01:00
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( HTMLFormElement ) ;
2023-11-19 19:47:52 +01:00
2022-02-18 21:00:52 +01:00
HTMLFormElement : : HTMLFormElement ( DOM : : Document & document , DOM : : QualifiedName qualified_name )
2021-02-07 11:20:15 +01:00
: HTMLElement ( document , move ( qualified_name ) )
2019-11-25 21:21:55 +01:00
{
2024-01-10 10:45:28 -07:00
m_legacy_platform_object_flags = LegacyPlatformObjectFlags {
. supports_indexed_properties = true ,
. supports_named_properties = true ,
. has_legacy_unenumerable_named_properties_interface_extended_attribute = true ,
. has_legacy_override_built_ins_interface_extended_attribute = true ,
} ;
2019-11-25 21:21:55 +01:00
}
2022-03-14 13:21:51 -06:00
HTMLFormElement : : ~ HTMLFormElement ( ) = default ;
2019-11-25 21:21:55 +01:00
2023-08-07 08:41:28 +02:00
void HTMLFormElement : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( HTMLFormElement ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
}
2022-08-28 13:42:07 +02:00
void HTMLFormElement : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2022-11-24 19:04:05 +01:00
visitor . visit ( m_elements ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( m_associated_elements ) ;
2024-04-05 11:09:35 -07:00
visitor . visit ( m_planned_navigation ) ;
2024-05-17 14:06:27 -07:00
visitor . visit ( m_rel_list ) ;
2022-08-28 13:42:07 +02:00
}
2024-01-31 13:26:01 -05:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission
WebIDL : : ExceptionOr < void > HTMLFormElement : : implicitly_submit_form ( )
{
// If the user agent supports letting the user submit a form implicitly (for example, on some platforms hitting the
// "enter" key while a text control is focused implicitly submits the form), then doing so for a form, whose default
// button has activation behavior and is not disabled, must cause the user agent to fire a click event at that
// default button.
if ( auto * default_button = this - > default_button ( ) ) {
auto & default_button_element = default_button - > form_associated_element_to_html_element ( ) ;
if ( default_button_element . has_activation_behavior ( ) & & default_button - > enabled ( ) )
default_button_element . click ( ) ;
return { } ;
}
// If the form has no submit button, then the implicit submission mechanism must perform the following steps:
// 1. If the form has more than one field that blocks implicit submission, then return.
if ( number_of_fields_blocking_implicit_submission ( ) > 1 )
return { } ;
// 2. Submit the form element from the form element itself with userInvolvement set to "activation".
TRY ( submit_form ( * this , { . user_involvement = UserNavigationInvolvement : : Activation } ) ) ;
return { } ;
}
2023-06-18 16:30:23 +01:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-submit
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < void > HTMLFormElement : : submit_form ( GC : : Ref < HTMLElement > submitter , SubmitFormOptions options )
2019-11-25 21:21:55 +01:00
{
2023-06-18 16:30:23 +01:00
auto & vm = this - > vm ( ) ;
auto & realm = this - > realm ( ) ;
// 1. If form cannot navigate, then return.
2020-11-21 21:53:18 +00:00
if ( cannot_navigate ( ) )
2023-03-01 20:10:01 +01:00
return { } ;
2020-11-21 21:53:18 +00:00
2023-06-18 16:30:23 +01:00
// 2. If form's constructing entry list is true, then return.
if ( m_constructing_entry_list )
2023-03-01 20:10:01 +01:00
return { } ;
2019-11-25 21:21:55 +01:00
2023-06-18 16:30:23 +01:00
// 3. Let form document be form's node document.
2024-11-15 04:01:23 +13:00
GC : : Ref < DOM : : Document > form_document = this - > document ( ) ;
2020-11-07 15:57:37 +00:00
2023-06-18 16:30:23 +01:00
// 4. If form document's active sandboxing flag set has its sandboxed forms browsing context flag set, then return.
2023-08-28 11:57:21 +02:00
if ( has_flag ( form_document - > active_sandboxing_flag_set ( ) , HTML : : SandboxingFlagSet : : SandboxedForms ) )
2023-06-18 16:30:23 +01:00
return { } ;
2019-11-25 21:21:55 +01:00
2023-06-18 16:30:23 +01:00
// 5. If the submitted from submit() method flag is not set, then:
2024-01-18 12:58:22 -07:00
if ( ! options . from_submit_binding ) {
2023-06-18 16:30:23 +01:00
// 1. If form's firing submission events is true, then return.
2020-11-21 21:53:18 +00:00
if ( m_firing_submission_events )
2023-03-01 20:10:01 +01:00
return { } ;
2020-11-21 21:53:18 +00:00
2023-06-18 16:30:23 +01:00
// 2. Set form's firing submission events to true.
2020-11-21 21:53:18 +00:00
m_firing_submission_events = true ;
2025-02-02 20:41:23 +01:00
// 3. For each element field in the list of submittable elements whose form owner is form, set field's user validity to true.
for ( auto & element : get_submittable_elements ( ) ) {
// NOTE: Only input, select and textarea elements have a user validity flag.
// See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#user-validity
if ( is < HTMLInputElement > ( * element ) ) {
( & as < HTMLInputElement > ( * element ) ) - > set_user_validity ( false ) ;
} else if ( is < HTMLSelectElement > ( * element ) ) {
( & as < HTMLSelectElement > ( * element ) ) - > set_user_validity ( false ) ;
} else if ( is < HTMLTextAreaElement > ( * element ) ) {
( & as < HTMLTextAreaElement > ( * element ) ) - > set_user_validity ( false ) ;
}
}
// FIXME: 4. If the submitter element's no-validate state is false, then interactively validate the constraints
2023-06-18 16:30:23 +01:00
// of form and examine the result. If the result is negative (i.e., the constraint validation concluded
// that there were invalid fields and probably informed the user of this), then:
// 1. Set form's firing submission events to false.
// 2. Return.
2020-11-21 21:53:18 +00:00
2025-02-02 20:41:23 +01:00
// 5. Let submitterButton be null if submitter is form. Otherwise, let submitterButton be submitter.
2024-11-15 04:01:23 +13:00
GC : : Ptr < HTMLElement > submitter_button ;
2020-11-21 21:53:18 +00:00
if ( submitter ! = this )
submitter_button = submitter ;
2025-02-02 20:41:23 +01:00
// 6. Let shouldContinue be the result of firing an event named submit at form using SubmitEvent, with the
2023-06-18 16:30:23 +01:00
// submitter attribute initialized to submitterButton, the bubbles attribute initialized to true, and the
// cancelable attribute initialized to true.
2021-10-01 18:46:37 +03:00
SubmitEventInit event_init { } ;
event_init . submitter = submitter_button ;
2023-08-13 13:05:26 +02:00
auto submit_event = SubmitEvent : : create ( realm , EventNames : : submit , event_init ) ;
2020-11-21 21:53:18 +00:00
submit_event - > set_bubbles ( true ) ;
submit_event - > set_cancelable ( true ) ;
2023-06-18 16:30:23 +01:00
bool should_continue = dispatch_event ( * submit_event ) ;
2020-11-21 21:53:18 +00:00
2025-02-02 20:41:23 +01:00
// 7. Set form's firing submission events to false.
2020-11-21 21:53:18 +00:00
m_firing_submission_events = false ;
2025-02-02 20:41:23 +01:00
// 8. If shouldContinue is false, then return.
2023-06-18 16:30:23 +01:00
if ( ! should_continue )
2023-03-01 20:10:01 +01:00
return { } ;
2020-11-21 21:53:18 +00:00
2025-02-02 20:41:23 +01:00
// 9. If form cannot navigate, then return.
2023-06-18 16:30:23 +01:00
// Spec Note: Cannot navigate is run again as dispatching the submit event could have changed the outcome.
2020-11-21 21:53:18 +00:00
if ( cannot_navigate ( ) )
2023-03-01 20:10:01 +01:00
return { } ;
2020-11-21 21:53:18 +00:00
}
2023-06-18 16:30:23 +01:00
// 6. Let encoding be the result of picking an encoding for the form.
auto encoding = TRY_OR_THROW_OOM ( vm , pick_an_encoding ( ) ) ;
2020-11-07 15:57:37 +00:00
2023-06-18 16:30:23 +01:00
// 7. Let entry list be the result of constructing the entry list with form, submitter, and encoding.
auto entry_list_or_null = TRY ( construct_entry_list ( realm , * this , submitter , encoding ) ) ;
// 8. Assert: entry list is not null.
VERIFY ( entry_list_or_null . has_value ( ) ) ;
auto entry_list = entry_list_or_null . release_value ( ) ;
// 9. If form cannot navigate, then return.
// Spec Note: Cannot navigate is run again as dispatching the formdata event in constructing the entry list could
// have changed the outcome.
if ( cannot_navigate ( ) )
return { } ;
// 10. Let method be the submitter element's method.
auto method = method_state_from_form_element ( submitter ) ;
// 11. If method is dialog, then:
if ( method = = MethodAttributeState : : Dialog ) {
2024-02-13 22:37:16 +00:00
// 1. If form does not have an ancestor dialog element, then return.
// 2. Let subject be form's nearest ancestor dialog element.
auto * subject = first_ancestor_of_type < HTMLDialogElement > ( ) ;
if ( ! subject )
return { } ;
// 3. Let result be null.
Optional < String > result ;
2024-02-19 00:21:27 -05:00
// 4. If submitter is an input element whose type attribute is in the Image Button state, then:
if ( is < HTMLInputElement > ( * submitter ) ) {
auto const & input_element = static_cast < HTMLInputElement const & > ( * submitter ) ;
if ( input_element . type_state ( ) = = HTMLInputElement : : TypeAttributeState : : ImageButton ) {
// 1. Let (x, y) be the selected coordinate.
auto [ x , y ] = input_element . selected_coordinate ( ) ;
// 2. Set result to the concatenation of x, ",", and y.
result = MUST ( String : : formatted ( " {},{} " , x , y ) ) ;
}
}
2023-06-18 16:30:23 +01:00
2024-02-13 22:37:16 +00:00
// 5. Otherwise, if submitter has a value, then set result to that value.
2024-02-19 00:21:27 -05:00
if ( ! result . has_value ( ) )
result = submitter - > get_attribute_value ( AttributeNames : : value ) ;
2024-02-13 22:37:16 +00:00
// 6. Close the dialog subject with result.
subject - > close ( move ( result ) ) ;
// 7. Return.
2023-03-01 20:10:01 +01:00
return { } ;
2020-11-07 15:57:37 +00:00
}
2023-06-18 16:30:23 +01:00
// 12. Let action be the submitter element's action.
auto action = action_from_form_element ( submitter ) ;
2019-11-25 21:21:55 +01:00
2023-06-18 16:30:23 +01:00
// 13. If action is the empty string, let action be the URL of the form document.
if ( action . is_empty ( ) )
2023-12-24 15:51:37 +13:00
action = form_document - > url_string ( ) ;
2019-11-25 21:21:55 +01:00
2025-01-29 16:54:25 +00:00
// 14. Let parsed action be the result of encoding-parsing a URL given action, relative to submitter's node document.
auto parsed_action = submitter - > document ( ) . encoding_parse_url ( action ) ;
// 15. If parsed action is failure, then return.
2025-01-23 19:40:57 +13:00
if ( ! parsed_action . has_value ( ) ) {
2023-06-18 16:30:23 +01:00
dbgln ( " Failed to submit form: Invalid URL: {} " , action ) ;
return { } ;
2020-09-28 11:56:26 +02:00
}
2019-11-25 21:21:55 +01:00
2023-06-18 16:30:23 +01:00
// 16. Let scheme be the scheme of parsed action.
2025-01-23 19:40:57 +13:00
auto const & scheme = parsed_action - > scheme ( ) ;
2023-06-18 16:30:23 +01:00
// 17. Let enctype be the submitter element's enctype.
auto encoding_type = encoding_type_state_from_form_element ( submitter ) ;
2025-01-29 16:54:25 +00:00
// 18. Let formTarget be null.
Optional < String > form_target ;
// 19. If the submitter element is a submit button and it has a formtarget attribute, then set formTarget to the
// formtarget attribute value.
if ( auto * form_associated_element = as_if < FormAssociatedElement > ( * submitter ) ; form_associated_element & & form_associated_element - > is_submit_button ( ) ) {
if ( auto formtarget_attribute = submitter - > attribute ( AttributeNames : : formtarget ) ; formtarget_attribute . has_value ( ) ) {
form_target = formtarget_attribute . release_value ( ) ;
}
}
// 20. Let target be the result of getting an element's target given submitter's form owner and formTarget.
auto target = get_an_elements_target ( form_target ) ;
2023-06-18 16:30:23 +01:00
2025-01-29 16:54:25 +00:00
// 21. Let noopener be the result of getting an element's noopener with form, parsed action, and target.
2025-01-29 16:56:55 +00:00
auto no_opener = get_an_elements_noopener ( * parsed_action , target ) ;
2020-09-28 11:56:26 +02:00
2025-01-29 16:54:25 +00:00
// 22. Let targetNavigable be the first return value of applying the rules for choosing a navigable given target,
// form's node navigable, and noopener.
2023-06-23 00:32:10 +03:00
auto target_navigable = form_document - > navigable ( ) - > choose_a_navigable ( target , no_opener ) . navigable ;
2023-06-18 16:30:23 +01:00
2025-01-29 16:54:25 +00:00
// 23. If targetNavigable is null, then return.
2023-06-18 16:30:23 +01:00
if ( ! target_navigable ) {
dbgln ( " Failed to submit form: choose_a_browsing_context returning a null browsing context " ) ;
return { } ;
2020-09-28 11:56:26 +02:00
}
2025-01-29 16:54:25 +00:00
// 24. Let historyHandling be "auto".
2024-01-18 12:58:22 -07:00
auto history_handling = Bindings : : NavigationHistoryBehavior : : Auto ;
2023-06-18 16:30:23 +01:00
2025-01-29 16:54:25 +00:00
// 25. If form document equals targetNavigable's active document, and form document has not yet completely loaded,
// then set historyHandling to "replace".
if ( form_document = = target_navigable - > active_document ( ) & & ! form_document - > is_completely_loaded ( ) )
2024-01-18 12:58:22 -07:00
history_handling = Bindings : : NavigationHistoryBehavior : : Replace ;
2023-06-18 16:30:23 +01:00
2025-01-29 16:54:25 +00:00
// 25. Select the appropriate row in the table below based on scheme as given by the first cell of each row.
2023-06-18 16:30:23 +01:00
// Then, select the appropriate cell on that row based on method as given in the first cell of each column.
// Then, jump to the steps named in that cell and defined below the table.
2024-01-31 13:24:49 -05:00
// | GET | POST
2023-06-18 16:30:23 +01:00
// ------------------------------------------------------
2024-01-31 13:24:49 -05:00
// http | Mutate action URL | Submit as entity body
// https | Mutate action URL | Submit as entity body
// ftp | Get action URL | Get action URL
// javascript | Get action URL | Get action URL
// data | Mutate action URL | Get action URL
// mailto | Mail with headers | Mail as body
2023-06-18 16:30:23 +01:00
// If scheme is not one of those listed in this table, then the behavior is not defined by this specification.
// User agents should, in the absence of another specification defining this, act in a manner analogous to that defined
// in this specification for similar schemes.
2024-02-18 21:58:04 -05:00
// AD-HOC: In accordance with the above paragraph, we implement file:// submission URLs the same as data: URLs.
2023-06-18 16:30:23 +01:00
// This should have been handled above.
VERIFY ( method ! = MethodAttributeState : : Dialog ) ;
if ( scheme . is_one_of ( " http " sv , " https " sv ) ) {
if ( method = = MethodAttributeState : : GET )
2025-01-23 19:40:57 +13:00
TRY_OR_THROW_OOM ( vm , mutate_action_url ( parsed_action . release_value ( ) , move ( entry_list ) , move ( encoding ) , * target_navigable , history_handling , options . user_involvement ) ) ;
2023-06-18 16:30:23 +01:00
else
2025-01-23 19:40:57 +13:00
TRY_OR_THROW_OOM ( vm , submit_as_entity_body ( parsed_action . release_value ( ) , move ( entry_list ) , encoding_type , move ( encoding ) , * target_navigable , history_handling , options . user_involvement ) ) ;
2023-06-18 16:30:23 +01:00
} else if ( scheme . is_one_of ( " ftp " sv , " javascript " sv ) ) {
2024-11-25 17:10:33 +00:00
get_action_url ( parsed_action . release_value ( ) , move ( entry_list ) , * target_navigable , history_handling , options . user_involvement ) ;
2024-02-18 21:58:04 -05:00
} else if ( scheme . is_one_of ( " data " sv , " file " sv ) ) {
2023-06-18 16:30:23 +01:00
if ( method = = MethodAttributeState : : GET )
2025-01-23 19:40:57 +13:00
TRY_OR_THROW_OOM ( vm , mutate_action_url ( parsed_action . release_value ( ) , move ( entry_list ) , move ( encoding ) , * target_navigable , history_handling , options . user_involvement ) ) ;
2023-06-18 16:30:23 +01:00
else
2024-11-25 17:10:33 +00:00
get_action_url ( parsed_action . release_value ( ) , move ( entry_list ) , * target_navigable , history_handling , options . user_involvement ) ;
2023-06-18 16:30:23 +01:00
} else if ( scheme = = " mailto " sv ) {
if ( method = = MethodAttributeState : : GET )
2025-01-23 19:40:57 +13:00
TRY_OR_THROW_OOM ( vm , mail_with_headers ( parsed_action . release_value ( ) , move ( entry_list ) , move ( encoding ) , * target_navigable , history_handling , options . user_involvement ) ) ;
2023-06-18 16:30:23 +01:00
else
2025-01-23 19:40:57 +13:00
TRY_OR_THROW_OOM ( vm , mail_as_body ( parsed_action . release_value ( ) , move ( entry_list ) , encoding_type , move ( encoding ) , * target_navigable , history_handling , options . user_involvement ) ) ;
2023-06-18 16:30:23 +01:00
} else {
dbgln ( " Failed to submit form: Unknown scheme: {} " , scheme ) ;
return { } ;
}
2023-03-01 20:10:01 +01:00
return { } ;
2019-11-25 21:21:55 +01:00
}
2020-03-07 10:27:02 +01:00
2022-12-22 19:32:20 -05:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#resetting-a-form
void HTMLFormElement : : reset_form ( )
{
// 1. Let reset be the result of firing an event named reset at form, with the bubbles and cancelable attributes initialized to true.
2023-08-13 13:05:26 +02:00
auto reset_event = DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : reset ) ;
2022-12-22 19:32:20 -05:00
reset_event - > set_bubbles ( true ) ;
reset_event - > set_cancelable ( true ) ;
2023-02-14 22:43:17 +01:00
bool reset = dispatch_event ( reset_event ) ;
2022-12-22 19:32:20 -05:00
// 2. If reset is true, then invoke the reset algorithm of each resettable element whose form owner is form.
if ( reset ) {
2025-01-21 13:48:16 +01:00
GC : : RootVector < GC : : Ref < HTMLElement > > associated_elements_copy ( heap ( ) , m_associated_elements ) ;
for ( auto element : associated_elements_copy ) {
2022-12-22 19:32:20 -05:00
VERIFY ( is < FormAssociatedElement > ( * element ) ) ;
auto & form_associated_element = dynamic_cast < FormAssociatedElement & > ( * element ) ;
if ( form_associated_element . is_resettable ( ) )
form_associated_element . reset_algorithm ( ) ;
}
}
}
2023-03-01 20:10:01 +01:00
WebIDL : : ExceptionOr < void > HTMLFormElement : : submit ( )
2020-11-21 21:53:18 +00:00
{
2024-01-18 12:58:22 -07:00
return submit_form ( * this , { . from_submit_binding = true } ) ;
2020-11-21 21:53:18 +00:00
}
2024-02-15 18:29:53 +00:00
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < void > HTMLFormElement : : request_submit ( GC : : Ptr < Element > submitter )
2024-02-15 18:29:53 +00:00
{
// 1. If submitter is not null, then:
if ( submitter ) {
// 1. If submitter is not a submit button, then throw a TypeError.
auto * form_associated_element = dynamic_cast < FormAssociatedElement * > ( submitter . ptr ( ) ) ;
if ( ! ( form_associated_element & & form_associated_element - > is_submit_button ( ) ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " The submitter is not a submit button " sv } ;
// 2. If submitter's form owner is not this form element, then throw a "NotFoundError" DOMException.
if ( form_associated_element - > form ( ) ! = this )
2024-10-12 20:56:21 +02:00
return WebIDL : : NotFoundError : : create ( realm ( ) , " The submitter is not owned by this form element " _string ) ;
2024-02-15 18:29:53 +00:00
}
// 2. Otherwise, set submitter to this form element.
else {
submitter = this ;
}
// 3. Submit this form element, from submitter.
return submit_form ( static_cast < HTMLElement & > ( * submitter ) , { } ) ;
}
2022-12-22 19:32:20 -05:00
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-reset
void HTMLFormElement : : reset ( )
{
// 1. If the form element is marked as locked for reset, then return.
if ( m_locked_for_reset )
return ;
// 2. Mark the form element as locked for reset.
m_locked_for_reset = true ;
// 3. Reset the form element.
reset_form ( ) ;
// 4. Unmark the form element as locked for reset.
m_locked_for_reset = false ;
}
2021-04-20 23:34:49 +02:00
void HTMLFormElement : : add_associated_element ( Badge < FormAssociatedElement > , HTMLElement & element )
{
m_associated_elements . append ( element ) ;
}
void HTMLFormElement : : remove_associated_element ( Badge < FormAssociatedElement > , HTMLElement & element )
{
m_associated_elements . remove_first_matching ( [ & ] ( auto & entry ) { return entry . ptr ( ) = = & element ; } ) ;
2024-01-10 10:45:28 -07:00
// If an element listed in a form element's past names map changes form owner, then its entries must be removed from that map.
m_past_names_map . remove_all_matching ( [ & ] ( auto & , auto const & entry ) { return entry . node = = & element ; } ) ;
2021-04-20 23:34:49 +02:00
}
2023-06-18 16:30:23 +01:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fs-action
2024-11-15 04:01:23 +13:00
String HTMLFormElement : : action_from_form_element ( GC : : Ref < HTMLElement > element ) const
2022-02-18 19:46:10 -05:00
{
2023-06-18 16:30:23 +01:00
// The action of an element is the value of the element's formaction attribute, if the element is a submit button
// and has such an attribute, or the value of its form owner's action attribute, if it has one, or else the empty
// string.
if ( auto const * form_associated_element = dynamic_cast < FormAssociatedElement const * > ( element . ptr ( ) ) ;
2023-12-24 15:51:37 +13:00
form_associated_element & & form_associated_element - > is_submit_button ( ) ) {
if ( auto maybe_attribute = element - > attribute ( AttributeNames : : formaction ) ; maybe_attribute . has_value ( ) )
return maybe_attribute . release_value ( ) ;
}
2023-06-18 16:30:23 +01:00
2023-12-24 15:51:37 +13:00
if ( auto maybe_attribute = attribute ( AttributeNames : : action ) ; maybe_attribute . has_value ( ) )
return maybe_attribute . release_value ( ) ;
2023-06-18 16:30:23 +01:00
2023-12-24 15:51:37 +13:00
return String { } ;
2023-06-18 16:30:23 +01:00
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-attributes:attr-fs-method-2
static HTMLFormElement : : MethodAttributeState method_attribute_to_method_state ( StringView method )
{
# define __ENUMERATE_FORM_METHOD_ATTRIBUTE(keyword, state) \
if ( Infra : : is_ascii_case_insensitive_match ( # keyword # # sv , method ) ) \
return HTMLFormElement : : MethodAttributeState : : state ;
ENUMERATE_FORM_METHOD_ATTRIBUTES
# undef __ENUMERATE_FORM_METHOD_ATTRIBUTE
// The method attribute's invalid value default and missing value default are both the GET state.
return HTMLFormElement : : MethodAttributeState : : GET ;
}
2022-02-18 19:46:10 -05:00
2023-06-18 16:30:23 +01:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fs-method
2024-11-15 04:01:23 +13:00
HTMLFormElement : : MethodAttributeState HTMLFormElement : : method_state_from_form_element ( GC : : Ref < HTMLElement const > element ) const
2023-06-18 16:30:23 +01:00
{
// If the element is a submit button and has a formmethod attribute, then the element's method is that attribute's state;
// otherwise, it is the form owner's method attribute's state.
if ( auto const * form_associated_element = dynamic_cast < FormAssociatedElement const * > ( element . ptr ( ) ) ;
2024-01-12 09:25:01 +01:00
form_associated_element & & form_associated_element - > is_submit_button ( ) ) {
if ( auto maybe_formmethod = element - > attribute ( AttributeNames : : formmethod ) ; maybe_formmethod . has_value ( ) ) {
// NOTE: `formmethod` is the same as `method`, except that it has no missing value default.
// This is handled by not calling `method_attribute_to_method_state` in the first place if there is no `formmethod` attribute.
return method_attribute_to_method_state ( maybe_formmethod . value ( ) ) ;
}
2023-06-18 16:30:23 +01:00
}
2024-01-12 09:25:01 +01:00
if ( auto maybe_method = attribute ( AttributeNames : : method ) ; maybe_method . has_value ( ) ) {
return method_attribute_to_method_state ( maybe_method . value ( ) ) ;
}
2023-06-18 16:30:23 +01:00
2024-01-12 09:25:01 +01:00
return MethodAttributeState : : GET ;
2023-06-18 16:30:23 +01:00
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-attributes:attr-fs-enctype-2
static HTMLFormElement : : EncodingTypeAttributeState encoding_type_attribute_to_encoding_type_state ( StringView encoding_type )
{
# define __ENUMERATE_FORM_METHOD_ENCODING_TYPE(keyword, state) \
if ( Infra : : is_ascii_case_insensitive_match ( keyword # # sv , encoding_type ) ) \
return HTMLFormElement : : EncodingTypeAttributeState : : state ;
ENUMERATE_FORM_METHOD_ENCODING_TYPES
# undef __ENUMERATE_FORM_METHOD_ENCODING_TYPE
// The enctype attribute's invalid value default and missing value default are both the application/x-www-form-urlencoded state.
return HTMLFormElement : : EncodingTypeAttributeState : : FormUrlEncoded ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fs-enctype
2024-11-15 04:01:23 +13:00
HTMLFormElement : : EncodingTypeAttributeState HTMLFormElement : : encoding_type_state_from_form_element ( GC : : Ref < HTMLElement > element ) const
2023-06-18 16:30:23 +01:00
{
// If the element is a submit button and has a formenctype attribute, then the element's enctype is that attribute's state;
// otherwise, it is the form owner's enctype attribute's state.
if ( auto const * form_associated_element = dynamic_cast < FormAssociatedElement const * > ( element . ptr ( ) ) ;
2024-01-12 09:25:01 +01:00
form_associated_element & & form_associated_element - > is_submit_button ( ) ) {
if ( auto formenctype = element - > attribute ( AttributeNames : : formenctype ) ; formenctype . has_value ( ) ) {
// NOTE: `formenctype` is the same as `enctype`, except that it has nomissing value default.
// This is handled by not calling `encoding_type_attribute_to_encoding_type_state` in the first place if there is no
// `formenctype` attribute.
return encoding_type_attribute_to_encoding_type_state ( formenctype . value ( ) ) ;
}
2022-02-18 19:46:10 -05:00
}
2024-01-12 09:25:01 +01:00
if ( auto maybe_enctype = attribute ( AttributeNames : : enctype ) ; maybe_enctype . has_value ( ) )
return encoding_type_attribute_to_encoding_type_state ( maybe_enctype . value ( ) ) ;
2023-06-18 16:30:23 +01:00
2024-01-12 09:25:01 +01:00
return EncodingTypeAttributeState : : FormUrlEncoded ;
2022-02-18 19:46:10 -05:00
}
2024-01-10 10:45:28 -07:00
// https://html.spec.whatwg.org/multipage/forms.html#category-listed
static bool is_listed_element ( DOM : : Element const & element )
2022-02-25 21:19:06 +01:00
{
2024-01-10 10:45:28 -07:00
// Denotes elements that are listed in the form.elements and fieldset.elements APIs.
// These elements also have a form content attribute, and a matching form IDL attribute,
// that allow authors to specify an explicit form owner.
// => button, fieldset, input, object, output, select, textarea, form-associated custom elements
2022-02-25 21:19:06 +01:00
if ( is < HTMLButtonElement > ( element )
| | is < HTMLFieldSetElement > ( element )
2024-01-10 10:45:28 -07:00
| | is < HTMLInputElement > ( element )
2022-02-25 21:19:06 +01:00
| | is < HTMLObjectElement > ( element )
| | is < HTMLOutputElement > ( element )
| | is < HTMLSelectElement > ( element )
| | is < HTMLTextAreaElement > ( element ) ) {
return true ;
}
2024-01-10 10:45:28 -07:00
// FIXME: Form-associated custom elements return also true
return false ;
}
static bool is_form_control ( DOM : : Element const & element , HTMLFormElement const & form )
{
// The elements IDL attribute must return an HTMLFormControlsCollection rooted at the form element's root,
// whose filter matches listed elements whose form owner is the form element,
// with the exception of input elements whose type attribute is in the Image Button state, which must,
// for historical reasons, be excluded from this particular collection.
if ( ! is_listed_element ( element ) )
return false ;
2022-02-25 21:19:06 +01:00
if ( is < HTMLInputElement > ( element )
2024-01-10 10:45:28 -07:00
& & static_cast < HTMLInputElement const & > ( element ) . type_state ( ) = = HTMLInputElement : : TypeAttributeState : : ImageButton ) {
return false ;
2022-02-25 21:19:06 +01:00
}
2024-01-10 10:45:28 -07:00
auto const & form_associated_element = dynamic_cast < FormAssociatedElement const & > ( element ) ;
if ( form_associated_element . form ( ) ! = & form )
return false ;
2023-12-10 10:42:41 +01:00
2024-01-10 10:45:28 -07:00
return true ;
2022-02-25 21:19:06 +01:00
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-elements
2024-11-15 04:01:23 +13:00
GC : : Ref < HTMLFormControlsCollection > HTMLFormElement : : elements ( ) const
2022-02-25 21:19:06 +01:00
{
2022-11-24 19:04:05 +01:00
if ( ! m_elements ) {
2025-01-21 09:12:05 -05:00
auto & root = as < ParentNode > ( const_cast < HTMLFormElement * > ( this ) - > root ( ) ) ;
2024-11-02 09:22:13 -04:00
m_elements = HTMLFormControlsCollection : : create ( root , DOM : : HTMLCollection : : Scope : : Descendants , [ this ] ( Element const & element ) {
2024-01-10 10:45:28 -07:00
return is_form_control ( element , * this ) ;
2023-08-13 13:05:26 +02:00
} ) ;
2022-11-24 19:04:05 +01:00
}
return * m_elements ;
2022-02-25 21:19:06 +01:00
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-length
unsigned HTMLFormElement : : length ( ) const
{
// The length IDL attribute must return the number of nodes represented by the elements collection.
return elements ( ) - > length ( ) ;
}
2025-02-22 22:14:44 +09:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints
HTMLFormElement : : StaticValidationResult HTMLFormElement : : statically_validate_constraints ( )
{
// 1. Let controls be a list of all the submittable elements whose form owner is form, in tree order.
auto controls = get_submittable_elements ( ) ;
// 2. Let invalid controls be an initially empty list of elements.
GC : : RootVector < GC : : Ref < DOM : : Element > > invalid_controls ( realm ( ) . heap ( ) ) ;
// 3. For each element field in controls, in tree order:
for ( auto & element : controls ) {
auto & field = as < FormAssociatedElement > ( * element ) ;
// 1. If field is not a candidate for constraint validation, then move on to the next element.
if ( ! field . is_candidate_for_constraint_validation ( ) )
continue ;
// 2. Otherwise, if field satisfies its constraints, then move on to the next element.
if ( field . satisfies_its_constraints ( ) )
continue ;
// 3. Otherwise, add field to invalid controls.
invalid_controls . append ( field . form_associated_element_to_html_element ( ) ) ;
}
// 4. If invalid controls is empty, then return a positive result.
if ( invalid_controls . is_empty ( ) )
return { true , invalid_controls } ;
// 5. Let unhandled invalid controls be an initially empty list of elements.
GC : : RootVector < GC : : Ref < DOM : : Element > > unhandled_invalid_controls ( realm ( ) . heap ( ) ) ;
// 6. For each element field in invalid controls, if any, in tree order:
for ( auto & field : invalid_controls ) {
// 1. Let notCanceled be the result of firing an event named invalid at field, with the cancelable attribute
// initialized to true.
auto not_canceled = field - > dispatch_event ( DOM : : Event : : create ( this - > realm ( ) ,
EventNames : : invalid , { . cancelable = true } ) ) ;
// 2. If notCanceled is true, then add field to unhandled invalid controls.
if ( not_canceled )
unhandled_invalid_controls . append ( field ) ;
}
// 7. Return a negative result with the list of elements in the unhandled invalid controls list.
return { false , unhandled_invalid_controls } ;
}
2025-05-03 19:52:01 +02:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints
bool HTMLFormElement : : interactively_validate_constraints ( )
{
// 1. Statically validate the constraints of form, and let unhandled invalid controls be the list of elements returned if the result was negative.
// 2. If the result was positive, then return that result.
auto static_validation_result = statically_validate_constraints ( ) ;
if ( static_validation_result . result )
return true ;
auto unhandled_invalid_controls = static_validation_result . unhandled_invalid_controls ;
// 3. Report the problems with the constraints of at least one of the elements given in unhandled invalid controls to the user.
// - User agents may focus one of those elements in the process, by running the focusing steps for that element, and may change the
// scrolling position of the document, or perform some other action that brings the element to the user's attention.
// For elements that are form-associated custom elements, user agents should use their validation anchor instead, for the purposes of these actions.
// - User agents may report more than one constraint violation.
// - User agents may coalesce related constraint violation reports if appropriate (e.g. if multiple radio buttons in a group are marked as required, only one error need be reported).
// - If one of the controls is not being rendered (e.g. it has the hidden attribute set) then user agents may report a script error.
// FIXME: Does this align with other browsers?
auto first_invalid_control = unhandled_invalid_controls . first_matching ( [ ] ( auto control ) {
return control - > check_visibility ( { } ) ;
} ) ;
if ( first_invalid_control . has_value ( ) ) {
auto control = first_invalid_control . release_value ( ) ;
run_focusing_steps ( control ) ;
DOM : : ScrollIntoViewOptions scroll_options ;
scroll_options . block = Bindings : : ScrollLogicalPosition : : Nearest ;
scroll_options . inline_ = Bindings : : ScrollLogicalPosition : : Nearest ;
scroll_options . behavior = Bindings : : ScrollBehavior : : Instant ;
( void ) control - > scroll_into_view ( scroll_options ) ;
}
// 4. Return a negative result.
return false ;
}
2023-03-04 23:58:25 +01:00
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-checkvalidity
WebIDL : : ExceptionOr < bool > HTMLFormElement : : check_validity ( )
{
2025-02-22 22:14:44 +09:00
return statically_validate_constraints ( ) . result ;
2023-03-04 23:58:25 +01:00
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-reportvalidity
WebIDL : : ExceptionOr < bool > HTMLFormElement : : report_validity ( )
{
2025-05-03 19:52:01 +02:00
return interactively_validate_constraints ( ) ;
2023-03-04 23:58:25 +01:00
}
2023-02-03 21:50:14 +01:00
// https://html.spec.whatwg.org/multipage/forms.html#category-submit
2024-11-15 04:01:23 +13:00
Vector < GC : : Ref < DOM : : Element > > HTMLFormElement : : get_submittable_elements ( )
2023-02-03 21:50:14 +01:00
{
2024-11-15 04:01:23 +13:00
Vector < GC : : Ref < DOM : : Element > > submittable_elements ;
2023-02-03 21:50:14 +01:00
2024-02-18 21:52:27 -05:00
root ( ) . for_each_in_subtree ( [ & ] ( auto & node ) {
if ( auto * form_associated_element = dynamic_cast < FormAssociatedElement * > ( & node ) ) {
if ( form_associated_element - > is_submittable ( ) & & form_associated_element - > form ( ) = = this )
submittable_elements . append ( form_associated_element - > form_associated_element_to_html_element ( ) ) ;
}
2023-02-03 21:50:14 +01:00
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2024-02-18 21:52:27 -05:00
} ) ;
2023-02-03 21:50:14 +01:00
2024-02-18 21:52:27 -05:00
return submittable_elements ;
2023-02-03 21:50:14 +01:00
}
2023-06-18 16:30:23 +01:00
2024-05-16 06:02:56 +01:00
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-rellist
2024-11-15 04:01:23 +13:00
GC : : Ref < DOM : : DOMTokenList > HTMLFormElement : : rel_list ( )
2024-05-16 06:02:56 +01:00
{
// The relList IDL attribute must reflect the rel content attribute.
if ( ! m_rel_list )
m_rel_list = DOM : : DOMTokenList : : create ( * this , HTML : : AttributeNames : : rel ) ;
2024-05-18 05:54:15 +01:00
return * m_rel_list ;
2024-05-16 06:02:56 +01:00
}
2023-06-18 16:30:23 +01:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-fs-method
2023-09-03 15:44:24 +12:00
WebIDL : : ExceptionOr < void > HTMLFormElement : : set_method ( String const & method )
2023-06-18 16:30:23 +01:00
{
// The method and enctype IDL attributes must reflect the respective content attributes of the same name, limited to only known values.
return set_attribute ( AttributeNames : : method , method ) ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-fs-action
2023-09-03 15:44:24 +12:00
String HTMLFormElement : : action ( ) const
2023-06-18 16:30:23 +01:00
{
// The action IDL attribute must reflect the content attribute of the same name, except that on getting, when the
// content attribute is missing or its value is the empty string, the element's node document's URL must be returned
// instead.
2024-09-08 00:51:59 +01:00
auto form_action_attribute = attribute ( AttributeNames : : action ) ;
if ( ! form_action_attribute . has_value ( ) | | form_action_attribute . value ( ) . is_empty ( ) ) {
return document ( ) . url_string ( ) ;
2024-01-12 09:25:01 +01:00
}
2023-06-18 16:30:23 +01:00
2025-02-15 22:55:46 +13:00
if ( auto maybe_url = document ( ) . base_url ( ) . complete_url ( form_action_attribute . value ( ) ) ; maybe_url . has_value ( ) )
return maybe_url - > to_string ( ) ;
return { } ;
2023-06-18 16:30:23 +01:00
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-fs-action
2023-09-03 15:44:24 +12:00
WebIDL : : ExceptionOr < void > HTMLFormElement : : set_action ( String const & value )
2023-06-18 16:30:23 +01:00
{
return set_attribute ( AttributeNames : : action , value ) ;
}
2024-11-14 08:14:16 -05:00
void HTMLFormElement : : attribute_changed ( FlyString const & name , Optional < String > const & old_value , Optional < String > const & value , Optional < FlyString > const & namespace_ )
2024-05-16 06:02:56 +01:00
{
2024-11-14 08:14:16 -05:00
Base : : attribute_changed ( name , old_value , value , namespace_ ) ;
2024-05-16 06:02:56 +01:00
if ( name = = HTML : : AttributeNames : : rel ) {
if ( m_rel_list )
m_rel_list - > associated_attribute_changed ( value . value_or ( String { } ) ) ;
}
}
2023-06-18 16:30:23 +01:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#picking-an-encoding-for-the-form
ErrorOr < String > HTMLFormElement : : pick_an_encoding ( ) const
{
// 1. Let encoding be the document's character encoding.
auto encoding = document ( ) . encoding_or_default ( ) ;
// 2. If the form element has an accept-charset attribute, set encoding to the return value of running these substeps:
2024-01-12 09:25:01 +01:00
if ( auto maybe_input = attribute ( AttributeNames : : accept_charset ) ; maybe_input . has_value ( ) ) {
2023-06-18 16:30:23 +01:00
// 1. Let input be the value of the form element's accept-charset attribute.
2024-01-12 09:25:01 +01:00
auto input = maybe_input . release_value ( ) ;
2023-06-18 16:30:23 +01:00
// 2. Let candidate encoding labels be the result of splitting input on ASCII whitespace.
2024-01-12 09:25:01 +01:00
auto candidate_encoding_labels = input . bytes_as_string_view ( ) . split_view_if ( Infra : : is_ascii_whitespace ) ;
2023-06-18 16:30:23 +01:00
// 3. Let candidate encodings be an empty list of character encodings.
Vector < StringView > candidate_encodings ;
// 4. For each token in candidate encoding labels in turn (in the order in which they were found in input),
// get an encoding for the token and, if this does not result in failure, append the encoding to candidate
// encodings.
for ( auto const & token : candidate_encoding_labels ) {
auto candidate_encoding = TextCodec : : get_standardized_encoding ( token ) ;
if ( candidate_encoding . has_value ( ) )
TRY ( candidate_encodings . try_append ( candidate_encoding . value ( ) ) ) ;
}
// 5. If candidate encodings is empty, return UTF-8.
if ( candidate_encodings . is_empty ( ) )
return " UTF-8 " _string ;
// 6. Return the first encoding in candidate encodings.
return String : : from_utf8 ( candidate_encodings . first ( ) ) ;
}
// 3. Return the result of getting an output encoding from encoding.
2023-09-14 19:03:38 +12:00
return MUST ( String : : from_utf8 ( TextCodec : : get_output_encoding ( encoding ) ) ) ;
2023-06-18 16:30:23 +01:00
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#convert-to-a-list-of-name-value-pairs
2024-02-11 19:48:56 +13:00
static ErrorOr < Vector < DOMURL : : QueryParam > > convert_to_list_of_name_value_pairs ( Vector < XHR : : FormDataEntry > const & entry_list )
2023-06-18 16:30:23 +01:00
{
// 1. Let list be an empty list of name-value pairs.
2024-02-11 19:48:56 +13:00
Vector < DOMURL : : QueryParam > list ;
2023-06-18 16:30:23 +01:00
// 2. For each entry of entry list:
for ( auto const & entry : entry_list ) {
// 1. Let name be entry's name, with every occurrence of U+000D (CR) not followed by U+000A (LF), and every occurrence of U+000A (LF)
// not preceded by U+000D (CR), replaced by a string consisting of U+000D (CR) and U+000A (LF).
auto name = TRY ( normalize_line_breaks ( entry . name ) ) ;
// 2. If entry's value is a File object, then let value be entry's value's name. Otherwise, let value be entry's value.
String value ;
entry . value . visit (
2024-11-15 04:01:23 +13:00
[ & value ] ( GC : : Root < FileAPI : : File > const & file ) {
2023-06-18 16:30:23 +01:00
value = file - > name ( ) ;
} ,
[ & value ] ( String const & string ) {
value = string ;
} ) ;
// 3. Replace every occurrence of U+000D (CR) not followed by U+000A (LF), and every occurrence of
// U+000A (LF) not preceded by U+000D (CR), in value, by a string consisting of U+000D (CR) and U+000A (LF).
auto normalized_value = TRY ( normalize_line_breaks ( value ) ) ;
// 4. Append to list a new name-value pair whose name is name and whose value is value.
2024-02-11 19:48:56 +13:00
TRY ( list . try_append ( DOMURL : : QueryParam { . name = move ( name ) , . value = move ( normalized_value ) } ) ) ;
2023-06-18 16:30:23 +01:00
}
// 3. Return list.
return list ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#text/plain-encoding-algorithm
2024-02-11 19:48:56 +13:00
static ErrorOr < String > plain_text_encode ( Vector < DOMURL : : QueryParam > const & pairs )
2023-06-18 16:30:23 +01:00
{
// 1. Let result be the empty string.
StringBuilder result ;
// 2. For each pair in pairs:
for ( auto const & pair : pairs ) {
// 1. Append pair's name to result.
TRY ( result . try_append ( pair . name ) ) ;
// 2. Append a single U+003D EQUALS SIGN character (=) to result.
TRY ( result . try_append ( ' = ' ) ) ;
// 3. Append pair's value to result.
TRY ( result . try_append ( pair . value ) ) ;
// 4. Append a U+000D CARRIAGE RETURN (CR) U+000A LINE FEED (LF) character pair to result.
TRY ( result . try_append ( " \r \n " sv ) ) ;
}
// 3. Return result.
return result . to_string ( ) ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-mutate-action
2024-11-15 04:01:23 +13:00
ErrorOr < void > HTMLFormElement : : mutate_action_url ( URL : : URL parsed_action , Vector < XHR : : FormDataEntry > entry_list , String encoding , GC : : Ref < Navigable > target_navigable , Bindings : : NavigationHistoryBehavior history_handling , UserNavigationInvolvement user_involvement )
2023-06-18 16:30:23 +01:00
{
// 1. Let pairs be the result of converting to a list of name-value pairs with entry list.
auto pairs = TRY ( convert_to_list_of_name_value_pairs ( entry_list ) ) ;
// 2. Let query be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
2024-08-11 00:24:54 +12:00
auto query = url_encode ( pairs , encoding ) ;
2023-06-18 16:30:23 +01:00
// 3. Set parsed action's query component to query.
2023-08-12 19:28:19 +12:00
parsed_action . set_query ( query ) ;
2023-06-18 16:30:23 +01:00
// 4. Plan to navigate to parsed action.
2024-11-25 17:10:33 +00:00
plan_to_navigate_to ( move ( parsed_action ) , Empty { } , move ( entry_list ) , target_navigable , history_handling , user_involvement ) ;
2023-06-18 16:30:23 +01:00
return { } ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-body
2024-11-15 04:01:23 +13:00
ErrorOr < void > HTMLFormElement : : submit_as_entity_body ( URL : : URL parsed_action , Vector < XHR : : FormDataEntry > entry_list , EncodingTypeAttributeState encoding_type , [[maybe_unused]] String encoding , GC : : Ref < Navigable > target_navigable , Bindings : : NavigationHistoryBehavior history_handling , UserNavigationInvolvement user_involvement )
2023-06-18 16:30:23 +01:00
{
// 1. Assert: method is POST.
2023-06-23 00:32:10 +03:00
POSTResource : : RequestContentType mime_type { } ;
2024-08-21 15:18:28 -04:00
Vector < POSTResource : : Directive > mime_type_directives ;
2023-06-18 16:30:23 +01:00
ByteBuffer body ;
// 2. Switch on enctype:
switch ( encoding_type ) {
case EncodingTypeAttributeState : : FormUrlEncoded : {
// -> application/x-www-form-urlencoded
// 1. Let pairs be the result of converting to a list of name-value pairs with entry list.
auto pairs = TRY ( convert_to_list_of_name_value_pairs ( entry_list ) ) ;
// 2. Let body be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
2024-08-11 00:24:54 +12:00
body = TRY ( ByteBuffer : : copy ( url_encode ( pairs , encoding ) . bytes ( ) ) ) ;
2023-06-18 16:30:23 +01:00
// 3. Set body to the result of encoding body.
// NOTE: `encoding` refers to `UTF-8 encode`, which body already is encoded as because it uses AK::String.
// 4. Let mimeType be `application/x-www-form-urlencoded`.
2023-06-23 00:32:10 +03:00
mime_type = POSTResource : : RequestContentType : : ApplicationXWWWFormUrlencoded ;
2023-06-18 16:30:23 +01:00
break ;
}
case EncodingTypeAttributeState : : FormData : {
// -> multipart/form-data
// 1. Let body be the result of running the multipart/form-data encoding algorithm with entry list and encoding.
auto body_and_mime_type = TRY ( serialize_to_multipart_form_data ( entry_list ) ) ;
body = move ( body_and_mime_type . serialized_data ) ;
// 2. Let mimeType be the isomorphic encoding of the concatenation of "multipart/form-data; boundary=" and the multipart/form-data
// boundary string generated by the multipart/form-data encoding algorithm.
2023-06-23 00:32:10 +03:00
mime_type = POSTResource : : RequestContentType : : MultipartFormData ;
2024-08-21 15:18:28 -04:00
mime_type_directives . empend ( " boundary " sv , move ( body_and_mime_type . boundary ) ) ;
2024-08-21 13:16:55 -04:00
break ;
2023-06-18 16:30:23 +01:00
}
case EncodingTypeAttributeState : : PlainText : {
// -> text/plain
// 1. Let pairs be the result of converting to a list of name-value pairs with entry list.
auto pairs = TRY ( convert_to_list_of_name_value_pairs ( entry_list ) ) ;
// 2. Let body be the result of running the text/plain encoding algorithm with pairs.
body = TRY ( ByteBuffer : : copy ( TRY ( plain_text_encode ( pairs ) ) . bytes ( ) ) ) ;
// FIXME: 3. Set body to the result of encoding body using encoding.
// 4. Let mimeType be `text/plain`.
2023-06-23 00:32:10 +03:00
mime_type = POSTResource : : RequestContentType : : TextPlain ;
2023-06-18 16:30:23 +01:00
break ;
}
default :
VERIFY_NOT_REACHED ( ) ;
}
2023-06-23 00:32:10 +03:00
// 3. Plan to navigate to parsed action given a POST resource whose request body is body and request content-type is mimeType.
2024-11-25 17:10:33 +00:00
plan_to_navigate_to ( parsed_action , POSTResource { . request_body = move ( body ) , . request_content_type = mime_type , . request_content_type_directives = move ( mime_type_directives ) } , move ( entry_list ) , target_navigable , history_handling , user_involvement ) ;
2023-06-18 16:30:23 +01:00
return { } ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-get-action
2024-11-25 17:10:33 +00:00
void HTMLFormElement : : get_action_url ( URL : : URL parsed_action , Vector < XHR : : FormDataEntry > entry_list , GC : : Ref < Navigable > target_navigable , Bindings : : NavigationHistoryBehavior history_handling , UserNavigationInvolvement user_involvement )
2023-06-18 16:30:23 +01:00
{
// 1. Plan to navigate to parsed action.
// Spec Note: entry list is discarded.
2024-11-25 17:10:33 +00:00
plan_to_navigate_to ( move ( parsed_action ) , Empty { } , move ( entry_list ) , target_navigable , history_handling , user_involvement ) ;
2023-06-18 16:30:23 +01:00
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#submit-mailto-headers
2024-11-15 04:01:23 +13:00
ErrorOr < void > HTMLFormElement : : mail_with_headers ( URL : : URL parsed_action , Vector < XHR : : FormDataEntry > entry_list , [[maybe_unused]] String encoding , GC : : Ref < Navigable > target_navigable , Bindings : : NavigationHistoryBehavior history_handling , UserNavigationInvolvement user_involvement )
2023-06-18 16:30:23 +01:00
{
// 1. Let pairs be the result of converting to a list of name-value pairs with entry list.
auto pairs = TRY ( convert_to_list_of_name_value_pairs ( entry_list ) ) ;
// 2. Let headers be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
2024-08-11 00:24:54 +12:00
auto headers = url_encode ( pairs , encoding ) ;
2023-06-18 16:30:23 +01:00
// 3. Replace occurrences of U+002B PLUS SIGN characters (+) in headers with the string "%20".
TRY ( headers . replace ( " + " sv , " %20 " sv , ReplaceMode : : All ) ) ;
// 4. Set parsed action's query to headers.
2023-08-12 19:28:19 +12:00
parsed_action . set_query ( headers ) ;
2023-06-18 16:30:23 +01:00
// 5. Plan to navigate to parsed action.
2024-11-25 17:10:33 +00:00
plan_to_navigate_to ( move ( parsed_action ) , Empty { } , move ( entry_list ) , target_navigable , history_handling , user_involvement ) ;
2023-06-18 16:30:23 +01:00
return { } ;
}
2024-11-15 04:01:23 +13:00
ErrorOr < void > HTMLFormElement : : mail_as_body ( URL : : URL parsed_action , Vector < XHR : : FormDataEntry > entry_list , EncodingTypeAttributeState encoding_type , [[maybe_unused]] String encoding , GC : : Ref < Navigable > target_navigable , Bindings : : NavigationHistoryBehavior history_handling , UserNavigationInvolvement user_involvement )
2023-06-18 16:30:23 +01:00
{
// 1. Let pairs be the result of converting to a list of name-value pairs with entry list.
auto pairs = TRY ( convert_to_list_of_name_value_pairs ( entry_list ) ) ;
String body ;
// 2. Switch on enctype:
switch ( encoding_type ) {
case EncodingTypeAttributeState : : PlainText : {
// -> text/plain
// 1. Let body be the result of running the text/plain encoding algorithm with pairs.
body = TRY ( plain_text_encode ( pairs ) ) ;
// 2. Set body to the result of running UTF-8 percent-encode on body using the default encode set. [URL]
// NOTE: body is already UTF-8 encoded due to using AK::String, so we only have to do the percent encoding.
// NOTE: "default encode set" links to "path percent-encode-set": https://url.spec.whatwg.org/#default-encode-set
2024-08-10 13:12:19 +12:00
body = URL : : percent_encode ( body , URL : : PercentEncodeSet : : Path ) ;
2023-06-18 16:30:23 +01:00
break ;
}
default :
// -> Otherwise
// Let body be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
2024-08-11 00:24:54 +12:00
body = url_encode ( pairs , encoding ) ;
2023-06-18 16:30:23 +01:00
break ;
}
// 3. If parsed action's query is null, then set it to the empty string.
2023-08-12 19:28:19 +12:00
if ( ! parsed_action . query ( ) . has_value ( ) )
parsed_action . set_query ( String { } ) ;
2023-06-18 16:30:23 +01:00
StringBuilder query_builder ;
2023-08-12 19:28:19 +12:00
query_builder . append ( * parsed_action . query ( ) ) ;
2023-06-18 16:30:23 +01:00
// 4. If parsed action's query is not the empty string, then append a single U+0026 AMPERSAND character (&) to it.
2023-08-12 19:28:19 +12:00
if ( ! parsed_action . query ( ) - > is_empty ( ) )
2023-06-18 16:30:23 +01:00
TRY ( query_builder . try_append ( ' & ' ) ) ;
// 5. Append "body=" to parsed action's query.
TRY ( query_builder . try_append ( " body= " sv ) ) ;
// 6. Append body to parsed action's query.
TRY ( query_builder . try_append ( body ) ) ;
2023-08-12 19:28:19 +12:00
parsed_action . set_query ( MUST ( query_builder . to_string ( ) ) ) ;
2023-06-18 16:30:23 +01:00
// 7. Plan to navigate to parsed action.
2024-11-25 17:10:33 +00:00
plan_to_navigate_to ( move ( parsed_action ) , Empty { } , move ( entry_list ) , target_navigable , history_handling , user_involvement ) ;
2023-06-18 16:30:23 +01:00
return { } ;
}
2023-06-23 00:32:10 +03:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plan-to-navigate
2024-11-25 17:10:33 +00:00
void HTMLFormElement : : plan_to_navigate_to ( URL : : URL url , Variant < Empty , String , POSTResource > post_resource , Vector < XHR : : FormDataEntry > entry_list , GC : : Ref < Navigable > target_navigable , Bindings : : NavigationHistoryBehavior history_handling , UserNavigationInvolvement user_involvement )
2023-06-23 00:32:10 +03:00
{
2023-06-18 16:30:23 +01:00
// 1. Let referrerPolicy be the empty string.
2023-06-23 00:32:10 +03:00
ReferrerPolicy : : ReferrerPolicy referrer_policy = ReferrerPolicy : : ReferrerPolicy : : EmptyString ;
2023-06-18 16:30:23 +01:00
// 2. If the form element's link types include the noreferrer keyword, then set referrerPolicy to "no-referrer".
2024-01-16 19:04:45 +01:00
auto rel = MUST ( get_attribute_value ( HTML : : AttributeNames : : rel ) . to_lowercase ( ) ) ;
auto link_types = rel . bytes_as_string_view ( ) . split_view_if ( Infra : : is_ascii_whitespace ) ;
2023-06-18 16:30:23 +01:00
if ( link_types . contains_slow ( " noreferrer " sv ) )
referrer_policy = ReferrerPolicy : : ReferrerPolicy : : NoReferrer ;
// 3. If the form has a non-null planned navigation, remove it from its task queue.
if ( m_planned_navigation ) {
HTML : : main_thread_event_loop ( ) . task_queue ( ) . remove_tasks_matching ( [ this ] ( Task const & task ) {
return & task = = m_planned_navigation ;
} ) ;
}
// 4. Queue an element task on the DOM manipulation task source given the form element and the following steps:
2024-11-15 04:01:23 +13:00
// NOTE: `this`, `actual_resource` and `target_navigable` are protected by GC::HeapFunction.
2024-11-25 17:10:33 +00:00
queue_an_element_task ( Task : : Source : : DOMManipulation , [ this , url , post_resource , entry_list = move ( entry_list ) , target_navigable , history_handling , referrer_policy , user_involvement ] ( ) {
2023-06-18 16:30:23 +01:00
// 1. Set the form's planned navigation to null.
2024-04-05 13:47:48 -07:00
m_planned_navigation = { } ;
2023-06-18 16:30:23 +01:00
2023-06-23 00:32:10 +03:00
// 2. Navigate targetNavigable to url using the form element's node document, with historyHandling set to historyHandling,
2024-11-25 17:10:33 +00:00
// referrerPolicy set to referrerPolicy, documentResource set to postResource, and formDataEntryList set to entry list.
2023-10-10 16:05:38 +02:00
MUST ( target_navigable - > navigate ( { . url = url ,
. source_document = this - > document ( ) ,
. document_resource = post_resource ,
. response = nullptr ,
. exceptions_enabled = false ,
2024-01-18 12:58:22 -07:00
. history_handling = history_handling ,
2024-11-25 17:10:33 +00:00
. form_data_entry_list = move ( entry_list ) ,
2024-01-18 12:58:22 -07:00
. referrer_policy = referrer_policy ,
. user_involvement = user_involvement } ) ) ;
2023-06-18 16:30:23 +01:00
} ) ;
// 5. Set the form's planned navigation to the just-queued task.
m_planned_navigation = HTML : : main_thread_event_loop ( ) . task_queue ( ) . last_added_task ( ) ;
VERIFY ( m_planned_navigation ) ;
}
2024-01-10 10:45:28 -07:00
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-item
2024-07-25 18:15:51 +12:00
Optional < JS : : Value > HTMLFormElement : : item_value ( size_t index ) const
2024-01-10 10:45:28 -07:00
{
// To determine the value of an indexed property for a form element, the user agent must return the value returned by
// the item method on the elements collection, when invoked with the given index as its argument.
2024-07-25 18:15:51 +12:00
if ( auto value = elements ( ) - > item ( index ) )
return value ;
return { } ;
2024-01-10 10:45:28 -07:00
}
// https://html.spec.whatwg.org/multipage/forms.html#the-form-element:supported-property-names
Vector < FlyString > HTMLFormElement : : supported_property_names ( ) const
{
// The supported property names consist of the names obtained from the following algorithm, in the order obtained from this algorithm:
// 1. Let sourced names be an initially empty ordered list of tuples consisting of a string, an element, a source,
// where the source is either id, name, or past, and, if the source is past, an age.
struct SourcedName {
FlyString name ;
2024-11-15 04:01:23 +13:00
GC : : Ptr < DOM : : Element const > element ;
2024-01-10 10:45:28 -07:00
enum class Source {
Id ,
Name ,
Past ,
} source ;
2024-07-16 23:45:44 -06:00
AK : : Duration age ;
2024-01-10 10:45:28 -07:00
} ;
Vector < SourcedName > sourced_names ;
// 2. For each listed element candidate whose form owner is the form element, with the exception of any
// input elements whose type attribute is in the Image Button state:
for ( auto const & candidate : m_associated_elements ) {
if ( ! is_form_control ( * candidate , * this ) )
continue ;
// 1. If candidate has an id attribute, add an entry to sourced names with that id attribute's value as the
// string, candidate as the element, and id as the source.
2024-01-12 09:25:01 +01:00
if ( candidate - > id ( ) . has_value ( ) )
2024-01-10 10:45:28 -07:00
sourced_names . append ( SourcedName { candidate - > id ( ) . value ( ) , candidate , SourcedName : : Source : : Id , { } } ) ;
// 2. If candidate has a name attribute, add an entry to sourced names with that name attribute's value as the
// string, candidate as the element, and name as the source.
2024-01-13 20:12:25 +13:00
if ( candidate - > name ( ) . has_value ( ) )
sourced_names . append ( SourcedName { candidate - > name ( ) . value ( ) , candidate , SourcedName : : Source : : Name , { } } ) ;
2024-01-10 10:45:28 -07:00
}
// 3. For each img element candidate whose form owner is the form element:
for ( auto const & candidate : m_associated_elements ) {
if ( ! is < HTMLImageElement > ( * candidate ) )
continue ;
// Every element in m_associated_elements has this as the form owner.
// 1. If candidate has an id attribute, add an entry to sourced names with that id attribute's value as the
// string, candidate as the element, and id as the source.
2024-01-12 09:25:01 +01:00
if ( candidate - > id ( ) . has_value ( ) )
2024-01-10 10:45:28 -07:00
sourced_names . append ( SourcedName { candidate - > id ( ) . value ( ) , candidate , SourcedName : : Source : : Id , { } } ) ;
// 2. If candidate has a name attribute, add an entry to sourced names with that name attribute's value as the
// string, candidate as the element, and name as the source.
2024-01-13 20:12:25 +13:00
if ( candidate - > name ( ) . has_value ( ) )
sourced_names . append ( SourcedName { candidate - > name ( ) . value ( ) , candidate , SourcedName : : Source : : Name , { } } ) ;
2024-01-10 10:45:28 -07:00
}
// 4. For each entry past entry in the past names map add an entry to sourced names with the past entry's name as
// the string, past entry's element as the element, past as the source, and the length of time past entry has
// been in the past names map as the age.
auto const now = MonotonicTime : : now ( ) ;
for ( auto const & entry : m_past_names_map )
sourced_names . append ( SourcedName { entry . key , static_cast < DOM : : Element const * > ( entry . value . node . ptr ( ) ) , SourcedName : : Source : : Past , now - entry . value . insertion_time } ) ;
// 5. Sort sourced names by tree order of the element entry of each tuple, sorting entries with the same element by
// putting entries whose source is id first, then entries whose source is name, and finally entries whose source
// is past, and sorting entries with the same element and source by their age, oldest first.
// FIXME: Require less const casts here by changing the signature of DOM::Node::compare_document_position
quick_sort ( sourced_names , [ ] ( auto const & lhs , auto const & rhs ) - > bool {
if ( lhs . element ! = rhs . element )
return const_cast < DOM : : Element * > ( lhs . element . ptr ( ) ) - > compare_document_position ( const_cast < DOM : : Element * > ( rhs . element . ptr ( ) ) ) & DOM : : Node : : DOCUMENT_POSITION_FOLLOWING ;
if ( lhs . source ! = rhs . source )
return lhs . source < rhs . source ;
return lhs . age < rhs . age ;
} ) ;
// FIXME: Surely there's a more efficient way to do this without so many FlyStrings and collections?
// 6. Remove any entries in sourced names that have the empty string as their name.
// 7. Remove any entries in sourced names that have the same name as an earlier entry in the map.
// 8. Return the list of names from sourced names, maintaining their relative order.
OrderedHashTable < FlyString > names ;
names . ensure_capacity ( sourced_names . size ( ) ) ;
for ( auto const & entry : sourced_names ) {
if ( entry . name . is_empty ( ) )
continue ;
names . set ( entry . name , AK : : HashSetExistingEntryBehavior : : Keep ) ;
}
Vector < FlyString > result ;
result . ensure_capacity ( names . size ( ) ) ;
for ( auto const & name : names )
result . unchecked_append ( name ) ;
return result ;
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-nameditem
2024-07-25 17:36:10 +12:00
JS : : Value HTMLFormElement : : named_item_value ( FlyString const & name ) const
2024-01-10 10:45:28 -07:00
{
auto & realm = this - > realm ( ) ;
2025-01-21 09:12:05 -05:00
auto & root = as < ParentNode > ( this - > root ( ) ) ;
2024-01-10 10:45:28 -07:00
// To determine the value of a named property name for a form element, the user agent must run the following steps:
// 1. Let candidates be a live RadioNodeList object containing all the listed elements, whose form owner is the form
// element, that have either an id attribute or a name attribute equal to name, with the exception of input
// elements whose type attribute is in the Image Button state, in tree order.
2024-11-02 09:22:13 -04:00
auto candidates = RadioNodeList : : create ( realm , root , DOM : : LiveNodeList : : Scope : : Descendants , [ this , name ] ( auto & node ) - > bool {
2024-01-10 10:45:28 -07:00
if ( ! is < DOM : : Element > ( node ) )
return false ;
auto const & element = static_cast < DOM : : Element const & > ( node ) ;
// Form controls are defined as listed elements, with the exception of input elements in the Image Button state,
// whose form owner is the form element.
if ( ! is_form_control ( element , * this ) )
return false ;
2024-01-13 20:12:25 +13:00
return name = = element . id ( ) | | name = = element . name ( ) ;
2024-01-10 10:45:28 -07:00
} ) ;
// 2. If candidates is empty, let candidates be a live RadioNodeList object containing all the img elements,
// whose form owner is the form element, that have either an id attribute or a name attribute equal to name,
// in tree order.
if ( candidates - > length ( ) = = 0 ) {
2024-11-02 09:22:13 -04:00
candidates = RadioNodeList : : create ( realm , root , DOM : : LiveNodeList : : Scope : : Descendants , [ this , name ] ( auto & node ) - > bool {
2024-01-10 10:45:28 -07:00
if ( ! is < HTMLImageElement > ( node ) )
return false ;
auto const & element = static_cast < HTMLImageElement const & > ( node ) ;
if ( element . form ( ) ! = this )
return false ;
2024-01-13 20:12:25 +13:00
return name = = element . id ( ) | | name = = element . name ( ) ;
2024-01-10 10:45:28 -07:00
} ) ;
}
auto length = candidates - > length ( ) ;
// 3. If candidates is empty, name is the name of one of the entries in the form element's past names map: return the object associated with name in that map.
if ( length = = 0 ) {
auto it = m_past_names_map . find ( name ) ;
if ( it ! = m_past_names_map . end ( ) )
return it - > value . node ;
}
// 4. If candidates contains more than one node, return candidates.
if ( length > 1 )
return candidates ;
// 5. Otherwise, candidates contains exactly one node. Add a mapping from name to the node in candidates in the form
// element's past names map, replacing the previous entry with the same name, if any.
auto const * node = candidates - > item ( 0 ) ;
m_past_names_map . set ( name , HTMLFormElement : : PastNameEntry { . node = node , . insertion_time = MonotonicTime : : now ( ) } ) ;
// 6. Return the node in candidates.
return node ;
}
2024-01-31 13:26:01 -05:00
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#default-button
FormAssociatedElement * HTMLFormElement : : default_button ( )
{
// A form element's default button is the first submit button in tree order whose form owner is that form element.
FormAssociatedElement * default_button = nullptr ;
root ( ) . for_each_in_subtree ( [ & ] ( auto & node ) {
auto * form_associated_element = dynamic_cast < FormAssociatedElement * > ( & node ) ;
if ( ! form_associated_element )
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2024-01-31 13:26:01 -05:00
if ( form_associated_element - > form ( ) = = this & & form_associated_element - > is_submit_button ( ) ) {
default_button = form_associated_element ;
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Break ;
2024-01-31 13:26:01 -05:00
}
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2024-01-31 13:26:01 -05:00
} ) ;
return default_button ;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#field-that-blocks-implicit-submission
size_t HTMLFormElement : : number_of_fields_blocking_implicit_submission ( ) const
{
// For the purpose of the previous paragraph, an element is a field that blocks implicit submission of a form
// element if it is an input element whose form owner is that form element and whose type attribute is in one of
// the following states: Text, Search, Telephone, URL, Email, Password, Date, Month, Week, Time,
// Local Date and Time, Number.
size_t count = 0 ;
for ( auto element : m_associated_elements ) {
if ( ! is < HTMLInputElement > ( * element ) )
continue ;
auto const & input = static_cast < HTMLInputElement & > ( * element ) ;
using enum HTMLInputElement : : TypeAttributeState ;
switch ( input . type_state ( ) ) {
case Text :
case Search :
case Telephone :
case URL :
case Email :
case Password :
case Date :
case Month :
case Week :
case Time :
case LocalDateAndTime :
case Number :
+ + count ;
break ;
default :
break ;
}
} ;
return count ;
}
2020-03-07 10:27:02 +01:00
}