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