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, AttributeMutationReason, 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 .find_map(DomRoot::downcast::<HTMLFormElement>);
1627
1628 if old_owner.is_some() &&
1630 !(self.is_listed() && has_form_id) &&
1631 nearest_form_ancestor == old_owner
1632 {
1633 return;
1634 }
1635
1636 let new_owner = if self.is_listed() && has_form_id && elem.is_connected() {
1637 let doc = node.owner_document();
1639 let form_id = elem.get_string_attribute(&local_name!("form"));
1640 doc.GetElementById(form_id)
1641 .and_then(DomRoot::downcast::<HTMLFormElement>)
1642 } else {
1643 nearest_form_ancestor
1645 };
1646
1647 if old_owner != new_owner {
1648 if let Some(o) = old_owner {
1649 o.remove_control(self, can_gc);
1650 }
1651 if let Some(ref new_owner) = new_owner {
1652 new_owner.add_control(self, can_gc);
1653 }
1654 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1656 if html_elem.is_form_associated_custom_element() {
1657 ScriptThread::enqueue_callback_reaction(
1658 elem,
1659 CallbackReaction::FormAssociated(
1660 new_owner.as_ref().map(|form| DomRoot::from_ref(&**form)),
1661 ),
1662 None,
1663 )
1664 }
1665 }
1666 self.set_form_owner(new_owner.as_deref());
1667 }
1668 }
1669
1670 fn form_attribute_mutated(&self, mutation: AttributeMutation, can_gc: CanGc) {
1672 match mutation {
1673 AttributeMutation::Set(..) => {
1674 self.register_if_necessary();
1675 },
1676 AttributeMutation::Removed => {
1677 self.unregister_if_necessary();
1678 },
1679 }
1680
1681 self.reset_form_owner(can_gc);
1682 }
1683
1684 fn register_if_necessary(&self) {
1686 let elem = self.to_element();
1687 let form_id = elem.get_string_attribute(&local_name!("form"));
1688 let node = elem.upcast::<Node>();
1689
1690 if self.is_listed() && !form_id.is_empty() && node.is_connected() {
1691 node.owner_document()
1692 .register_form_id_listener(form_id, self);
1693 }
1694 }
1695
1696 fn unregister_if_necessary(&self) {
1697 let elem = self.to_element();
1698 let form_id = elem.get_string_attribute(&local_name!("form"));
1699
1700 if self.is_listed() && !form_id.is_empty() {
1701 elem.owner_document()
1702 .unregister_form_id_listener(form_id, self);
1703 }
1704 }
1705
1706 fn bind_form_control_to_tree(&self, can_gc: CanGc) {
1708 let elem = self.to_element();
1709 let node = elem.upcast::<Node>();
1710
1711 let must_skip_reset = node.get_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER);
1716 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, false);
1717
1718 if !must_skip_reset {
1719 self.form_attribute_mutated(
1720 AttributeMutation::Set(None, AttributeMutationReason::Directly),
1721 can_gc,
1722 );
1723 }
1724 }
1725
1726 fn unbind_form_control_from_tree(&self, can_gc: CanGc) {
1728 let elem = self.to_element();
1729 let has_form_attr = elem.has_attribute(&local_name!("form"));
1730 let same_subtree = self
1731 .form_owner()
1732 .is_none_or(|form| elem.is_in_same_home_subtree(&*form));
1733
1734 self.unregister_if_necessary();
1735
1736 if !same_subtree || (self.is_listed() && has_form_attr) {
1742 self.reset_form_owner(can_gc);
1743 }
1744 }
1745
1746 fn get_form_attribute<InputFn, OwnerFn>(
1747 &self,
1748 attr: &LocalName,
1749 input: InputFn,
1750 owner: OwnerFn,
1751 ) -> DOMString
1752 where
1753 InputFn: Fn(&Self) -> DOMString,
1754 OwnerFn: Fn(&HTMLFormElement) -> DOMString,
1755 Self: Sized,
1756 {
1757 if self.to_element().has_attribute(attr) {
1758 input(self)
1759 } else {
1760 self.form_owner().map_or(DOMString::new(), |t| owner(&t))
1761 }
1762 }
1763
1764 fn get_form_boolean_attribute<InputFn, OwnerFn>(
1765 &self,
1766 attr: &LocalName,
1767 input: InputFn,
1768 owner: OwnerFn,
1769 ) -> bool
1770 where
1771 InputFn: Fn(&Self) -> bool,
1772 OwnerFn: Fn(&HTMLFormElement) -> bool,
1773 Self: Sized,
1774 {
1775 if self.to_element().has_attribute(attr) {
1776 input(self)
1777 } else {
1778 self.form_owner().is_some_and(|t| owner(&t))
1779 }
1780 }
1781
1782 fn is_candidate_for_constraint_validation(&self) -> bool {
1784 let element = self.to_element();
1785 let html_element = element.downcast::<HTMLElement>();
1786 if let Some(html_element) = html_element {
1787 html_element.is_submittable_element() || element.is_instance_validatable()
1788 } else {
1789 false
1790 }
1791 }
1792
1793 }
1796
1797impl VirtualMethods for HTMLFormElement {
1798 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1799 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1800 }
1801
1802 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1803 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1804
1805 rooted_vec!(let mut to_reset);
1808 to_reset.extend(
1809 self.controls
1810 .borrow()
1811 .iter()
1812 .filter(|c| !c.is_in_same_home_subtree(self))
1813 .cloned(),
1814 );
1815
1816 for control in to_reset.iter() {
1817 control
1818 .as_maybe_form_control()
1819 .expect("Element must be a form control")
1820 .reset_form_owner(can_gc);
1821 }
1822 }
1823
1824 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1825 match name {
1826 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
1827 _ => self
1828 .super_type()
1829 .unwrap()
1830 .parse_plain_attribute(name, value),
1831 }
1832 }
1833
1834 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1835 self.super_type()
1836 .unwrap()
1837 .attribute_mutated(attr, mutation, can_gc);
1838
1839 match *attr.local_name() {
1840 local_name!("rel") | local_name!("rev") => {
1841 self.relations
1842 .set(LinkRelations::for_element(self.upcast()));
1843 },
1844 _ => {},
1845 }
1846 }
1847
1848 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1849 if let Some(s) = self.super_type() {
1850 s.bind_to_tree(context, can_gc);
1851 }
1852
1853 self.relations
1854 .set(LinkRelations::for_element(self.upcast()));
1855 }
1856}
1857
1858pub(crate) trait FormControlElementHelpers {
1859 fn as_maybe_form_control(&self) -> Option<&dyn FormControl>;
1860}
1861
1862impl FormControlElementHelpers for Element {
1863 fn as_maybe_form_control(&self) -> Option<&dyn FormControl> {
1864 let node = self.upcast::<Node>();
1865
1866 match node.type_id() {
1867 NodeTypeId::Element(ElementTypeId::HTMLElement(
1868 HTMLElementTypeId::HTMLButtonElement,
1869 )) => Some(self.downcast::<HTMLButtonElement>().unwrap() as &dyn FormControl),
1870 NodeTypeId::Element(ElementTypeId::HTMLElement(
1871 HTMLElementTypeId::HTMLFieldSetElement,
1872 )) => Some(self.downcast::<HTMLFieldSetElement>().unwrap() as &dyn FormControl),
1873 NodeTypeId::Element(ElementTypeId::HTMLElement(
1874 HTMLElementTypeId::HTMLImageElement,
1875 )) => Some(self.downcast::<HTMLImageElement>().unwrap() as &dyn FormControl),
1876 NodeTypeId::Element(ElementTypeId::HTMLElement(
1877 HTMLElementTypeId::HTMLInputElement,
1878 )) => Some(self.downcast::<HTMLInputElement>().unwrap() as &dyn FormControl),
1879 NodeTypeId::Element(ElementTypeId::HTMLElement(
1880 HTMLElementTypeId::HTMLLabelElement,
1881 )) => Some(self.downcast::<HTMLLabelElement>().unwrap() as &dyn FormControl),
1882 NodeTypeId::Element(ElementTypeId::HTMLElement(
1883 HTMLElementTypeId::HTMLLegendElement,
1884 )) => Some(self.downcast::<HTMLLegendElement>().unwrap() as &dyn FormControl),
1885 NodeTypeId::Element(ElementTypeId::HTMLElement(
1886 HTMLElementTypeId::HTMLObjectElement,
1887 )) => Some(self.downcast::<HTMLObjectElement>().unwrap() as &dyn FormControl),
1888 NodeTypeId::Element(ElementTypeId::HTMLElement(
1889 HTMLElementTypeId::HTMLOutputElement,
1890 )) => Some(self.downcast::<HTMLOutputElement>().unwrap() as &dyn FormControl),
1891 NodeTypeId::Element(ElementTypeId::HTMLElement(
1892 HTMLElementTypeId::HTMLSelectElement,
1893 )) => Some(self.downcast::<HTMLSelectElement>().unwrap() as &dyn FormControl),
1894 NodeTypeId::Element(ElementTypeId::HTMLElement(
1895 HTMLElementTypeId::HTMLTextAreaElement,
1896 )) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
1897 _ => self.downcast::<HTMLElement>().and_then(|elem| {
1898 if elem.is_form_associated_custom_element() {
1899 Some(elem as &dyn FormControl)
1900 } else {
1901 None
1902 }
1903 }),
1904 }
1905 }
1906}
1907
1908pub(crate) fn encode_multipart_form_data(
1910 form_data: &mut [FormDatum],
1911 boundary: String,
1912 encoding: &'static Encoding,
1913) -> Vec<u8> {
1914 let mut result = vec![];
1915
1916 fn clean_crlf(s: &DOMString) -> DOMString {
1918 let mut buf = "".to_owned();
1919 let mut prev = ' ';
1920 for ch in s.str().chars() {
1921 match ch {
1922 '\n' if prev != '\r' => {
1923 buf.push('\r');
1924 buf.push('\n');
1925 },
1926 '\n' => {
1927 buf.push('\n');
1928 },
1929 _ if prev == '\r' => {
1932 buf.push('\r');
1933 buf.push('\n');
1934 buf.push(ch);
1935 },
1936 _ => buf.push(ch),
1937 };
1938 prev = ch;
1939 }
1940 if prev == '\r' {
1942 buf.push('\n');
1943 }
1944 DOMString::from(buf)
1945 }
1946
1947 for entry in form_data.iter_mut() {
1948 entry.name = clean_crlf(&entry.name);
1950
1951 if let FormDatumValue::String(ref s) = entry.value {
1954 entry.value = FormDatumValue::String(clean_crlf(s));
1955 }
1956
1957 let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
1962 result.append(&mut boundary_bytes);
1963
1964 match entry.value {
1967 FormDatumValue::String(ref s) => {
1968 let content_disposition = format!("form-data; name=\"{}\"", entry.name);
1969 let mut bytes =
1970 format!("Content-Disposition: {}\r\n\r\n{}", content_disposition, s)
1971 .into_bytes();
1972 result.append(&mut bytes);
1973 },
1974 FormDatumValue::File(ref f) => {
1975 let charset = encoding.name();
1976 let extra = if charset.to_lowercase() == "utf-8" {
1977 format!("filename=\"{}\"", String::from(f.name().str()))
1978 } else {
1979 format!(
1980 "filename*=\"{}\"''{}",
1981 charset,
1982 http_percent_encode(&f.name().as_bytes())
1983 )
1984 };
1985
1986 let content_disposition = format!("form-data; name=\"{}\"; {}", entry.name, extra);
1987 let content_type: Mime = f
1989 .upcast::<Blob>()
1990 .Type()
1991 .parse()
1992 .unwrap_or(mime::TEXT_PLAIN);
1993 let mut type_bytes = format!(
1994 "Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n",
1995 content_disposition, content_type
1996 )
1997 .into_bytes();
1998 result.append(&mut type_bytes);
1999
2000 let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
2001
2002 result.append(&mut bytes);
2003 },
2004 }
2005 }
2006
2007 let mut boundary_bytes = format!("\r\n--{}--\r\n", boundary).into_bytes();
2008 result.append(&mut boundary_bytes);
2009
2010 result
2011}
2012
2013pub(crate) fn generate_boundary() -> String {
2015 let i1 = random::<u32>();
2016 let i2 = random::<u32>();
2017
2018 format!("---------------------------{0}{1}", i1, i2)
2019}