script/dom/html/
htmlformelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::ToOwned;
6use std::cell::Cell;
7
8use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
9use dom_struct::dom_struct;
10use encoding_rs::{Encoding, UTF_8};
11use headers::{ContentType, HeaderMapExt};
12use html5ever::{LocalName, Prefix, local_name, ns};
13use http::Method;
14use js::rust::HandleObject;
15use mime::{self, Mime};
16use net_traits::http_percent_encode;
17use net_traits::request::Referrer;
18use servo_rand::random;
19use style::attr::AttrValue;
20use style::str::split_html_space_chars;
21use stylo_atoms::Atom;
22use stylo_dom::ElementState;
23
24use crate::body::Extractable;
25use crate::dom::attr::Attr;
26use crate::dom::bindings::cell::DomRefCell;
27use crate::dom::bindings::codegen::Bindings::AttrBinding::Attr_Binding::AttrMethods;
28use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
29use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
30use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
31use crate::dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
32use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
33use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
34use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
35use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
37use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
38use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
39use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
40use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
41use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
42use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement;
43use crate::dom::bindings::error::{Error, Fallible};
44use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
45use crate::dom::bindings::refcounted::Trusted;
46use crate::dom::bindings::reflector::{DomGlobal, DomObject};
47use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, MutNullableDom};
48use crate::dom::bindings::str::DOMString;
49use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
50use crate::dom::blob::Blob;
51use crate::dom::customelementregistry::CallbackReaction;
52use crate::dom::document::Document;
53use crate::dom::domtokenlist::DOMTokenList;
54use crate::dom::element::{AttributeMutation, Element};
55use crate::dom::event::{Event, EventBubbles, EventCancelable};
56use crate::dom::eventtarget::EventTarget;
57use crate::dom::file::File;
58use crate::dom::formdata::FormData;
59use crate::dom::formdataevent::FormDataEvent;
60use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
61use crate::dom::html::htmlcollection::CollectionFilter;
62use crate::dom::html::htmldatalistelement::HTMLDataListElement;
63use crate::dom::html::htmlelement::HTMLElement;
64use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
65use crate::dom::html::htmlformcontrolscollection::HTMLFormControlsCollection;
66use crate::dom::html::htmlimageelement::HTMLImageElement;
67use crate::dom::html::htmlinputelement::{HTMLInputElement, InputType};
68use crate::dom::html::htmllabelelement::HTMLLabelElement;
69use crate::dom::html::htmllegendelement::HTMLLegendElement;
70use crate::dom::html::htmlobjectelement::HTMLObjectElement;
71use crate::dom::html::htmloutputelement::HTMLOutputElement;
72use crate::dom::html::htmlselectelement::HTMLSelectElement;
73use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
74use crate::dom::node::{
75    BindContext, Node, NodeFlags, NodeTraits, UnbindContext, VecPreOrderInsertionHelper,
76};
77use crate::dom::nodelist::{NodeList, RadioListMode};
78use crate::dom::radionodelist::RadioNodeList;
79use crate::dom::submitevent::SubmitEvent;
80use crate::dom::virtualmethods::VirtualMethods;
81use crate::dom::window::Window;
82use crate::links::{LinkRelations, get_element_target};
83use crate::script_runtime::CanGc;
84use crate::script_thread::ScriptThread;
85
86#[dom_struct]
87pub(crate) struct HTMLFormElement {
88    htmlelement: HTMLElement,
89    marked_for_reset: Cell<bool>,
90    /// <https://html.spec.whatwg.org/multipage/#constructing-entry-list>
91    constructing_entry_list: Cell<bool>,
92    elements: DomOnceCell<HTMLFormControlsCollection>,
93    controls: DomRefCell<Vec<Dom<Element>>>,
94
95    #[allow(clippy::type_complexity)]
96    past_names_map: DomRefCell<HashMapTracedValues<Atom, (Dom<Element>, NoTrace<usize>)>>,
97
98    /// The current generation of past names, i.e., the number of name changes to the name.
99    current_name_generation: Cell<usize>,
100
101    firing_submission_events: Cell<bool>,
102    rel_list: MutNullableDom<DOMTokenList>,
103
104    /// <https://html.spec.whatwg.org/multipage/#planned-navigation>
105    planned_navigation: Cell<usize>,
106
107    #[no_trace]
108    relations: Cell<LinkRelations>,
109}
110
111impl HTMLFormElement {
112    fn new_inherited(
113        local_name: LocalName,
114        prefix: Option<Prefix>,
115        document: &Document,
116    ) -> HTMLFormElement {
117        HTMLFormElement {
118            htmlelement: HTMLElement::new_inherited_with_state(
119                ElementState::VALID,
120                local_name,
121                prefix,
122                document,
123            ),
124            marked_for_reset: Cell::new(false),
125            constructing_entry_list: Cell::new(false),
126            elements: Default::default(),
127            controls: DomRefCell::new(Vec::new()),
128            past_names_map: DomRefCell::new(HashMapTracedValues::new()),
129            current_name_generation: Cell::new(0),
130            firing_submission_events: Cell::new(false),
131            rel_list: Default::default(),
132            planned_navigation: Default::default(),
133            relations: Cell::new(LinkRelations::empty()),
134        }
135    }
136
137    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
138    pub(crate) fn new(
139        local_name: LocalName,
140        prefix: Option<Prefix>,
141        document: &Document,
142        proto: Option<HandleObject>,
143        can_gc: CanGc,
144    ) -> DomRoot<HTMLFormElement> {
145        Node::reflect_node_with_proto(
146            Box::new(HTMLFormElement::new_inherited(local_name, prefix, document)),
147            document,
148            proto,
149            can_gc,
150        )
151    }
152
153    fn filter_for_radio_list(mode: RadioListMode, child: &Element, name: &Atom) -> bool {
154        if let Some(child) = child.downcast::<Element>() {
155            match mode {
156                RadioListMode::ControlsExceptImageInputs => {
157                    if child
158                        .downcast::<HTMLElement>()
159                        .is_some_and(|c| c.is_listed_element()) &&
160                        (child.get_id().is_some_and(|i| i == *name) ||
161                            child.get_name().is_some_and(|n| n == *name))
162                    {
163                        if let Some(inp) = child.downcast::<HTMLInputElement>() {
164                            // input, only return it if it's not image-button state
165                            return inp.input_type() != InputType::Image;
166                        } else {
167                            // control, but not an input
168                            return true;
169                        }
170                    }
171                    return false;
172                },
173                RadioListMode::Images => {
174                    return child.is::<HTMLImageElement>() &&
175                        (child.get_id().is_some_and(|i| i == *name) ||
176                            child.get_name().is_some_and(|n| n == *name));
177                },
178            }
179        }
180        false
181    }
182
183    pub(crate) fn nth_for_radio_list(
184        &self,
185        index: u32,
186        mode: RadioListMode,
187        name: &Atom,
188    ) -> Option<DomRoot<Node>> {
189        self.controls
190            .borrow()
191            .iter()
192            .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
193            .nth(index as usize)
194            .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
195    }
196
197    pub(crate) fn count_for_radio_list(&self, mode: RadioListMode, name: &Atom) -> u32 {
198        self.controls
199            .borrow()
200            .iter()
201            .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
202            .count() as u32
203    }
204}
205
206impl HTMLFormElementMethods<crate::DomTypeHolder> for HTMLFormElement {
207    // https://html.spec.whatwg.org/multipage/#dom-form-acceptcharset
208    make_getter!(AcceptCharset, "accept-charset");
209
210    // https://html.spec.whatwg.org/multipage/#dom-form-acceptcharset
211    make_setter!(SetAcceptCharset, "accept-charset");
212
213    // https://html.spec.whatwg.org/multipage/#dom-fs-action
214    make_form_action_getter!(Action, "action");
215
216    // https://html.spec.whatwg.org/multipage/#dom-fs-action
217    make_setter!(SetAction, "action");
218
219    // https://html.spec.whatwg.org/multipage/#dom-form-autocomplete
220    make_enumerated_getter!(
221        Autocomplete,
222        "autocomplete",
223        "on" | "off",
224        missing => "on",
225        invalid => "on"
226    );
227
228    // https://html.spec.whatwg.org/multipage/#dom-form-autocomplete
229    make_setter!(SetAutocomplete, "autocomplete");
230
231    // https://html.spec.whatwg.org/multipage/#dom-fs-enctype
232    make_enumerated_getter!(
233        Enctype,
234        "enctype",
235        "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
236        missing => "application/x-www-form-urlencoded",
237        invalid => "application/x-www-form-urlencoded"
238    );
239
240    // https://html.spec.whatwg.org/multipage/#dom-fs-enctype
241    make_setter!(SetEnctype, "enctype");
242
243    // https://html.spec.whatwg.org/multipage/#dom-fs-encoding
244    fn Encoding(&self) -> DOMString {
245        self.Enctype()
246    }
247
248    // https://html.spec.whatwg.org/multipage/#dom-fs-encoding
249    fn SetEncoding(&self, value: DOMString) {
250        self.SetEnctype(value)
251    }
252
253    // https://html.spec.whatwg.org/multipage/#dom-fs-method
254    make_enumerated_getter!(
255        Method,
256        "method",
257        "get" | "post" | "dialog",
258        missing => "get",
259        invalid => "get"
260    );
261
262    // https://html.spec.whatwg.org/multipage/#dom-fs-method
263    make_setter!(SetMethod, "method");
264
265    // https://html.spec.whatwg.org/multipage/#dom-form-name
266    make_getter!(Name, "name");
267
268    // https://html.spec.whatwg.org/multipage/#dom-form-name
269    make_atomic_setter!(SetName, "name");
270
271    // https://html.spec.whatwg.org/multipage/#dom-fs-novalidate
272    make_bool_getter!(NoValidate, "novalidate");
273
274    // https://html.spec.whatwg.org/multipage/#dom-fs-novalidate
275    make_bool_setter!(SetNoValidate, "novalidate");
276
277    // https://html.spec.whatwg.org/multipage/#dom-fs-target
278    make_getter!(Target, "target");
279
280    // https://html.spec.whatwg.org/multipage/#dom-fs-target
281    make_setter!(SetTarget, "target");
282
283    // https://html.spec.whatwg.org/multipage/#dom-a-rel
284    make_getter!(Rel, "rel");
285
286    // https://html.spec.whatwg.org/multipage/#the-form-element:concept-form-submit
287    fn Submit(&self, can_gc: CanGc) {
288        self.submit(
289            SubmittedFrom::FromForm,
290            FormSubmitterElement::Form(self),
291            can_gc,
292        );
293    }
294
295    // https://html.spec.whatwg.org/multipage/#dom-form-requestsubmit
296    fn RequestSubmit(&self, submitter: Option<&HTMLElement>, can_gc: CanGc) -> Fallible<()> {
297        let submitter: FormSubmitterElement = match submitter {
298            Some(submitter_element) => {
299                // Step 1.1
300                let error_not_a_submit_button =
301                    Err(Error::Type("submitter must be a submit button".to_string()));
302
303                let element = match submitter_element.upcast::<Node>().type_id() {
304                    NodeTypeId::Element(ElementTypeId::HTMLElement(element)) => element,
305                    _ => {
306                        return error_not_a_submit_button;
307                    },
308                };
309
310                let submit_button = match element {
311                    HTMLElementTypeId::HTMLInputElement => FormSubmitterElement::Input(
312                        submitter_element
313                            .downcast::<HTMLInputElement>()
314                            .expect("Failed to downcast submitter elem to HTMLInputElement."),
315                    ),
316                    HTMLElementTypeId::HTMLButtonElement => FormSubmitterElement::Button(
317                        submitter_element
318                            .downcast::<HTMLButtonElement>()
319                            .expect("Failed to downcast submitter elem to HTMLButtonElement."),
320                    ),
321                    _ => {
322                        return error_not_a_submit_button;
323                    },
324                };
325
326                if !submit_button.is_submit_button() {
327                    return error_not_a_submit_button;
328                }
329
330                let submitters_owner = submit_button.form_owner();
331
332                // Step 1.2
333                let owner = match submitters_owner {
334                    Some(owner) => owner,
335                    None => {
336                        return Err(Error::NotFound);
337                    },
338                };
339
340                if *owner != *self {
341                    return Err(Error::NotFound);
342                }
343
344                submit_button
345            },
346            None => {
347                // Step 2
348                FormSubmitterElement::Form(self)
349            },
350        };
351        // Step 3
352        self.submit(SubmittedFrom::NotFromForm, submitter, can_gc);
353        Ok(())
354    }
355
356    // https://html.spec.whatwg.org/multipage/#dom-form-reset
357    fn Reset(&self, can_gc: CanGc) {
358        self.reset(ResetFrom::FromForm, can_gc);
359    }
360
361    // https://html.spec.whatwg.org/multipage/#dom-form-elements
362    fn Elements(&self, can_gc: CanGc) -> DomRoot<HTMLFormControlsCollection> {
363        #[derive(JSTraceable, MallocSizeOf)]
364        struct ElementsFilter {
365            form: DomRoot<HTMLFormElement>,
366        }
367        impl CollectionFilter for ElementsFilter {
368            fn filter<'a>(&self, elem: &'a Element, _root: &'a Node) -> bool {
369                let form_owner = match elem.upcast::<Node>().type_id() {
370                    NodeTypeId::Element(ElementTypeId::HTMLElement(t)) => match t {
371                        HTMLElementTypeId::HTMLButtonElement => {
372                            elem.downcast::<HTMLButtonElement>().unwrap().form_owner()
373                        },
374                        HTMLElementTypeId::HTMLFieldSetElement => {
375                            elem.downcast::<HTMLFieldSetElement>().unwrap().form_owner()
376                        },
377                        HTMLElementTypeId::HTMLInputElement => {
378                            let input_elem = elem.downcast::<HTMLInputElement>().unwrap();
379                            if input_elem.input_type() == InputType::Image {
380                                return false;
381                            }
382                            input_elem.form_owner()
383                        },
384                        HTMLElementTypeId::HTMLObjectElement => {
385                            elem.downcast::<HTMLObjectElement>().unwrap().form_owner()
386                        },
387                        HTMLElementTypeId::HTMLOutputElement => {
388                            elem.downcast::<HTMLOutputElement>().unwrap().form_owner()
389                        },
390                        HTMLElementTypeId::HTMLSelectElement => {
391                            elem.downcast::<HTMLSelectElement>().unwrap().form_owner()
392                        },
393                        HTMLElementTypeId::HTMLTextAreaElement => {
394                            elem.downcast::<HTMLTextAreaElement>().unwrap().form_owner()
395                        },
396                        HTMLElementTypeId::HTMLElement => {
397                            let html_element = elem.downcast::<HTMLElement>().unwrap();
398                            if html_element.is_form_associated_custom_element() {
399                                html_element.form_owner()
400                            } else {
401                                return false;
402                            }
403                        },
404                        _ => {
405                            debug_assert!(
406                                !elem.downcast::<HTMLElement>().unwrap().is_listed_element()
407                            );
408                            return false;
409                        },
410                    },
411                    _ => return false,
412                };
413
414                match form_owner {
415                    Some(form_owner) => form_owner == self.form,
416                    None => false,
417                }
418            }
419        }
420        DomRoot::from_ref(self.elements.init_once(|| {
421            let filter = Box::new(ElementsFilter {
422                form: DomRoot::from_ref(self),
423            });
424            let window = self.owner_window();
425            HTMLFormControlsCollection::new(&window, self, filter, can_gc)
426        }))
427    }
428
429    // https://html.spec.whatwg.org/multipage/#dom-form-length
430    fn Length(&self) -> u32 {
431        self.Elements(CanGc::note()).Length()
432    }
433
434    // https://html.spec.whatwg.org/multipage/#dom-form-item
435    fn IndexedGetter(&self, index: u32, can_gc: CanGc) -> Option<DomRoot<Element>> {
436        let elements = self.Elements(can_gc);
437        elements.IndexedGetter(index)
438    }
439
440    // https://html.spec.whatwg.org/multipage/#the-form-element%3Adetermine-the-value-of-a-named-property
441    fn NamedGetter(&self, name: DOMString, can_gc: CanGc) -> Option<RadioNodeListOrElement> {
442        let window = self.owner_window();
443
444        let name = Atom::from(name);
445
446        // Step 1
447        let mut candidates =
448            RadioNodeList::new_controls_except_image_inputs(&window, self, &name, can_gc);
449        let mut candidates_length = candidates.Length();
450
451        // Step 2
452        if candidates_length == 0 {
453            candidates = RadioNodeList::new_images(&window, self, &name, can_gc);
454            candidates_length = candidates.Length();
455        }
456
457        let mut past_names_map = self.past_names_map.borrow_mut();
458
459        // Step 3
460        if candidates_length == 0 {
461            if past_names_map.contains_key(&name) {
462                return Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
463                    &*past_names_map.get(&name).unwrap().0,
464                )));
465            }
466            return None;
467        }
468
469        // Step 4
470        if candidates_length > 1 {
471            return Some(RadioNodeListOrElement::RadioNodeList(candidates));
472        }
473
474        // Step 5
475        // candidates_length is 1, so we can unwrap item 0
476        let element_node = candidates.upcast::<NodeList>().Item(0).unwrap();
477        past_names_map.insert(
478            name,
479            (
480                Dom::from_ref(element_node.downcast::<Element>().unwrap()),
481                NoTrace(self.current_name_generation.get() + 1),
482            ),
483        );
484        self.current_name_generation
485            .set(self.current_name_generation.get() + 1);
486
487        // Step 6
488        Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
489            element_node.downcast::<Element>().unwrap(),
490        )))
491    }
492
493    // https://html.spec.whatwg.org/multipage/#dom-a-rel
494    fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
495        self.upcast::<Element>()
496            .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
497    }
498
499    // https://html.spec.whatwg.org/multipage/#dom-a-rellist
500    fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
501        self.rel_list.or_init(|| {
502            DOMTokenList::new(
503                self.upcast(),
504                &local_name!("rel"),
505                Some(vec![
506                    Atom::from("noopener"),
507                    Atom::from("noreferrer"),
508                    Atom::from("opener"),
509                ]),
510                can_gc,
511            )
512        })
513    }
514
515    // https://html.spec.whatwg.org/multipage/#the-form-element:supported-property-names
516    #[allow(non_snake_case)]
517    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
518        // Step 1
519        #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
520        enum SourcedNameSource {
521            Id,
522            Name,
523            Past(usize),
524        }
525
526        impl SourcedNameSource {
527            fn is_past(&self) -> bool {
528                matches!(self, SourcedNameSource::Past(..))
529            }
530        }
531
532        struct SourcedName {
533            name: Atom,
534            element: DomRoot<Element>,
535            source: SourcedNameSource,
536        }
537
538        let mut sourced_names_vec: Vec<SourcedName> = Vec::new();
539
540        // Step 2
541        for child in self.controls.borrow().iter() {
542            if child
543                .downcast::<HTMLElement>()
544                .is_some_and(|c| c.is_listed_element())
545            {
546                if let Some(id_atom) = child.get_id() {
547                    let entry = SourcedName {
548                        name: id_atom,
549                        element: DomRoot::from_ref(child),
550                        source: SourcedNameSource::Id,
551                    };
552                    sourced_names_vec.push(entry);
553                }
554                if let Some(name_atom) = child.get_name() {
555                    let entry = SourcedName {
556                        name: name_atom,
557                        element: DomRoot::from_ref(child),
558                        source: SourcedNameSource::Name,
559                    };
560                    sourced_names_vec.push(entry);
561                }
562            }
563        }
564
565        // Step 3
566        for child in self.controls.borrow().iter() {
567            if child.is::<HTMLImageElement>() {
568                if let Some(id_atom) = child.get_id() {
569                    let entry = SourcedName {
570                        name: id_atom,
571                        element: DomRoot::from_ref(child),
572                        source: SourcedNameSource::Id,
573                    };
574                    sourced_names_vec.push(entry);
575                }
576                if let Some(name_atom) = child.get_name() {
577                    let entry = SourcedName {
578                        name: name_atom,
579                        element: DomRoot::from_ref(child),
580                        source: SourcedNameSource::Name,
581                    };
582                    sourced_names_vec.push(entry);
583                }
584            }
585        }
586
587        // Step 4
588        let past_names_map = self.past_names_map.borrow();
589        for (key, val) in past_names_map.iter() {
590            let entry = SourcedName {
591                name: key.clone(),
592                element: DomRoot::from_ref(&*val.0),
593                source: SourcedNameSource::Past(self.current_name_generation.get() - val.1.0),
594            };
595            sourced_names_vec.push(entry);
596        }
597
598        // Step 5
599        // TODO need to sort as per spec.
600        // if a.CompareDocumentPosition(b) returns 0 that means a=b in which case
601        // the remaining part where sorting is to be done by putting entries whose source is id first,
602        // then entries whose source is name, and finally entries whose source is past,
603        // and sorting entries with the same element and source by their age, oldest first.
604
605        // if a.CompareDocumentPosition(b) has set NodeConstants::DOCUMENT_POSITION_FOLLOWING
606        // (this can be checked by bitwise operations) then b would follow a in tree order and
607        // Ordering::Less should be returned in the closure else Ordering::Greater
608
609        sourced_names_vec.sort_by(|a, b| {
610            if a.element
611                .upcast::<Node>()
612                .CompareDocumentPosition(b.element.upcast::<Node>()) ==
613                0
614            {
615                if a.source.is_past() && b.source.is_past() {
616                    b.source.cmp(&a.source)
617                } else {
618                    a.source.cmp(&b.source)
619                }
620            } else if a
621                .element
622                .upcast::<Node>()
623                .CompareDocumentPosition(b.element.upcast::<Node>()) &
624                NodeConstants::DOCUMENT_POSITION_FOLLOWING ==
625                NodeConstants::DOCUMENT_POSITION_FOLLOWING
626            {
627                std::cmp::Ordering::Less
628            } else {
629                std::cmp::Ordering::Greater
630            }
631        });
632
633        // Step 6
634        sourced_names_vec.retain(|sn| !sn.name.to_string().is_empty());
635
636        // Step 7-8
637        let mut names_vec: Vec<DOMString> = Vec::new();
638        for elem in sourced_names_vec.iter() {
639            if !names_vec.iter().any(|name| *name == *elem.name) {
640                names_vec.push(DOMString::from(&*elem.name));
641            }
642        }
643
644        names_vec
645    }
646
647    /// <https://html.spec.whatwg.org/multipage/#dom-form-checkvalidity>
648    fn CheckValidity(&self, can_gc: CanGc) -> bool {
649        self.static_validation(can_gc).is_ok()
650    }
651
652    /// <https://html.spec.whatwg.org/multipage/#dom-form-reportvalidity>
653    fn ReportValidity(&self, can_gc: CanGc) -> bool {
654        self.interactive_validation(can_gc).is_ok()
655    }
656}
657
658#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
659pub(crate) enum SubmittedFrom {
660    FromForm,
661    NotFromForm,
662}
663
664#[derive(Clone, Copy, MallocSizeOf)]
665pub(crate) enum ResetFrom {
666    FromForm,
667    NotFromForm,
668}
669
670impl HTMLFormElement {
671    // https://html.spec.whatwg.org/multipage/#picking-an-encoding-for-the-form
672    fn pick_encoding(&self) -> &'static Encoding {
673        // Step 2
674        if self
675            .upcast::<Element>()
676            .has_attribute(&local_name!("accept-charset"))
677        {
678            // Substep 1
679            let input = self
680                .upcast::<Element>()
681                .get_string_attribute(&local_name!("accept-charset"));
682
683            // Substep 2, 3, 4
684            let mut candidate_encodings =
685                split_html_space_chars(&input).filter_map(|c| Encoding::for_label(c.as_bytes()));
686
687            // Substep 5, 6
688            return candidate_encodings.next().unwrap_or(UTF_8);
689        }
690
691        // Step 1, 3
692        self.owner_document().encoding()
693    }
694
695    // https://html.spec.whatwg.org/multipage/#text/plain-encoding-algorithm
696    fn encode_plaintext(&self, form_data: &mut [FormDatum]) -> String {
697        // Step 1
698        let mut result = String::new();
699
700        // Step 2
701        for entry in form_data.iter() {
702            let value = match &entry.value {
703                FormDatumValue::File(f) => f.name(),
704                FormDatumValue::String(s) => s,
705            };
706            result.push_str(&format!("{}={}\r\n", entry.name, value));
707        }
708
709        // Step 3
710        result
711    }
712
713    pub(crate) fn update_validity(&self, can_gc: CanGc) {
714        let is_any_invalid = self
715            .controls
716            .borrow()
717            .iter()
718            .any(|control| control.is_invalid(false, can_gc));
719
720        self.upcast::<Element>()
721            .set_state(ElementState::VALID, !is_any_invalid);
722        self.upcast::<Element>()
723            .set_state(ElementState::INVALID, is_any_invalid);
724    }
725
726    /// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit)
727    pub(crate) fn submit(
728        &self,
729        submit_method_flag: SubmittedFrom,
730        submitter: FormSubmitterElement,
731        can_gc: CanGc,
732    ) {
733        // Step 1
734        if self.upcast::<Element>().cannot_navigate() {
735            return;
736        }
737
738        // Step 2
739        if self.constructing_entry_list.get() {
740            return;
741        }
742        // Step 3
743        let doc = self.owner_document();
744        let base = doc.base_url();
745        // TODO: Handle browsing contexts (Step 4, 5)
746        // Step 6
747        if submit_method_flag == SubmittedFrom::NotFromForm {
748            // Step 6.1
749            if self.firing_submission_events.get() {
750                return;
751            }
752            // Step 6.2
753            self.firing_submission_events.set(true);
754            // Step 6.3
755            if !submitter.no_validate(self) && self.interactive_validation(can_gc).is_err() {
756                self.firing_submission_events.set(false);
757                return;
758            }
759            // Step 6.4
760            // spec calls this "submitterButton" but it doesn't have to be a button,
761            // just not be the form itself
762            let submitter_button = match submitter {
763                FormSubmitterElement::Form(f) => {
764                    if f == self {
765                        None
766                    } else {
767                        Some(f.upcast::<HTMLElement>())
768                    }
769                },
770                FormSubmitterElement::Input(i) => Some(i.upcast::<HTMLElement>()),
771                FormSubmitterElement::Button(b) => Some(b.upcast::<HTMLElement>()),
772            };
773
774            // Step 6.5
775            let event = SubmitEvent::new(
776                self.global().as_window(),
777                atom!("submit"),
778                true,
779                true,
780                submitter_button.map(DomRoot::from_ref),
781                can_gc,
782            );
783            let event = event.upcast::<Event>();
784            event.fire(self.upcast::<EventTarget>(), can_gc);
785
786            // Step 6.6
787            self.firing_submission_events.set(false);
788            // Step 6.7
789            if event.DefaultPrevented() {
790                return;
791            }
792            // Step 6.8
793            if self.upcast::<Element>().cannot_navigate() {
794                return;
795            }
796        }
797
798        // Step 7
799        let encoding = self.pick_encoding();
800
801        // Step 8
802        let mut form_data = match self.get_form_dataset(Some(submitter), Some(encoding), can_gc) {
803            Some(form_data) => form_data,
804            None => return,
805        };
806
807        // Step 9
808        if self.upcast::<Element>().cannot_navigate() {
809            return;
810        }
811
812        // Step 10
813        let mut action = submitter.action();
814
815        // Step 11
816        if action.is_empty() {
817            action = DOMString::from(base.as_str());
818        }
819        // Step 12-13
820        let action_components = match base.join(&action) {
821            Ok(url) => url,
822            Err(_) => return,
823        };
824        // Step 14-16
825        let scheme = action_components.scheme().to_owned();
826        let enctype = submitter.enctype();
827        let method = submitter.method();
828
829        // Step 17
830        let target_attribute_value =
831            if submitter.is_submit_button() && submitter.target() != DOMString::new() {
832                Some(submitter.target())
833            } else {
834                let form_owner = submitter.form_owner();
835                let form = form_owner.as_deref().unwrap_or(self);
836                get_element_target(form.upcast::<Element>())
837            };
838
839        // Step 18
840        let noopener = self
841            .relations
842            .get()
843            .get_element_noopener(target_attribute_value.as_ref());
844
845        // Step 19
846        let source = doc.browsing_context().unwrap();
847        let (maybe_chosen, _new) = source
848            .choose_browsing_context(target_attribute_value.unwrap_or(DOMString::new()), noopener);
849
850        // Step 20
851        let chosen = match maybe_chosen {
852            Some(proxy) => proxy,
853            None => return,
854        };
855        let target_document = match chosen.document() {
856            Some(doc) => doc,
857            None => return,
858        };
859        // Step 21
860        let target_window = target_document.window();
861        let mut load_data = LoadData::new(
862            LoadOrigin::Script(doc.origin().immutable().clone()),
863            action_components,
864            None,
865            target_window.as_global_scope().get_referrer(),
866            target_document.get_referrer_policy(),
867            Some(target_window.as_global_scope().is_secure_context()),
868            Some(target_document.insecure_requests_policy()),
869            target_document.has_trustworthy_ancestor_origin(),
870        );
871
872        // Step 22
873        match (&*scheme, method) {
874            (_, FormMethod::Dialog) => {
875                // TODO: Submit dialog
876                // https://html.spec.whatwg.org/multipage/#submit-dialog
877            },
878            // https://html.spec.whatwg.org/multipage/#submit-mutate-action
879            ("http", FormMethod::Get) | ("https", FormMethod::Get) | ("data", FormMethod::Get) => {
880                load_data
881                    .headers
882                    .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
883                self.mutate_action_url(&mut form_data, load_data, encoding, target_window);
884            },
885            // https://html.spec.whatwg.org/multipage/#submit-body
886            ("http", FormMethod::Post) | ("https", FormMethod::Post) => {
887                load_data.method = Method::POST;
888                self.submit_entity_body(
889                    &mut form_data,
890                    load_data,
891                    enctype,
892                    encoding,
893                    target_window,
894                    can_gc,
895                );
896            },
897            // https://html.spec.whatwg.org/multipage/#submit-get-action
898            ("file", _) |
899            ("about", _) |
900            ("data", FormMethod::Post) |
901            ("ftp", _) |
902            ("javascript", _) => {
903                self.plan_to_navigate(load_data, target_window);
904            },
905            ("mailto", FormMethod::Post) => {
906                // TODO: Mail as body
907                // https://html.spec.whatwg.org/multipage/#submit-mailto-body
908            },
909            ("mailto", FormMethod::Get) => {
910                // TODO: Mail with headers
911                // https://html.spec.whatwg.org/multipage/#submit-mailto-headers
912            },
913            _ => (),
914        }
915    }
916
917    // https://html.spec.whatwg.org/multipage/#submit-mutate-action
918    fn mutate_action_url(
919        &self,
920        form_data: &mut [FormDatum],
921        mut load_data: LoadData,
922        encoding: &'static Encoding,
923        target: &Window,
924    ) {
925        let charset = encoding.name();
926
927        self.set_url_query_pairs(
928            &mut load_data.url,
929            form_data
930                .iter()
931                .map(|field| (&*field.name, field.replace_value(charset))),
932        );
933
934        self.plan_to_navigate(load_data, target);
935    }
936
937    // https://html.spec.whatwg.org/multipage/#submit-body
938    fn submit_entity_body(
939        &self,
940        form_data: &mut [FormDatum],
941        mut load_data: LoadData,
942        enctype: FormEncType,
943        encoding: &'static Encoding,
944        target: &Window,
945        can_gc: CanGc,
946    ) {
947        let boundary = generate_boundary();
948        let bytes = match enctype {
949            FormEncType::UrlEncoded => {
950                let charset = encoding.name();
951                load_data
952                    .headers
953                    .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
954
955                let mut url = load_data.url.clone();
956                self.set_url_query_pairs(
957                    &mut url,
958                    form_data
959                        .iter()
960                        .map(|field| (&*field.name, field.replace_value(charset))),
961                );
962
963                url.query().unwrap_or("").to_string().into_bytes()
964            },
965            FormEncType::MultipartFormData => {
966                let mime: Mime = format!("multipart/form-data; boundary={}", boundary)
967                    .parse()
968                    .unwrap();
969                load_data.headers.typed_insert(ContentType::from(mime));
970                encode_multipart_form_data(form_data, boundary, encoding)
971            },
972            FormEncType::TextPlain => {
973                load_data
974                    .headers
975                    .typed_insert(ContentType::from(mime::TEXT_PLAIN));
976                self.encode_plaintext(form_data).into_bytes()
977            },
978        };
979
980        let global = self.global();
981
982        let request_body = bytes
983            .extract(&global, can_gc)
984            .expect("Couldn't extract body.")
985            .into_net_request_body()
986            .0;
987        load_data.data = Some(request_body);
988
989        self.plan_to_navigate(load_data, target);
990    }
991
992    fn set_url_query_pairs<'a>(
993        &self,
994        url: &mut servo_url::ServoUrl,
995        pairs: impl Iterator<Item = (&'a str, String)>,
996    ) {
997        let encoding = self.pick_encoding();
998        url.as_mut_url()
999            .query_pairs_mut()
1000            .encoding_override(Some(&|s| encoding.encode(s).0))
1001            .clear()
1002            .extend_pairs(pairs);
1003    }
1004
1005    /// [Planned navigation](https://html.spec.whatwg.org/multipage/#planned-navigation)
1006    fn plan_to_navigate(&self, mut load_data: LoadData, target: &Window) {
1007        // 1. Let referrerPolicy be the empty string.
1008        // 2. If the form element's link types include the noreferrer keyword,
1009        //    then set referrerPolicy to "no-referrer".
1010        // Note: both steps done below.
1011        let elem = self.upcast::<Element>();
1012        let referrer = match elem.get_attribute(&ns!(), &local_name!("rel")) {
1013            Some(ref link_types) if link_types.Value().contains("noreferrer") => {
1014                Referrer::NoReferrer
1015            },
1016            _ => target.as_global_scope().get_referrer(),
1017        };
1018
1019        // 3. If the form has a non-null planned navigation, remove it from its task queue.
1020        // Note: done by incrementing `planned_navigation`.
1021        self.planned_navigation
1022            .set(self.planned_navigation.get().wrapping_add(1));
1023        let planned_navigation = self.planned_navigation.get();
1024
1025        // Note: we start to use
1026        // the beginnings of an `ongoing_navigation` concept,
1027        // to cancel planned navigations as part of
1028        // <https://html.spec.whatwg.org/multipage/#nav-stop>
1029        //
1030        // The concept of ongoing navigation must be separated from the form's
1031        // planned navigation concept, because each planned navigation cancels the previous one
1032        // for a given form, whereas an ongoing navigation is a per navigable (read: window for now)
1033        // concept.
1034        //
1035        // Setting the ongoing navigation now means the navigation could be cancelled
1036        // even if the below task has not run yet. This is not how the spec is written: it
1037        // seems instead to imply that a `window.stop` should only cancel the navigation
1038        // that has already started (here the task is queued, but the navigation starts only
1039        // in the task). See <https://github.com/whatwg/html/issues/11562>.
1040        let ongoing_navigation = target.set_ongoing_navigation();
1041
1042        let referrer_policy = target.Document().get_referrer_policy();
1043        load_data.creator_pipeline_id = Some(target.pipeline_id());
1044        load_data.referrer = referrer;
1045        load_data.referrer_policy = referrer_policy;
1046
1047        // 4. Queue an element task on the DOM manipulation task source
1048        // given the form element and the following steps:
1049        let form = Trusted::new(self);
1050        let window = Trusted::new(target);
1051        let task = task!(navigate_to_form_planned_navigation: move || {
1052            // 4.1 Set the form's planned navigation to null.
1053            // Note: we implement the equivalent by incrementing the counter above,
1054            // and checking it here.
1055            if planned_navigation != form.root().planned_navigation.get() {
1056                return;
1057            }
1058
1059            // Note: we also check if the navigation has been cancelled,
1060            // see https://github.com/whatwg/html/issues/11562
1061            if ongoing_navigation != window.root().ongoing_navigation() {
1062                return;
1063            }
1064
1065            // 4.2 Navigate targetNavigable to url
1066            window
1067                .root()
1068                .load_url(
1069                    NavigationHistoryBehavior::Push,
1070                    false,
1071                    load_data,
1072                    CanGc::note(),
1073                );
1074        });
1075
1076        // 5. Set the form's planned navigation to the just-queued task.
1077        // Done above as part of incrementing the planned navigation counter.
1078
1079        // Note: task queued here.
1080        target
1081            .global()
1082            .task_manager()
1083            .dom_manipulation_task_source()
1084            .queue(task)
1085    }
1086
1087    /// Interactively validate the constraints of form elements
1088    /// <https://html.spec.whatwg.org/multipage/#interactively-validate-the-constraints>
1089    fn interactive_validation(&self, can_gc: CanGc) -> Result<(), ()> {
1090        // Step 1-2
1091        let unhandled_invalid_controls = match self.static_validation(can_gc) {
1092            Ok(()) => return Ok(()),
1093            Err(err) => err,
1094        };
1095
1096        // Step 3
1097        let mut first = true;
1098
1099        for elem in unhandled_invalid_controls {
1100            if let Some(validatable) = elem.as_maybe_validatable() {
1101                println!("Validation error: {}", validatable.validation_message());
1102            }
1103            if first {
1104                if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1105                    // TODO: "Focusing steps" has a different meaning from the focus() method.
1106                    // The actual focusing steps should be implemented
1107                    html_elem.Focus(&FocusOptions::default(), can_gc);
1108                    first = false;
1109                }
1110            }
1111        }
1112
1113        // If it's form-associated and has a validation anchor, point the
1114        //  user there instead of the element itself.
1115        // Step 4
1116        Err(())
1117    }
1118
1119    /// Statitically validate the constraints of form elements
1120    /// <https://html.spec.whatwg.org/multipage/#statically-validate-the-constraints>
1121    fn static_validation(&self, can_gc: CanGc) -> Result<(), Vec<DomRoot<Element>>> {
1122        // Step 1-3
1123        let invalid_controls = self
1124            .controls
1125            .borrow()
1126            .iter()
1127            .filter_map(|field| {
1128                if let Some(element) = field.downcast::<Element>() {
1129                    if element.is_invalid(true, can_gc) {
1130                        Some(DomRoot::from_ref(element))
1131                    } else {
1132                        None
1133                    }
1134                } else {
1135                    None
1136                }
1137            })
1138            .collect::<Vec<DomRoot<Element>>>();
1139        // Step 4
1140        if invalid_controls.is_empty() {
1141            return Ok(());
1142        }
1143        // Step 5-6
1144        let unhandled_invalid_controls = invalid_controls
1145            .into_iter()
1146            .filter_map(|field| {
1147                let event = field
1148                    .upcast::<EventTarget>()
1149                    .fire_cancelable_event(atom!("invalid"), can_gc);
1150                if !event.DefaultPrevented() {
1151                    return Some(field);
1152                }
1153                None
1154            })
1155            .collect::<Vec<DomRoot<Element>>>();
1156        // Step 7
1157        Err(unhandled_invalid_controls)
1158    }
1159
1160    /// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set>
1161    /// terminology note:  "form data set" = "entry list"
1162    /// Steps range from 3 to 5
1163    /// 5.x substeps are mostly handled inside element-specific methods
1164    fn get_unclean_dataset(
1165        &self,
1166        submitter: Option<FormSubmitterElement>,
1167        encoding: Option<&'static Encoding>,
1168        can_gc: CanGc,
1169    ) -> Vec<FormDatum> {
1170        let mut data_set = Vec::new();
1171        for child in self.controls.borrow().iter() {
1172            // Step 5.1: The field element is disabled.
1173            if child.disabled_state() {
1174                continue;
1175            }
1176            let child = child.upcast::<Node>();
1177
1178            // Step 5.1: The field element has a datalist element ancestor.
1179            if child.ancestors().any(|a| a.is::<HTMLDataListElement>()) {
1180                continue;
1181            }
1182            if let NodeTypeId::Element(ElementTypeId::HTMLElement(element)) = child.type_id() {
1183                match element {
1184                    HTMLElementTypeId::HTMLInputElement => {
1185                        let input = child.downcast::<HTMLInputElement>().unwrap();
1186                        data_set.append(&mut input.form_datums(submitter, encoding));
1187                    },
1188                    HTMLElementTypeId::HTMLButtonElement => {
1189                        let button = child.downcast::<HTMLButtonElement>().unwrap();
1190                        if let Some(datum) = button.form_datum(submitter) {
1191                            data_set.push(datum);
1192                        }
1193                    },
1194                    HTMLElementTypeId::HTMLObjectElement => {
1195                        // Unimplemented
1196                    },
1197                    HTMLElementTypeId::HTMLSelectElement => {
1198                        let select = child.downcast::<HTMLSelectElement>().unwrap();
1199                        select.push_form_data(&mut data_set);
1200                    },
1201                    HTMLElementTypeId::HTMLTextAreaElement => {
1202                        let textarea = child.downcast::<HTMLTextAreaElement>().unwrap();
1203                        let name = textarea.Name();
1204                        if !name.is_empty() {
1205                            data_set.push(FormDatum {
1206                                ty: textarea.Type(),
1207                                name,
1208                                value: FormDatumValue::String(textarea.Value()),
1209                            });
1210                        }
1211                    },
1212                    HTMLElementTypeId::HTMLElement => {
1213                        let custom = child.downcast::<HTMLElement>().unwrap();
1214                        if custom.is_form_associated_custom_element() {
1215                            // https://html.spec.whatwg.org/multipage/#face-entry-construction
1216                            let internals =
1217                                custom.upcast::<Element>().ensure_element_internals(can_gc);
1218                            internals.perform_entry_construction(&mut data_set);
1219                            // Otherwise no form value has been set so there is nothing to do.
1220                        }
1221                    },
1222                    _ => (),
1223                }
1224            }
1225
1226            // Step: 5.13. Add an entry if element has dirname attribute
1227            // An element can only have a dirname attribute if it is a textarea element
1228            // or an input element whose type attribute is in either the Text state or the Search state
1229            let child_element = child.downcast::<Element>().unwrap();
1230            let input_matches = child_element
1231                .downcast::<HTMLInputElement>()
1232                .is_some_and(|input| {
1233                    matches!(input.input_type(), InputType::Text | InputType::Search)
1234                });
1235            let textarea_matches = child_element.is::<HTMLTextAreaElement>();
1236            let dirname = child_element.get_string_attribute(&local_name!("dirname"));
1237            if (input_matches || textarea_matches) && !dirname.is_empty() {
1238                let dir = DOMString::from(child_element.directionality());
1239                data_set.push(FormDatum {
1240                    ty: DOMString::from("string"),
1241                    name: dirname,
1242                    value: FormDatumValue::String(dir),
1243                });
1244            }
1245        }
1246        data_set
1247    }
1248
1249    /// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set>
1250    pub(crate) fn get_form_dataset(
1251        &self,
1252        submitter: Option<FormSubmitterElement>,
1253        encoding: Option<&'static Encoding>,
1254        can_gc: CanGc,
1255    ) -> Option<Vec<FormDatum>> {
1256        // Step 1
1257        if self.constructing_entry_list.get() {
1258            return None;
1259        }
1260
1261        // Step 2
1262        self.constructing_entry_list.set(true);
1263
1264        // Step 3-6
1265        let ret = self.get_unclean_dataset(submitter, encoding, can_gc);
1266
1267        let window = self.owner_window();
1268
1269        // Step 6
1270        let form_data = FormData::new(Some(ret), &window.global(), can_gc);
1271
1272        // Step 7
1273        let event = FormDataEvent::new(
1274            &window,
1275            atom!("formdata"),
1276            EventBubbles::Bubbles,
1277            EventCancelable::NotCancelable,
1278            &form_data,
1279            can_gc,
1280        );
1281
1282        event
1283            .upcast::<Event>()
1284            .fire(self.upcast::<EventTarget>(), can_gc);
1285
1286        // Step 8
1287        self.constructing_entry_list.set(false);
1288
1289        // Step 9
1290        Some(form_data.datums())
1291    }
1292
1293    pub(crate) fn reset(&self, _reset_method_flag: ResetFrom, can_gc: CanGc) {
1294        // https://html.spec.whatwg.org/multipage/#locked-for-reset
1295        if self.marked_for_reset.get() {
1296            return;
1297        } else {
1298            self.marked_for_reset.set(true);
1299        }
1300
1301        let event = self
1302            .upcast::<EventTarget>()
1303            .fire_bubbling_cancelable_event(atom!("reset"), can_gc);
1304        if event.DefaultPrevented() {
1305            return;
1306        }
1307
1308        let controls: Vec<_> = self
1309            .controls
1310            .borrow()
1311            .iter()
1312            .map(|c| c.as_rooted())
1313            .collect();
1314
1315        for child in controls {
1316            let child = child.upcast::<Node>();
1317
1318            match child.type_id() {
1319                NodeTypeId::Element(ElementTypeId::HTMLElement(
1320                    HTMLElementTypeId::HTMLInputElement,
1321                )) => {
1322                    child.downcast::<HTMLInputElement>().unwrap().reset(can_gc);
1323                },
1324                NodeTypeId::Element(ElementTypeId::HTMLElement(
1325                    HTMLElementTypeId::HTMLSelectElement,
1326                )) => {
1327                    child.downcast::<HTMLSelectElement>().unwrap().reset();
1328                },
1329                NodeTypeId::Element(ElementTypeId::HTMLElement(
1330                    HTMLElementTypeId::HTMLTextAreaElement,
1331                )) => {
1332                    child.downcast::<HTMLTextAreaElement>().unwrap().reset();
1333                },
1334                NodeTypeId::Element(ElementTypeId::HTMLElement(
1335                    HTMLElementTypeId::HTMLOutputElement,
1336                )) => {
1337                    child.downcast::<HTMLOutputElement>().unwrap().reset(can_gc);
1338                },
1339                NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1340                    let html_element = child.downcast::<HTMLElement>().unwrap();
1341                    if html_element.is_form_associated_custom_element() {
1342                        ScriptThread::enqueue_callback_reaction(
1343                            html_element.upcast::<Element>(),
1344                            CallbackReaction::FormReset,
1345                            None,
1346                        )
1347                    }
1348                },
1349                _ => {},
1350            }
1351        }
1352        self.marked_for_reset.set(false);
1353    }
1354
1355    fn add_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1356        {
1357            let root = self.upcast::<Element>().root_element();
1358            let root = root.upcast::<Node>();
1359            let mut controls = self.controls.borrow_mut();
1360            controls.insert_pre_order(control.to_element(), root);
1361        }
1362        self.update_validity(can_gc);
1363    }
1364
1365    fn remove_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1366        {
1367            let control = control.to_element();
1368            let mut controls = self.controls.borrow_mut();
1369            controls
1370                .iter()
1371                .position(|c| &**c == control)
1372                .map(|idx| controls.remove(idx));
1373
1374            // https://html.spec.whatwg.org/multipage#forms.html#the-form-element:past-names-map-5
1375            // "If an element listed in a form element's past names map
1376            // changes form owner, then its entries must be removed
1377            // from that map."
1378            let mut past_names_map = self.past_names_map.borrow_mut();
1379            past_names_map.0.retain(|_k, v| v.0 != control);
1380        }
1381        self.update_validity(can_gc);
1382    }
1383}
1384
1385#[derive(Clone, JSTraceable, MallocSizeOf)]
1386pub(crate) enum FormDatumValue {
1387    #[allow(dead_code)]
1388    File(DomRoot<File>),
1389    String(DOMString),
1390}
1391
1392#[derive(Clone, JSTraceable, MallocSizeOf)]
1393pub(crate) struct FormDatum {
1394    pub(crate) ty: DOMString,
1395    pub(crate) name: DOMString,
1396    pub(crate) value: FormDatumValue,
1397}
1398
1399impl FormDatum {
1400    pub(crate) fn replace_value(&self, charset: &str) -> String {
1401        if self.name.to_ascii_lowercase() == "_charset_" && self.ty == "hidden" {
1402            return charset.to_string();
1403        }
1404
1405        match self.value {
1406            FormDatumValue::File(ref f) => String::from(f.name().clone()),
1407            FormDatumValue::String(ref s) => String::from(s.clone()),
1408        }
1409    }
1410}
1411
1412#[derive(Clone, Copy, MallocSizeOf)]
1413pub(crate) enum FormEncType {
1414    TextPlain,
1415    UrlEncoded,
1416    MultipartFormData,
1417}
1418
1419#[derive(Clone, Copy, MallocSizeOf)]
1420pub(crate) enum FormMethod {
1421    Get,
1422    Post,
1423    Dialog,
1424}
1425
1426/// <https://html.spec.whatwg.org/multipage/#form-associated-element>
1427#[derive(Clone, Copy, MallocSizeOf)]
1428pub(crate) enum FormSubmitterElement<'a> {
1429    Form(&'a HTMLFormElement),
1430    Input(&'a HTMLInputElement),
1431    Button(&'a HTMLButtonElement),
1432    // TODO: implement other types of form associated elements
1433    // (including custom elements) that can be passed as submitter.
1434}
1435
1436impl FormSubmitterElement<'_> {
1437    fn action(&self) -> DOMString {
1438        match *self {
1439            FormSubmitterElement::Form(form) => form.Action(),
1440            FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1441                &local_name!("formaction"),
1442                |i| i.FormAction(),
1443                |f| f.Action(),
1444            ),
1445            FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1446                &local_name!("formaction"),
1447                |i| i.FormAction(),
1448                |f| f.Action(),
1449            ),
1450        }
1451    }
1452
1453    fn enctype(&self) -> FormEncType {
1454        let attr = match *self {
1455            FormSubmitterElement::Form(form) => form.Enctype(),
1456            FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1457                &local_name!("formenctype"),
1458                |i| i.FormEnctype(),
1459                |f| f.Enctype(),
1460            ),
1461            FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1462                &local_name!("formenctype"),
1463                |i| i.FormEnctype(),
1464                |f| f.Enctype(),
1465            ),
1466        };
1467        match &*attr {
1468            "multipart/form-data" => FormEncType::MultipartFormData,
1469            "text/plain" => FormEncType::TextPlain,
1470            // https://html.spec.whatwg.org/multipage/#attr-fs-enctype
1471            // urlencoded is the default
1472            _ => FormEncType::UrlEncoded,
1473        }
1474    }
1475
1476    fn method(&self) -> FormMethod {
1477        let attr = match *self {
1478            FormSubmitterElement::Form(form) => form.Method(),
1479            FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1480                &local_name!("formmethod"),
1481                |i| i.FormMethod(),
1482                |f| f.Method(),
1483            ),
1484            FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1485                &local_name!("formmethod"),
1486                |i| i.FormMethod(),
1487                |f| f.Method(),
1488            ),
1489        };
1490        match &*attr {
1491            "dialog" => FormMethod::Dialog,
1492            "post" => FormMethod::Post,
1493            _ => FormMethod::Get,
1494        }
1495    }
1496
1497    fn target(&self) -> DOMString {
1498        match *self {
1499            FormSubmitterElement::Form(form) => form.Target(),
1500            FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1501                &local_name!("formtarget"),
1502                |i| i.FormTarget(),
1503                |f| f.Target(),
1504            ),
1505            FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1506                &local_name!("formtarget"),
1507                |i| i.FormTarget(),
1508                |f| f.Target(),
1509            ),
1510        }
1511    }
1512
1513    fn no_validate(&self, _form_owner: &HTMLFormElement) -> bool {
1514        match *self {
1515            FormSubmitterElement::Form(form) => form.NoValidate(),
1516            FormSubmitterElement::Input(input_element) => input_element.get_form_boolean_attribute(
1517                &local_name!("formnovalidate"),
1518                |i| i.FormNoValidate(),
1519                |f| f.NoValidate(),
1520            ),
1521            FormSubmitterElement::Button(button_element) => button_element
1522                .get_form_boolean_attribute(
1523                    &local_name!("formnovalidate"),
1524                    |i| i.FormNoValidate(),
1525                    |f| f.NoValidate(),
1526                ),
1527        }
1528    }
1529
1530    // https://html.spec.whatwg.org/multipage/#concept-submit-button
1531    pub(crate) fn is_submit_button(&self) -> bool {
1532        match *self {
1533            // https://html.spec.whatwg.org/multipage/#image-button-state-(type=image)
1534            // https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit)
1535            FormSubmitterElement::Input(input_element) => input_element.is_submit_button(),
1536            // https://html.spec.whatwg.org/multipage/#attr-button-type-submit-state
1537            FormSubmitterElement::Button(button_element) => button_element.is_submit_button(),
1538            _ => false,
1539        }
1540    }
1541
1542    // https://html.spec.whatwg.org/multipage/#form-owner
1543    pub(crate) fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1544        match *self {
1545            FormSubmitterElement::Button(button_el) => button_el.form_owner(),
1546            FormSubmitterElement::Input(input_el) => input_el.form_owner(),
1547            _ => None,
1548        }
1549    }
1550}
1551
1552pub(crate) trait FormControl: DomObject {
1553    fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>>;
1554
1555    fn set_form_owner(&self, form: Option<&HTMLFormElement>);
1556
1557    fn to_element(&self) -> &Element;
1558
1559    fn is_listed(&self) -> bool {
1560        true
1561    }
1562
1563    // https://html.spec.whatwg.org/multipage/#create-an-element-for-the-token
1564    // Part of step 12.
1565    // '..suppress the running of the reset the form owner algorithm
1566    // when the parser subsequently attempts to insert the element..'
1567    fn set_form_owner_from_parser(&self, form: &HTMLFormElement, can_gc: CanGc) {
1568        let elem = self.to_element();
1569        let node = elem.upcast::<Node>();
1570        node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, true);
1571        form.add_control(self, can_gc);
1572        self.set_form_owner(Some(form));
1573    }
1574
1575    // https://html.spec.whatwg.org/multipage/#reset-the-form-owner
1576    fn reset_form_owner(&self, can_gc: CanGc) {
1577        let elem = self.to_element();
1578        let node = elem.upcast::<Node>();
1579        let old_owner = self.form_owner();
1580        let has_form_id = elem.has_attribute(&local_name!("form"));
1581        let nearest_form_ancestor = node
1582            .ancestors()
1583            .filter_map(DomRoot::downcast::<HTMLFormElement>)
1584            .next();
1585
1586        // Step 1
1587        if old_owner.is_some() &&
1588            !(self.is_listed() && has_form_id) &&
1589            nearest_form_ancestor == old_owner
1590        {
1591            return;
1592        }
1593
1594        let new_owner = if self.is_listed() && has_form_id && elem.is_connected() {
1595            // Step 3
1596            let doc = node.owner_document();
1597            let form_id = elem.get_string_attribute(&local_name!("form"));
1598            doc.GetElementById(form_id)
1599                .and_then(DomRoot::downcast::<HTMLFormElement>)
1600        } else {
1601            // Step 4
1602            nearest_form_ancestor
1603        };
1604
1605        if old_owner != new_owner {
1606            if let Some(o) = old_owner {
1607                o.remove_control(self, can_gc);
1608            }
1609            if let Some(ref new_owner) = new_owner {
1610                new_owner.add_control(self, can_gc);
1611            }
1612            // https://html.spec.whatwg.org/multipage/#custom-element-reactions:reset-the-form-owner
1613            if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1614                if html_elem.is_form_associated_custom_element() {
1615                    ScriptThread::enqueue_callback_reaction(
1616                        elem,
1617                        CallbackReaction::FormAssociated(
1618                            new_owner.as_ref().map(|form| DomRoot::from_ref(&**form)),
1619                        ),
1620                        None,
1621                    )
1622                }
1623            }
1624            self.set_form_owner(new_owner.as_deref());
1625        }
1626    }
1627
1628    // https://html.spec.whatwg.org/multipage/#association-of-controls-and-forms
1629    fn form_attribute_mutated(&self, mutation: AttributeMutation, can_gc: CanGc) {
1630        match mutation {
1631            AttributeMutation::Set(_) => {
1632                self.register_if_necessary();
1633            },
1634            AttributeMutation::Removed => {
1635                self.unregister_if_necessary();
1636            },
1637        }
1638
1639        self.reset_form_owner(can_gc);
1640    }
1641
1642    // https://html.spec.whatwg.org/multipage/#association-of-controls-and-forms
1643    fn register_if_necessary(&self) {
1644        let elem = self.to_element();
1645        let form_id = elem.get_string_attribute(&local_name!("form"));
1646        let node = elem.upcast::<Node>();
1647
1648        if self.is_listed() && !form_id.is_empty() && node.is_connected() {
1649            node.owner_document()
1650                .register_form_id_listener(form_id, self);
1651        }
1652    }
1653
1654    fn unregister_if_necessary(&self) {
1655        let elem = self.to_element();
1656        let form_id = elem.get_string_attribute(&local_name!("form"));
1657
1658        if self.is_listed() && !form_id.is_empty() {
1659            elem.owner_document()
1660                .unregister_form_id_listener(form_id, self);
1661        }
1662    }
1663
1664    // https://html.spec.whatwg.org/multipage/#association-of-controls-and-forms
1665    fn bind_form_control_to_tree(&self, can_gc: CanGc) {
1666        let elem = self.to_element();
1667        let node = elem.upcast::<Node>();
1668
1669        // https://html.spec.whatwg.org/multipage/#create-an-element-for-the-token
1670        // Part of step 12.
1671        // '..suppress the running of the reset the form owner algorithm
1672        // when the parser subsequently attempts to insert the element..'
1673        let must_skip_reset = node.get_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER);
1674        node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, false);
1675
1676        if !must_skip_reset {
1677            self.form_attribute_mutated(AttributeMutation::Set(None), can_gc);
1678        }
1679    }
1680
1681    // https://html.spec.whatwg.org/multipage/#association-of-controls-and-forms
1682    fn unbind_form_control_from_tree(&self, can_gc: CanGc) {
1683        let elem = self.to_element();
1684        let has_form_attr = elem.has_attribute(&local_name!("form"));
1685        let same_subtree = self
1686            .form_owner()
1687            .is_none_or(|form| elem.is_in_same_home_subtree(&*form));
1688
1689        self.unregister_if_necessary();
1690
1691        // Since this control has been unregistered from the id->listener map
1692        // in the previous step, reset_form_owner will not be invoked on it
1693        // when the form owner element is unbound (i.e it is in the same
1694        // subtree) if it appears later in the tree order. Hence invoke
1695        // reset from here if this control has the form attribute set.
1696        if !same_subtree || (self.is_listed() && has_form_attr) {
1697            self.reset_form_owner(can_gc);
1698        }
1699    }
1700
1701    fn get_form_attribute<InputFn, OwnerFn>(
1702        &self,
1703        attr: &LocalName,
1704        input: InputFn,
1705        owner: OwnerFn,
1706    ) -> DOMString
1707    where
1708        InputFn: Fn(&Self) -> DOMString,
1709        OwnerFn: Fn(&HTMLFormElement) -> DOMString,
1710        Self: Sized,
1711    {
1712        if self.to_element().has_attribute(attr) {
1713            input(self)
1714        } else {
1715            self.form_owner().map_or(DOMString::new(), |t| owner(&t))
1716        }
1717    }
1718
1719    fn get_form_boolean_attribute<InputFn, OwnerFn>(
1720        &self,
1721        attr: &LocalName,
1722        input: InputFn,
1723        owner: OwnerFn,
1724    ) -> bool
1725    where
1726        InputFn: Fn(&Self) -> bool,
1727        OwnerFn: Fn(&HTMLFormElement) -> bool,
1728        Self: Sized,
1729    {
1730        if self.to_element().has_attribute(attr) {
1731            input(self)
1732        } else {
1733            self.form_owner().is_some_and(|t| owner(&t))
1734        }
1735    }
1736
1737    /// <https://html.spec.whatwg.org/multipage/#candidate-for-constraint-validation>
1738    fn is_candidate_for_constraint_validation(&self) -> bool {
1739        let element = self.to_element();
1740        let html_element = element.downcast::<HTMLElement>();
1741        if let Some(html_element) = html_element {
1742            html_element.is_submittable_element() || element.is_instance_validatable()
1743        } else {
1744            false
1745        }
1746    }
1747
1748    // XXXKiChjang: Implement these on inheritors
1749    // fn satisfies_constraints(&self) -> bool;
1750}
1751
1752impl VirtualMethods for HTMLFormElement {
1753    fn super_type(&self) -> Option<&dyn VirtualMethods> {
1754        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1755    }
1756
1757    fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1758        self.super_type().unwrap().unbind_from_tree(context, can_gc);
1759
1760        // Collect the controls to reset because reset_form_owner
1761        // will mutably borrow self.controls
1762        rooted_vec!(let mut to_reset);
1763        to_reset.extend(
1764            self.controls
1765                .borrow()
1766                .iter()
1767                .filter(|c| !c.is_in_same_home_subtree(self))
1768                .cloned(),
1769        );
1770
1771        for control in to_reset.iter() {
1772            control
1773                .as_maybe_form_control()
1774                .expect("Element must be a form control")
1775                .reset_form_owner(can_gc);
1776        }
1777    }
1778
1779    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1780        match name {
1781            &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
1782            _ => self
1783                .super_type()
1784                .unwrap()
1785                .parse_plain_attribute(name, value),
1786        }
1787    }
1788
1789    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1790        self.super_type()
1791            .unwrap()
1792            .attribute_mutated(attr, mutation, can_gc);
1793
1794        match *attr.local_name() {
1795            local_name!("rel") | local_name!("rev") => {
1796                self.relations
1797                    .set(LinkRelations::for_element(self.upcast()));
1798            },
1799            _ => {},
1800        }
1801    }
1802
1803    fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1804        if let Some(s) = self.super_type() {
1805            s.bind_to_tree(context, can_gc);
1806        }
1807
1808        self.relations
1809            .set(LinkRelations::for_element(self.upcast()));
1810    }
1811}
1812
1813pub(crate) trait FormControlElementHelpers {
1814    fn as_maybe_form_control(&self) -> Option<&dyn FormControl>;
1815}
1816
1817impl FormControlElementHelpers for Element {
1818    fn as_maybe_form_control(&self) -> Option<&dyn FormControl> {
1819        let node = self.upcast::<Node>();
1820
1821        match node.type_id() {
1822            NodeTypeId::Element(ElementTypeId::HTMLElement(
1823                HTMLElementTypeId::HTMLButtonElement,
1824            )) => Some(self.downcast::<HTMLButtonElement>().unwrap() as &dyn FormControl),
1825            NodeTypeId::Element(ElementTypeId::HTMLElement(
1826                HTMLElementTypeId::HTMLFieldSetElement,
1827            )) => Some(self.downcast::<HTMLFieldSetElement>().unwrap() as &dyn FormControl),
1828            NodeTypeId::Element(ElementTypeId::HTMLElement(
1829                HTMLElementTypeId::HTMLImageElement,
1830            )) => Some(self.downcast::<HTMLImageElement>().unwrap() as &dyn FormControl),
1831            NodeTypeId::Element(ElementTypeId::HTMLElement(
1832                HTMLElementTypeId::HTMLInputElement,
1833            )) => Some(self.downcast::<HTMLInputElement>().unwrap() as &dyn FormControl),
1834            NodeTypeId::Element(ElementTypeId::HTMLElement(
1835                HTMLElementTypeId::HTMLLabelElement,
1836            )) => Some(self.downcast::<HTMLLabelElement>().unwrap() as &dyn FormControl),
1837            NodeTypeId::Element(ElementTypeId::HTMLElement(
1838                HTMLElementTypeId::HTMLLegendElement,
1839            )) => Some(self.downcast::<HTMLLegendElement>().unwrap() as &dyn FormControl),
1840            NodeTypeId::Element(ElementTypeId::HTMLElement(
1841                HTMLElementTypeId::HTMLObjectElement,
1842            )) => Some(self.downcast::<HTMLObjectElement>().unwrap() as &dyn FormControl),
1843            NodeTypeId::Element(ElementTypeId::HTMLElement(
1844                HTMLElementTypeId::HTMLOutputElement,
1845            )) => Some(self.downcast::<HTMLOutputElement>().unwrap() as &dyn FormControl),
1846            NodeTypeId::Element(ElementTypeId::HTMLElement(
1847                HTMLElementTypeId::HTMLSelectElement,
1848            )) => Some(self.downcast::<HTMLSelectElement>().unwrap() as &dyn FormControl),
1849            NodeTypeId::Element(ElementTypeId::HTMLElement(
1850                HTMLElementTypeId::HTMLTextAreaElement,
1851            )) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
1852            _ => self.downcast::<HTMLElement>().and_then(|elem| {
1853                if elem.is_form_associated_custom_element() {
1854                    Some(elem as &dyn FormControl)
1855                } else {
1856                    None
1857                }
1858            }),
1859        }
1860    }
1861}
1862
1863/// <https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm>
1864pub(crate) fn encode_multipart_form_data(
1865    form_data: &mut [FormDatum],
1866    boundary: String,
1867    encoding: &'static Encoding,
1868) -> Vec<u8> {
1869    let mut result = vec![];
1870
1871    // Newline replacement routine as described in Step 1
1872    fn clean_crlf(s: &str) -> DOMString {
1873        let mut buf = "".to_owned();
1874        let mut prev = ' ';
1875        for ch in s.chars() {
1876            match ch {
1877                '\n' if prev != '\r' => {
1878                    buf.push('\r');
1879                    buf.push('\n');
1880                },
1881                '\n' => {
1882                    buf.push('\n');
1883                },
1884                // This character isn't LF but is
1885                // preceded by CR
1886                _ if prev == '\r' => {
1887                    buf.push('\r');
1888                    buf.push('\n');
1889                    buf.push(ch);
1890                },
1891                _ => buf.push(ch),
1892            };
1893            prev = ch;
1894        }
1895        // In case the last character was CR
1896        if prev == '\r' {
1897            buf.push('\n');
1898        }
1899        DOMString::from(buf)
1900    }
1901
1902    for entry in form_data.iter_mut() {
1903        // Step 1.1: Perform newline replacement on entry's name
1904        entry.name = clean_crlf(&entry.name);
1905
1906        // Step 1.2: If entry's value is not a File object, perform newline replacement on entry's
1907        // value
1908        if let FormDatumValue::String(ref s) = entry.value {
1909            entry.value = FormDatumValue::String(clean_crlf(s));
1910        }
1911
1912        // Step 2: Return the byte sequence resulting from encoding the entry list.
1913        // https://tools.ietf.org/html/rfc7578#section-4
1914        // NOTE(izgzhen): The encoding here expected by most servers seems different from
1915        // what spec says (that it should start with a '\r\n').
1916        let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
1917        result.append(&mut boundary_bytes);
1918
1919        // TODO(eijebong): Everthing related to content-disposition it to redo once typed headers
1920        // are capable of it.
1921        match entry.value {
1922            FormDatumValue::String(ref s) => {
1923                let content_disposition = format!("form-data; name=\"{}\"", entry.name);
1924                let mut bytes =
1925                    format!("Content-Disposition: {}\r\n\r\n{}", content_disposition, s)
1926                        .into_bytes();
1927                result.append(&mut bytes);
1928            },
1929            FormDatumValue::File(ref f) => {
1930                let charset = encoding.name();
1931                let extra = if charset.to_lowercase() == "utf-8" {
1932                    format!(
1933                        "filename=\"{}\"",
1934                        String::from_utf8(f.name().as_bytes().into()).unwrap()
1935                    )
1936                } else {
1937                    format!(
1938                        "filename*=\"{}\"''{}",
1939                        charset,
1940                        http_percent_encode(f.name().as_bytes())
1941                    )
1942                };
1943
1944                let content_disposition = format!("form-data; name=\"{}\"; {}", entry.name, extra);
1945                // https://tools.ietf.org/html/rfc7578#section-4.4
1946                let content_type: Mime = f
1947                    .upcast::<Blob>()
1948                    .Type()
1949                    .parse()
1950                    .unwrap_or(mime::TEXT_PLAIN);
1951                let mut type_bytes = format!(
1952                    "Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n",
1953                    content_disposition, content_type
1954                )
1955                .into_bytes();
1956                result.append(&mut type_bytes);
1957
1958                let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
1959
1960                result.append(&mut bytes);
1961            },
1962        }
1963    }
1964
1965    let mut boundary_bytes = format!("\r\n--{}--\r\n", boundary).into_bytes();
1966    result.append(&mut boundary_bytes);
1967
1968    result
1969}
1970
1971// https://tools.ietf.org/html/rfc7578#section-4.1
1972pub(crate) fn generate_boundary() -> String {
1973    let i1 = random::<u32>();
1974    let i2 = random::<u32>();
1975
1976    format!("---------------------------{0}{1}", i1, i2)
1977}