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