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