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