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};
14use http::Method;
15use js::context::JSContext;
16use js::rust::HandleObject;
17use mime::{self, Mime};
18use net_traits::http_percent_encode;
19use net_traits::request::Referrer;
20use rand::random;
21use rustc_hash::FxBuildHasher;
22use script_bindings::match_domstring_ascii;
23use style::attr::AttrValue;
24use style::str::split_html_space_chars;
25use stylo_atoms::Atom;
26use stylo_dom::ElementState;
27
28use crate::body::Extractable;
29use crate::dom::attr::Attr;
30use crate::dom::bindings::cell::DomRefCell;
31use crate::dom::bindings::codegen::Bindings::AttrBinding::Attr_Binding::AttrMethods;
32use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
33use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
34use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
35use crate::dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
37use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
38use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
39use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
40use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
41use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
42use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
43use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
44use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
45use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
46use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement;
47use crate::dom::bindings::error::{Error, Fallible};
48use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
49use crate::dom::bindings::refcounted::Trusted;
50use crate::dom::bindings::reflector::{DomGlobal, DomObject};
51use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, MutNullableDom};
52use crate::dom::bindings::str::DOMString;
53use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
54use crate::dom::blob::Blob;
55use crate::dom::customelementregistry::CallbackReaction;
56use crate::dom::document::Document;
57use crate::dom::domtokenlist::DOMTokenList;
58use crate::dom::element::{AttributeMutation, AttributeMutationReason, Element};
59use crate::dom::event::{Event, EventBubbles, EventCancelable};
60use crate::dom::eventtarget::EventTarget;
61use crate::dom::file::File;
62use crate::dom::formdata::FormData;
63use crate::dom::formdataevent::FormDataEvent;
64use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
65use crate::dom::html::htmlcollection::CollectionFilter;
66use crate::dom::html::htmldatalistelement::HTMLDataListElement;
67use crate::dom::html::htmlelement::HTMLElement;
68use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
69use crate::dom::html::htmlformcontrolscollection::HTMLFormControlsCollection;
70use crate::dom::html::htmlimageelement::HTMLImageElement;
71use crate::dom::html::htmlinputelement::{HTMLInputElement, InputType};
72use crate::dom::html::htmllabelelement::HTMLLabelElement;
73use crate::dom::html::htmllegendelement::HTMLLegendElement;
74use crate::dom::html::htmlobjectelement::HTMLObjectElement;
75use crate::dom::html::htmloutputelement::HTMLOutputElement;
76use crate::dom::html::htmlselectelement::HTMLSelectElement;
77use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
78use crate::dom::node::{
79 BindContext, Node, NodeFlags, NodeTraits, UnbindContext, VecPreOrderInsertionHelper,
80};
81use crate::dom::nodelist::{NodeList, RadioListMode};
82use crate::dom::radionodelist::RadioNodeList;
83use crate::dom::submitevent::SubmitEvent;
84use crate::dom::types::HTMLIFrameElement;
85use crate::dom::virtualmethods::VirtualMethods;
86use crate::dom::window::Window;
87use crate::links::{LinkRelations, get_element_target, valid_navigable_target_name_or_keyword};
88use crate::script_runtime::CanGc;
89use crate::script_thread::ScriptThread;
90
91#[dom_struct]
92pub(crate) struct HTMLFormElement {
93 htmlelement: HTMLElement,
94 marked_for_reset: Cell<bool>,
95 constructing_entry_list: Cell<bool>,
97 elements: DomOnceCell<HTMLFormControlsCollection>,
98 controls: DomRefCell<Vec<Dom<Element>>>,
99
100 #[expect(clippy::type_complexity)]
102 past_names_map:
103 DomRefCell<HashMapTracedValues<Atom, (Dom<Element>, NoTrace<usize>), FxBuildHasher>>,
104
105 current_name_generation: Cell<usize>,
107
108 firing_submission_events: Cell<bool>,
109 rel_list: MutNullableDom<DOMTokenList>,
110
111 planned_navigation: Cell<usize>,
113
114 #[no_trace]
115 relations: Cell<LinkRelations>,
116}
117
118impl HTMLFormElement {
119 fn new_inherited(
120 local_name: LocalName,
121 prefix: Option<Prefix>,
122 document: &Document,
123 ) -> HTMLFormElement {
124 HTMLFormElement {
125 htmlelement: HTMLElement::new_inherited_with_state(
126 ElementState::VALID,
127 local_name,
128 prefix,
129 document,
130 ),
131 marked_for_reset: Cell::new(false),
132 constructing_entry_list: Cell::new(false),
133 elements: Default::default(),
134 controls: DomRefCell::new(Vec::new()),
135 past_names_map: DomRefCell::new(HashMapTracedValues::new_fx()),
136 current_name_generation: Cell::new(0),
137 firing_submission_events: Cell::new(false),
138 rel_list: Default::default(),
139 planned_navigation: Default::default(),
140 relations: Cell::new(LinkRelations::empty()),
141 }
142 }
143
144 pub(crate) fn new(
145 local_name: LocalName,
146 prefix: Option<Prefix>,
147 document: &Document,
148 proto: Option<HandleObject>,
149 can_gc: CanGc,
150 ) -> DomRoot<HTMLFormElement> {
151 Node::reflect_node_with_proto(
152 Box::new(HTMLFormElement::new_inherited(local_name, prefix, document)),
153 document,
154 proto,
155 can_gc,
156 )
157 }
158
159 fn filter_for_radio_list(mode: RadioListMode, child: &Element, name: &Atom) -> bool {
160 if let Some(child) = child.downcast::<Element>() {
161 match mode {
162 RadioListMode::ControlsExceptImageInputs => {
163 if child
164 .downcast::<HTMLElement>()
165 .is_some_and(|c| c.is_listed_element()) &&
166 (child.get_id().is_some_and(|i| i == *name) ||
167 child.get_name().is_some_and(|n| n == *name))
168 {
169 if let Some(inp) = child.downcast::<HTMLInputElement>() {
170 return inp.input_type() != InputType::Image;
172 } else {
173 return true;
175 }
176 }
177 return false;
178 },
179 RadioListMode::Images => {
180 return child.is::<HTMLImageElement>() &&
181 (child.get_id().is_some_and(|i| i == *name) ||
182 child.get_name().is_some_and(|n| n == *name));
183 },
184 }
185 }
186 false
187 }
188
189 pub(crate) fn nth_for_radio_list(
190 &self,
191 index: u32,
192 mode: RadioListMode,
193 name: &Atom,
194 ) -> Option<DomRoot<Node>> {
195 self.controls
196 .borrow()
197 .iter()
198 .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
199 .nth(index as usize)
200 .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
201 }
202
203 pub(crate) fn count_for_radio_list(&self, mode: RadioListMode, name: &Atom) -> u32 {
204 self.controls
205 .borrow()
206 .iter()
207 .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
208 .count() as u32
209 }
210}
211
212impl HTMLFormElementMethods<crate::DomTypeHolder> for HTMLFormElement {
213 make_getter!(AcceptCharset, "accept-charset");
215
216 make_setter!(SetAcceptCharset, "accept-charset");
218
219 make_form_action_getter!(Action, "action");
221
222 make_setter!(SetAction, "action");
224
225 make_enumerated_getter!(
227 Autocomplete,
228 "autocomplete",
229 "on" | "off",
230 missing => "on",
231 invalid => "on"
232 );
233
234 make_setter!(SetAutocomplete, "autocomplete");
236
237 make_enumerated_getter!(
239 Enctype,
240 "enctype",
241 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
242 missing => "application/x-www-form-urlencoded",
243 invalid => "application/x-www-form-urlencoded"
244 );
245
246 make_setter!(SetEnctype, "enctype");
248
249 fn Encoding(&self) -> DOMString {
251 self.Enctype()
252 }
253
254 fn SetEncoding(&self, value: DOMString) {
256 self.SetEnctype(value)
257 }
258
259 make_enumerated_getter!(
261 Method,
262 "method",
263 "get" | "post" | "dialog",
264 missing => "get",
265 invalid => "get"
266 );
267
268 make_setter!(SetMethod, "method");
270
271 make_getter!(Name, "name");
273
274 make_atomic_setter!(SetName, "name");
276
277 make_bool_getter!(NoValidate, "novalidate");
279
280 make_bool_setter!(SetNoValidate, "novalidate");
282
283 make_getter!(Target, "target");
285
286 make_setter!(SetTarget, "target");
288
289 make_getter!(Rel, "rel");
291
292 fn Submit(&self, can_gc: CanGc) {
294 self.submit(
295 SubmittedFrom::FromForm,
296 FormSubmitterElement::Form(self),
297 can_gc,
298 );
299 }
300
301 fn RequestSubmit(&self, submitter: Option<&HTMLElement>, can_gc: CanGc) -> Fallible<()> {
303 let submitter: FormSubmitterElement = match submitter {
304 Some(submitter_element) => {
305 let error_not_a_submit_button =
307 Err(Error::Type(c"submitter must be a submit button".to_owned()));
308
309 let element = match submitter_element.upcast::<Node>().type_id() {
310 NodeTypeId::Element(ElementTypeId::HTMLElement(element)) => element,
311 _ => {
312 return error_not_a_submit_button;
313 },
314 };
315
316 let submit_button = match element {
317 HTMLElementTypeId::HTMLInputElement => FormSubmitterElement::Input(
318 submitter_element
319 .downcast::<HTMLInputElement>()
320 .expect("Failed to downcast submitter elem to HTMLInputElement."),
321 ),
322 HTMLElementTypeId::HTMLButtonElement => FormSubmitterElement::Button(
323 submitter_element
324 .downcast::<HTMLButtonElement>()
325 .expect("Failed to downcast submitter elem to HTMLButtonElement."),
326 ),
327 _ => {
328 return error_not_a_submit_button;
329 },
330 };
331
332 if !submit_button.is_submit_button() {
333 return error_not_a_submit_button;
334 }
335
336 let submitters_owner = submit_button.form_owner();
337
338 let owner = match submitters_owner {
340 Some(owner) => owner,
341 None => {
342 return Err(Error::NotFound(None));
343 },
344 };
345
346 if *owner != *self {
347 return Err(Error::NotFound(None));
348 }
349
350 submit_button
351 },
352 None => {
353 FormSubmitterElement::Form(self)
355 },
356 };
357 self.submit(SubmittedFrom::NotFromForm, submitter, can_gc);
359 Ok(())
360 }
361
362 fn Reset(&self, can_gc: CanGc) {
364 self.reset(ResetFrom::FromForm, can_gc);
365 }
366
367 fn Elements(&self, can_gc: CanGc) -> DomRoot<HTMLFormControlsCollection> {
369 #[derive(JSTraceable, MallocSizeOf)]
370 struct ElementsFilter {
371 form: DomRoot<HTMLFormElement>,
372 }
373 impl CollectionFilter for ElementsFilter {
374 fn filter<'a>(&self, elem: &'a Element, _root: &'a Node) -> bool {
375 let form_owner = match elem.upcast::<Node>().type_id() {
376 NodeTypeId::Element(ElementTypeId::HTMLElement(t)) => match t {
377 HTMLElementTypeId::HTMLButtonElement => {
378 elem.downcast::<HTMLButtonElement>().unwrap().form_owner()
379 },
380 HTMLElementTypeId::HTMLFieldSetElement => {
381 elem.downcast::<HTMLFieldSetElement>().unwrap().form_owner()
382 },
383 HTMLElementTypeId::HTMLInputElement => {
384 let input_elem = elem.downcast::<HTMLInputElement>().unwrap();
385 if input_elem.input_type() == InputType::Image {
386 return false;
387 }
388 input_elem.form_owner()
389 },
390 HTMLElementTypeId::HTMLObjectElement => {
391 elem.downcast::<HTMLObjectElement>().unwrap().form_owner()
392 },
393 HTMLElementTypeId::HTMLOutputElement => {
394 elem.downcast::<HTMLOutputElement>().unwrap().form_owner()
395 },
396 HTMLElementTypeId::HTMLSelectElement => {
397 elem.downcast::<HTMLSelectElement>().unwrap().form_owner()
398 },
399 HTMLElementTypeId::HTMLTextAreaElement => {
400 elem.downcast::<HTMLTextAreaElement>().unwrap().form_owner()
401 },
402 HTMLElementTypeId::HTMLElement => {
403 let html_element = elem.downcast::<HTMLElement>().unwrap();
404 if html_element.is_form_associated_custom_element() {
405 html_element.form_owner()
406 } else {
407 return false;
408 }
409 },
410 _ => {
411 debug_assert!(
412 !elem.downcast::<HTMLElement>().unwrap().is_listed_element()
413 );
414 return false;
415 },
416 },
417 _ => return false,
418 };
419
420 match form_owner {
421 Some(form_owner) => form_owner == self.form,
422 None => false,
423 }
424 }
425 }
426 DomRoot::from_ref(self.elements.init_once(|| {
427 let filter = Box::new(ElementsFilter {
428 form: DomRoot::from_ref(self),
429 });
430 let window = self.owner_window();
431 HTMLFormControlsCollection::new(&window, self, filter, can_gc)
432 }))
433 }
434
435 fn Length(&self) -> u32 {
437 self.Elements(CanGc::note()).Length()
438 }
439
440 fn IndexedGetter(&self, index: u32, can_gc: CanGc) -> Option<DomRoot<Element>> {
442 let elements = self.Elements(can_gc);
443 elements.IndexedGetter(index)
444 }
445
446 fn NamedGetter(&self, name: DOMString, can_gc: CanGc) -> Option<RadioNodeListOrElement> {
448 let window = self.owner_window();
449
450 let name = Atom::from(name);
451
452 let mut candidates =
454 RadioNodeList::new_controls_except_image_inputs(&window, self, &name, can_gc);
455 let mut candidates_length = candidates.Length();
456
457 if candidates_length == 0 {
459 candidates = RadioNodeList::new_images(&window, self, &name, can_gc);
460 candidates_length = candidates.Length();
461 }
462
463 let mut past_names_map = self.past_names_map.borrow_mut();
464
465 if candidates_length == 0 {
467 if past_names_map.contains_key(&name) {
468 return Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
469 &*past_names_map.get(&name).unwrap().0,
470 )));
471 }
472 return None;
473 }
474
475 if candidates_length > 1 {
477 return Some(RadioNodeListOrElement::RadioNodeList(candidates));
478 }
479
480 let element_node = candidates.upcast::<NodeList>().Item(0).unwrap();
483 past_names_map.insert(
484 name,
485 (
486 Dom::from_ref(element_node.downcast::<Element>().unwrap()),
487 NoTrace(self.current_name_generation.get() + 1),
488 ),
489 );
490 self.current_name_generation
491 .set(self.current_name_generation.get() + 1);
492
493 Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
495 element_node.downcast::<Element>().unwrap(),
496 )))
497 }
498
499 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
501 self.upcast::<Element>()
502 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
503 }
504
505 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
507 self.rel_list.or_init(|| {
508 DOMTokenList::new(
509 self.upcast(),
510 &local_name!("rel"),
511 Some(vec![
512 Atom::from("noopener"),
513 Atom::from("noreferrer"),
514 Atom::from("opener"),
515 ]),
516 can_gc,
517 )
518 })
519 }
520
521 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, cx: &mut JSContext) -> bool {
654 self.static_validation(CanGc::from_cx(cx)).is_ok()
655 }
656
657 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
659 self.interactive_validation(CanGc::from_cx(cx)).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 method = submitter.method();
828 let mut action = submitter.action();
833
834 if action.is_empty() {
836 action = DOMString::from(base.as_str());
837 }
838 let action_components = match base.join(&action.str()) {
840 Ok(url) => url,
841 Err(_) => return,
843 };
844 let scheme = action_components.scheme().to_owned();
846 let enctype = submitter.enctype();
848
849 let form_target_attribute = submitter.target();
852 let form_target = if submitter.is_submit_button() &&
853 valid_navigable_target_name_or_keyword(&form_target_attribute)
854 {
855 Some(form_target_attribute)
856 } else {
857 None
859 };
860 let form_owner = submitter.form_owner();
862 let form = form_owner.as_deref().unwrap_or(self);
863 let target = get_element_target(form.upcast::<Element>(), form_target);
864
865 let noopener = self.relations.get().get_element_noopener(target.as_ref());
867
868 let source = doc.browsing_context().unwrap();
871 let (maybe_chosen, _new) =
872 source.choose_browsing_context(target.unwrap_or_default(), noopener);
873
874 let Some(chosen) = maybe_chosen else {
875 return;
877 };
878 let target_document = match chosen.document() {
885 Some(doc) => doc,
886 None => return,
887 };
888 let target_window = target_document.window();
889 let mut load_data = LoadData::new(
890 LoadOrigin::Script(doc.origin().snapshot()),
891 action_components,
892 target_document.about_base_url(),
893 None,
894 target_window.as_global_scope().get_referrer(),
895 target_document.get_referrer_policy(),
896 Some(target_window.as_global_scope().is_secure_context()),
897 Some(target_document.insecure_requests_policy()),
898 target_document.has_trustworthy_ancestor_origin(),
899 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
900 );
901
902 match (&*scheme, method) {
906 (_, FormMethod::Dialog) => {
907 },
910 ("http", FormMethod::Get) | ("https", FormMethod::Get) | ("data", FormMethod::Get) => {
912 load_data
913 .headers
914 .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
915 self.mutate_action_url(&mut form_data, load_data, encoding, target_window);
916 },
917 ("http", FormMethod::Post) | ("https", FormMethod::Post) => {
919 load_data.method = Method::POST;
920 self.submit_entity_body(
921 &mut form_data,
922 load_data,
923 enctype,
924 encoding,
925 target_window,
926 can_gc,
927 );
928 },
929 ("file", _) |
931 ("about", _) |
932 ("data", FormMethod::Post) |
933 ("ftp", _) |
934 ("javascript", _) => {
935 self.plan_to_navigate(load_data, target_window);
936 },
937 ("mailto", FormMethod::Post) => {
938 },
941 ("mailto", FormMethod::Get) => {
942 },
945 _ => (),
946 }
947 }
948
949 fn mutate_action_url(
951 &self,
952 form_data: &mut [FormDatum],
953 mut load_data: LoadData,
954 encoding: &'static Encoding,
955 target: &Window,
956 ) {
957 let charset = encoding.name();
958
959 self.set_url_query_pairs(
960 &mut load_data.url,
961 form_data
962 .iter()
963 .map(|field| (field.name.str(), field.replace_value(charset))),
964 );
965
966 self.plan_to_navigate(load_data, target);
967 }
968
969 fn submit_entity_body(
971 &self,
972 form_data: &mut [FormDatum],
973 mut load_data: LoadData,
974 enctype: FormEncType,
975 encoding: &'static Encoding,
976 target: &Window,
977 can_gc: CanGc,
978 ) {
979 let boundary = generate_boundary();
980 let bytes = match enctype {
981 FormEncType::UrlEncoded => {
982 let charset = encoding.name();
983 load_data
984 .headers
985 .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
986
987 let mut url = load_data.url.clone();
988 self.set_url_query_pairs(
989 &mut url,
990 form_data
991 .iter()
992 .map(|field| (field.name.str(), field.replace_value(charset))),
993 );
994
995 url.query().unwrap_or("").to_string().into_bytes()
996 },
997 FormEncType::MultipartFormData => {
998 let mime: Mime = format!("multipart/form-data; boundary={}", boundary)
999 .parse()
1000 .unwrap();
1001 load_data.headers.typed_insert(ContentType::from(mime));
1002 encode_multipart_form_data(form_data, boundary, encoding)
1003 },
1004 FormEncType::TextPlain => {
1005 load_data
1006 .headers
1007 .typed_insert(ContentType::from(mime::TEXT_PLAIN));
1008 self.encode_plaintext(form_data).into_bytes()
1009 },
1010 };
1011
1012 let global = self.global();
1013
1014 let request_body = bytes
1015 .extract(&global, false, can_gc)
1016 .expect("Couldn't extract body.")
1017 .into_net_request_body()
1018 .0;
1019 load_data.data = Some(request_body);
1020
1021 self.plan_to_navigate(load_data, target);
1022 }
1023
1024 fn set_url_query_pairs<T>(
1025 &self,
1026 url: &mut servo_url::ServoUrl,
1027 pairs: impl Iterator<Item = (T, String)>,
1028 ) where
1029 T: AsRef<str>,
1030 {
1031 let encoding = self.pick_encoding();
1032 url.as_mut_url()
1033 .query_pairs_mut()
1034 .encoding_override(Some(&|s| encoding.encode(s).0))
1035 .clear()
1036 .extend_pairs(pairs);
1037 }
1038
1039 fn plan_to_navigate(&self, mut load_data: LoadData, target: &Window) {
1041 let elem = self.upcast::<Element>();
1046 let referrer = match elem.get_attribute(&local_name!("rel")) {
1047 Some(ref link_types) if link_types.Value().contains("noreferrer") => {
1048 Referrer::NoReferrer
1049 },
1050 _ => target.as_global_scope().get_referrer(),
1051 };
1052
1053 self.planned_navigation
1056 .set(self.planned_navigation.get().wrapping_add(1));
1057 let planned_navigation = self.planned_navigation.get();
1058
1059 let ongoing_navigation = target.set_ongoing_navigation();
1075
1076 let referrer_policy = target.Document().get_referrer_policy();
1077 load_data.creator_pipeline_id = Some(target.pipeline_id());
1078 load_data.referrer = referrer;
1079 load_data.referrer_policy = referrer_policy;
1080
1081 if let Some(window_proxy) = target.undiscarded_window_proxy() {
1084 if let Some(frame) = window_proxy
1085 .frame_element()
1086 .and_then(|e| e.downcast::<HTMLIFrameElement>())
1087 {
1088 frame.note_pending_navigation()
1089 }
1090 }
1091
1092 let form = Trusted::new(self);
1095 let window = Trusted::new(target);
1096 let task = task!(navigate_to_form_planned_navigation: move || {
1097 if planned_navigation != form.root().planned_navigation.get() {
1101 return;
1102 }
1103
1104 if ongoing_navigation != window.root().ongoing_navigation() {
1107 return;
1108 }
1109
1110 window
1112 .root()
1113 .load_url(
1114 NavigationHistoryBehavior::Push,
1115 false,
1116 load_data,
1117 CanGc::note(),
1118 );
1119 });
1120
1121 target
1126 .global()
1127 .task_manager()
1128 .dom_manipulation_task_source()
1129 .queue(task)
1130 }
1131
1132 fn interactive_validation(&self, can_gc: CanGc) -> Result<(), ()> {
1135 let unhandled_invalid_controls = match self.static_validation(can_gc) {
1140 Ok(()) => return Ok(()),
1141 Err(err) => err,
1142 };
1143
1144 let mut first = true;
1147
1148 for elem in unhandled_invalid_controls {
1149 if let Some(validatable) = elem.as_maybe_validatable() {
1150 error!("Validation error: {}", validatable.validation_message());
1151 }
1152 if first {
1153 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1154 html_elem.Focus(&FocusOptions::default(), can_gc);
1161 first = false;
1162 }
1163 }
1164 }
1165
1166 Err(())
1170 }
1171
1172 fn static_validation(&self, can_gc: CanGc) -> Result<(), Vec<DomRoot<Element>>> {
1175 let invalid_controls = self
1177 .controls
1178 .borrow()
1179 .iter()
1180 .filter_map(|field| {
1181 if let Some(element) = field.downcast::<Element>() {
1182 if element.is_invalid(true, can_gc) {
1183 Some(DomRoot::from_ref(element))
1184 } else {
1185 None
1186 }
1187 } else {
1188 None
1189 }
1190 })
1191 .collect::<Vec<DomRoot<Element>>>();
1192 if invalid_controls.is_empty() {
1194 return Ok(());
1195 }
1196 let unhandled_invalid_controls = invalid_controls
1198 .into_iter()
1199 .filter_map(|field| {
1200 let not_canceled = field
1203 .upcast::<EventTarget>()
1204 .fire_cancelable_event(atom!("invalid"), can_gc);
1205 if not_canceled {
1207 return Some(field);
1208 }
1209 None
1210 })
1211 .collect::<Vec<DomRoot<Element>>>();
1212 Err(unhandled_invalid_controls)
1214 }
1215
1216 fn get_unclean_dataset(
1221 &self,
1222 submitter: Option<FormSubmitterElement>,
1223 encoding: Option<&'static Encoding>,
1224 can_gc: CanGc,
1225 ) -> Vec<FormDatum> {
1226 let mut data_set = Vec::new();
1227 for child in self.controls.borrow().iter() {
1228 if child.disabled_state() {
1230 continue;
1231 }
1232 let child = child.upcast::<Node>();
1233
1234 if child.ancestors().any(|a| a.is::<HTMLDataListElement>()) {
1236 continue;
1237 }
1238 if let NodeTypeId::Element(ElementTypeId::HTMLElement(element)) = child.type_id() {
1239 match element {
1240 HTMLElementTypeId::HTMLInputElement => {
1241 let input = child.downcast::<HTMLInputElement>().unwrap();
1242 data_set.append(&mut input.form_datums(submitter, encoding));
1243 },
1244 HTMLElementTypeId::HTMLButtonElement => {
1245 let button = child.downcast::<HTMLButtonElement>().unwrap();
1246 if let Some(datum) = button.form_datum(submitter) {
1247 data_set.push(datum);
1248 }
1249 },
1250 HTMLElementTypeId::HTMLObjectElement => {
1251 },
1253 HTMLElementTypeId::HTMLSelectElement => {
1254 let select = child.downcast::<HTMLSelectElement>().unwrap();
1255 select.push_form_data(&mut data_set);
1256 },
1257 HTMLElementTypeId::HTMLTextAreaElement => {
1258 let textarea = child.downcast::<HTMLTextAreaElement>().unwrap();
1259 let name = textarea.Name();
1260 if !name.is_empty() {
1261 data_set.push(FormDatum {
1262 ty: textarea.Type(),
1263 name,
1264 value: FormDatumValue::String(textarea.Value()),
1265 });
1266 }
1267 },
1268 HTMLElementTypeId::HTMLElement => {
1269 let custom = child.downcast::<HTMLElement>().unwrap();
1270 if custom.is_form_associated_custom_element() {
1271 let internals =
1273 custom.upcast::<Element>().ensure_element_internals(can_gc);
1274 internals.perform_entry_construction(&mut data_set);
1275 }
1277 },
1278 _ => (),
1279 }
1280 }
1281
1282 let child_element = child.downcast::<Element>().unwrap();
1286 let input_matches = child_element
1287 .downcast::<HTMLInputElement>()
1288 .is_some_and(|input| {
1289 matches!(input.input_type(), InputType::Text | InputType::Search)
1290 });
1291 let textarea_matches = child_element.is::<HTMLTextAreaElement>();
1292 let dirname = child_element.get_string_attribute(&local_name!("dirname"));
1293 if (input_matches || textarea_matches) && !dirname.is_empty() {
1294 let dir = DOMString::from(child_element.directionality());
1295 data_set.push(FormDatum {
1296 ty: DOMString::from("string"),
1297 name: dirname,
1298 value: FormDatumValue::String(dir),
1299 });
1300 }
1301 }
1302 data_set
1303 }
1304
1305 pub(crate) fn get_form_dataset(
1307 &self,
1308 submitter: Option<FormSubmitterElement>,
1309 encoding: Option<&'static Encoding>,
1310 can_gc: CanGc,
1311 ) -> Option<Vec<FormDatum>> {
1312 if self.constructing_entry_list.get() {
1314 return None;
1315 }
1316
1317 self.constructing_entry_list.set(true);
1319
1320 let ret = self.get_unclean_dataset(submitter, encoding, can_gc);
1322
1323 let window = self.owner_window();
1324
1325 let form_data = FormData::new(Some(ret), &window.global(), can_gc);
1327
1328 let event = FormDataEvent::new(
1330 &window,
1331 atom!("formdata"),
1332 EventBubbles::Bubbles,
1333 EventCancelable::NotCancelable,
1334 &form_data,
1335 can_gc,
1336 );
1337
1338 event
1339 .upcast::<Event>()
1340 .fire(self.upcast::<EventTarget>(), can_gc);
1341
1342 self.constructing_entry_list.set(false);
1344
1345 Some(form_data.datums())
1347 }
1348
1349 pub(crate) fn reset(&self, _reset_method_flag: ResetFrom, can_gc: CanGc) {
1351 if self.marked_for_reset.get() {
1353 return;
1354 } else {
1355 self.marked_for_reset.set(true);
1356 }
1357
1358 let reset = self
1362 .upcast::<EventTarget>()
1363 .fire_bubbling_cancelable_event(atom!("reset"), can_gc);
1364 if !reset {
1365 return;
1366 }
1367
1368 let controls: Vec<_> = self
1369 .controls
1370 .borrow()
1371 .iter()
1372 .map(|c| c.as_rooted())
1373 .collect();
1374
1375 for child in controls {
1376 child.reset(can_gc);
1377 }
1378 self.marked_for_reset.set(false);
1379 }
1380
1381 fn add_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1382 {
1383 let root = self.upcast::<Element>().root_element();
1384 let root = root.upcast::<Node>();
1385 let mut controls = self.controls.borrow_mut();
1386 controls.insert_pre_order(control.to_element(), root);
1387 }
1388 self.update_validity(can_gc);
1389 }
1390
1391 fn remove_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1392 {
1393 let control = control.to_element();
1394 let mut controls = self.controls.borrow_mut();
1395 controls
1396 .iter()
1397 .position(|c| &**c == control)
1398 .map(|idx| controls.remove(idx));
1399
1400 let mut past_names_map = self.past_names_map.borrow_mut();
1405 past_names_map.0.retain(|_k, v| v.0 != control);
1406 }
1407 self.update_validity(can_gc);
1408 }
1409}
1410
1411impl Element {
1412 pub(crate) fn is_resettable(&self) -> bool {
1413 let NodeTypeId::Element(ElementTypeId::HTMLElement(element_type)) =
1414 self.upcast::<Node>().type_id()
1415 else {
1416 return false;
1417 };
1418 matches!(
1419 element_type,
1420 HTMLElementTypeId::HTMLInputElement |
1421 HTMLElementTypeId::HTMLSelectElement |
1422 HTMLElementTypeId::HTMLTextAreaElement |
1423 HTMLElementTypeId::HTMLOutputElement |
1424 HTMLElementTypeId::HTMLElement
1425 )
1426 }
1427
1428 pub(crate) fn reset(&self, can_gc: CanGc) {
1429 if !self.is_resettable() {
1430 return;
1431 }
1432
1433 if let Some(input_element) = self.downcast::<HTMLInputElement>() {
1434 input_element.reset(can_gc);
1435 } else if let Some(select_element) = self.downcast::<HTMLSelectElement>() {
1436 select_element.reset();
1437 } else if let Some(textarea_element) = self.downcast::<HTMLTextAreaElement>() {
1438 textarea_element.reset(can_gc);
1439 } else if let Some(output_element) = self.downcast::<HTMLOutputElement>() {
1440 output_element.reset(can_gc);
1441 } else if let Some(html_element) = self.downcast::<HTMLElement>() {
1442 if html_element.is_form_associated_custom_element() {
1443 ScriptThread::enqueue_callback_reaction(
1444 html_element.upcast::<Element>(),
1445 CallbackReaction::FormReset,
1446 None,
1447 )
1448 }
1449 }
1450 }
1451}
1452
1453#[derive(Clone, JSTraceable, MallocSizeOf)]
1454pub(crate) enum FormDatumValue {
1455 File(DomRoot<File>),
1456 String(DOMString),
1457}
1458
1459#[derive(Clone, JSTraceable, MallocSizeOf)]
1460pub(crate) struct FormDatum {
1461 pub(crate) ty: DOMString,
1462 pub(crate) name: DOMString,
1463 pub(crate) value: FormDatumValue,
1464}
1465
1466impl FormDatum {
1467 pub(crate) fn replace_value(&self, charset: &str) -> String {
1468 if self.name.to_ascii_lowercase() == "_charset_" && self.ty == "hidden" {
1469 return charset.to_string();
1470 }
1471
1472 match self.value {
1473 FormDatumValue::File(ref f) => String::from(f.name().clone()),
1474 FormDatumValue::String(ref s) => String::from(s.clone()),
1475 }
1476 }
1477}
1478
1479#[derive(Clone, Copy, MallocSizeOf)]
1480pub(crate) enum FormEncType {
1481 TextPlain,
1482 UrlEncoded,
1483 MultipartFormData,
1484}
1485
1486#[derive(Clone, Copy, MallocSizeOf)]
1487pub(crate) enum FormMethod {
1488 Get,
1489 Post,
1490 Dialog,
1491}
1492
1493#[derive(Clone, Copy, MallocSizeOf)]
1495pub(crate) enum FormSubmitterElement<'a> {
1496 Form(&'a HTMLFormElement),
1497 Input(&'a HTMLInputElement),
1498 Button(&'a HTMLButtonElement),
1499 }
1502
1503impl FormSubmitterElement<'_> {
1504 fn action(&self) -> DOMString {
1505 match *self {
1506 FormSubmitterElement::Form(form) => form.Action(),
1507 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1508 &local_name!("formaction"),
1509 |i| i.FormAction(),
1510 |f| f.Action(),
1511 ),
1512 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1513 &local_name!("formaction"),
1514 |i| i.FormAction(),
1515 |f| f.Action(),
1516 ),
1517 }
1518 }
1519
1520 fn enctype(&self) -> FormEncType {
1521 let attr = match *self {
1522 FormSubmitterElement::Form(form) => form.Enctype(),
1523 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1524 &local_name!("formenctype"),
1525 |i| i.FormEnctype(),
1526 |f| f.Enctype(),
1527 ),
1528 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1529 &local_name!("formenctype"),
1530 |i| i.FormEnctype(),
1531 |f| f.Enctype(),
1532 ),
1533 };
1534 match_domstring_ascii!(attr,
1537 "multipart/form-data" => FormEncType::MultipartFormData,
1538 "text/plain" => FormEncType::TextPlain,
1539 _ => FormEncType::UrlEncoded,
1540 )
1541 }
1542
1543 fn method(&self) -> FormMethod {
1544 let attr = match *self {
1545 FormSubmitterElement::Form(form) => form.Method(),
1546 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1547 &local_name!("formmethod"),
1548 |i| i.FormMethod(),
1549 |f| f.Method(),
1550 ),
1551 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1552 &local_name!("formmethod"),
1553 |i| i.FormMethod(),
1554 |f| f.Method(),
1555 ),
1556 };
1557 match_domstring_ascii!(attr,
1558 "dialog" => FormMethod::Dialog,
1559 "post" => FormMethod::Post,
1560 _ => FormMethod::Get,
1561 )
1562 }
1563
1564 fn target(&self) -> DOMString {
1565 match *self {
1566 FormSubmitterElement::Form(form) => form.Target(),
1567 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1568 &local_name!("formtarget"),
1569 |i| i.FormTarget(),
1570 |f| f.Target(),
1571 ),
1572 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1573 &local_name!("formtarget"),
1574 |i| i.FormTarget(),
1575 |f| f.Target(),
1576 ),
1577 }
1578 }
1579
1580 fn no_validate(&self, _form_owner: &HTMLFormElement) -> bool {
1581 match *self {
1582 FormSubmitterElement::Form(form) => form.NoValidate(),
1583 FormSubmitterElement::Input(input_element) => input_element.get_form_boolean_attribute(
1584 &local_name!("formnovalidate"),
1585 |i| i.FormNoValidate(),
1586 |f| f.NoValidate(),
1587 ),
1588 FormSubmitterElement::Button(button_element) => button_element
1589 .get_form_boolean_attribute(
1590 &local_name!("formnovalidate"),
1591 |i| i.FormNoValidate(),
1592 |f| f.NoValidate(),
1593 ),
1594 }
1595 }
1596
1597 pub(crate) fn is_submit_button(&self) -> bool {
1599 match *self {
1600 FormSubmitterElement::Input(input_element) => input_element.is_submit_button(),
1603 FormSubmitterElement::Button(button_element) => button_element.is_submit_button(),
1605 _ => false,
1606 }
1607 }
1608
1609 pub(crate) fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1611 match *self {
1612 FormSubmitterElement::Button(button_el) => button_el.form_owner(),
1613 FormSubmitterElement::Input(input_el) => input_el.form_owner(),
1614 _ => None,
1615 }
1616 }
1617}
1618
1619pub(crate) trait FormControl: DomObject<ReflectorType = ()> {
1620 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>>;
1621
1622 fn set_form_owner(&self, form: Option<&HTMLFormElement>);
1623
1624 fn to_element(&self) -> ∈
1625
1626 fn is_listed(&self) -> bool {
1627 true
1628 }
1629
1630 fn set_form_owner_from_parser(&self, form: &HTMLFormElement, can_gc: CanGc) {
1635 let elem = self.to_element();
1636 let node = elem.upcast::<Node>();
1637 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, true);
1638 form.add_control(self, can_gc);
1639 self.set_form_owner(Some(form));
1640 }
1641
1642 fn reset_form_owner(&self, can_gc: CanGc) {
1644 let elem = self.to_element();
1645 let node = elem.upcast::<Node>();
1646 let old_owner = self.form_owner();
1647 let has_form_id = elem.has_attribute(&local_name!("form"));
1648 let nearest_form_ancestor = node
1649 .ancestors()
1650 .find_map(DomRoot::downcast::<HTMLFormElement>);
1651
1652 if old_owner.is_some() &&
1654 !(self.is_listed() && has_form_id) &&
1655 nearest_form_ancestor == old_owner
1656 {
1657 return;
1658 }
1659
1660 let new_owner = if self.is_listed() && has_form_id && elem.is_connected() {
1661 let doc = node.owner_document();
1663 let form_id = elem.get_string_attribute(&local_name!("form"));
1664 doc.GetElementById(form_id)
1665 .and_then(DomRoot::downcast::<HTMLFormElement>)
1666 } else {
1667 nearest_form_ancestor
1669 };
1670
1671 if old_owner != new_owner {
1672 if let Some(o) = old_owner {
1673 o.remove_control(self, can_gc);
1674 }
1675 if let Some(ref new_owner) = new_owner {
1676 new_owner.add_control(self, can_gc);
1677 }
1678 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1680 if html_elem.is_form_associated_custom_element() {
1681 ScriptThread::enqueue_callback_reaction(
1682 elem,
1683 CallbackReaction::FormAssociated(
1684 new_owner.as_ref().map(|form| DomRoot::from_ref(&**form)),
1685 ),
1686 None,
1687 )
1688 }
1689 }
1690 self.set_form_owner(new_owner.as_deref());
1691 }
1692 }
1693
1694 fn form_attribute_mutated(&self, mutation: AttributeMutation, can_gc: CanGc) {
1696 match mutation {
1697 AttributeMutation::Set(..) => {
1698 self.register_if_necessary();
1699 },
1700 AttributeMutation::Removed => {
1701 self.unregister_if_necessary();
1702 },
1703 }
1704
1705 self.reset_form_owner(can_gc);
1706 }
1707
1708 fn register_if_necessary(&self) {
1710 let elem = self.to_element();
1711 let form_id = elem.get_string_attribute(&local_name!("form"));
1712 let node = elem.upcast::<Node>();
1713
1714 if self.is_listed() && !form_id.is_empty() && node.is_connected() {
1715 node.owner_document()
1716 .register_form_id_listener(form_id, self);
1717 }
1718 }
1719
1720 fn unregister_if_necessary(&self) {
1721 let elem = self.to_element();
1722 let form_id = elem.get_string_attribute(&local_name!("form"));
1723
1724 if self.is_listed() && !form_id.is_empty() {
1725 elem.owner_document()
1726 .unregister_form_id_listener(form_id, self);
1727 }
1728 }
1729
1730 fn bind_form_control_to_tree(&self, can_gc: CanGc) {
1732 let elem = self.to_element();
1733 let node = elem.upcast::<Node>();
1734
1735 let must_skip_reset = node.get_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER);
1740 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, false);
1741
1742 if !must_skip_reset {
1743 self.form_attribute_mutated(
1744 AttributeMutation::Set(None, AttributeMutationReason::Directly),
1745 can_gc,
1746 );
1747 }
1748 }
1749
1750 fn unbind_form_control_from_tree(&self, can_gc: CanGc) {
1752 let elem = self.to_element();
1753 let has_form_attr = elem.has_attribute(&local_name!("form"));
1754 let same_subtree = self
1755 .form_owner()
1756 .is_none_or(|form| elem.is_in_same_home_subtree(&*form));
1757
1758 self.unregister_if_necessary();
1759
1760 if !same_subtree || (self.is_listed() && has_form_attr) {
1766 self.reset_form_owner(can_gc);
1767 }
1768 }
1769
1770 fn get_form_attribute<InputFn, OwnerFn>(
1771 &self,
1772 attr: &LocalName,
1773 input: InputFn,
1774 owner: OwnerFn,
1775 ) -> DOMString
1776 where
1777 InputFn: Fn(&Self) -> DOMString,
1778 OwnerFn: Fn(&HTMLFormElement) -> DOMString,
1779 Self: Sized,
1780 {
1781 if self.to_element().has_attribute(attr) {
1782 input(self)
1783 } else {
1784 self.form_owner().map_or(DOMString::new(), |t| owner(&t))
1785 }
1786 }
1787
1788 fn get_form_boolean_attribute<InputFn, OwnerFn>(
1789 &self,
1790 attr: &LocalName,
1791 input: InputFn,
1792 owner: OwnerFn,
1793 ) -> bool
1794 where
1795 InputFn: Fn(&Self) -> bool,
1796 OwnerFn: Fn(&HTMLFormElement) -> bool,
1797 Self: Sized,
1798 {
1799 if self.to_element().has_attribute(attr) {
1800 input(self)
1801 } else {
1802 self.form_owner().is_some_and(|t| owner(&t))
1803 }
1804 }
1805
1806 fn is_candidate_for_constraint_validation(&self) -> bool {
1808 let element = self.to_element();
1809 let html_element = element.downcast::<HTMLElement>();
1810 if let Some(html_element) = html_element {
1811 html_element.is_submittable_element() || element.is_instance_validatable()
1812 } else {
1813 false
1814 }
1815 }
1816
1817 fn moving_steps(&self, can_gc: CanGc) {
1818 let same_subtree = self
1821 .form_owner()
1822 .is_none_or(|form| self.to_element().is_in_same_home_subtree(&*form));
1823 if !same_subtree {
1824 self.reset_form_owner(can_gc)
1825 }
1826 }
1827
1828 }
1831
1832impl VirtualMethods for HTMLFormElement {
1833 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1834 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1835 }
1836
1837 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1838 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1839
1840 rooted_vec!(let mut to_reset);
1843 to_reset.extend(
1844 self.controls
1845 .borrow()
1846 .iter()
1847 .filter(|c| !c.is_in_same_home_subtree(self))
1848 .cloned(),
1849 );
1850
1851 for control in to_reset.iter() {
1852 control
1853 .as_maybe_form_control()
1854 .expect("Element must be a form control")
1855 .reset_form_owner(can_gc);
1856 }
1857 }
1858
1859 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1860 match name {
1861 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
1862 _ => self
1863 .super_type()
1864 .unwrap()
1865 .parse_plain_attribute(name, value),
1866 }
1867 }
1868
1869 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1870 self.super_type()
1871 .unwrap()
1872 .attribute_mutated(attr, mutation, can_gc);
1873
1874 match *attr.local_name() {
1875 local_name!("rel") | local_name!("rev") => {
1876 self.relations
1877 .set(LinkRelations::for_element(self.upcast()));
1878 },
1879 _ => {},
1880 }
1881 }
1882
1883 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1884 if let Some(s) = self.super_type() {
1885 s.bind_to_tree(context, can_gc);
1886 }
1887
1888 self.relations
1889 .set(LinkRelations::for_element(self.upcast()));
1890 }
1891}
1892
1893pub(crate) trait FormControlElementHelpers {
1894 fn as_maybe_form_control(&self) -> Option<&dyn FormControl>;
1895}
1896
1897impl FormControlElementHelpers for Element {
1898 fn as_maybe_form_control(&self) -> Option<&dyn FormControl> {
1899 let node = self.upcast::<Node>();
1900
1901 match node.type_id() {
1902 NodeTypeId::Element(ElementTypeId::HTMLElement(
1903 HTMLElementTypeId::HTMLButtonElement,
1904 )) => Some(self.downcast::<HTMLButtonElement>().unwrap() as &dyn FormControl),
1905 NodeTypeId::Element(ElementTypeId::HTMLElement(
1906 HTMLElementTypeId::HTMLFieldSetElement,
1907 )) => Some(self.downcast::<HTMLFieldSetElement>().unwrap() as &dyn FormControl),
1908 NodeTypeId::Element(ElementTypeId::HTMLElement(
1909 HTMLElementTypeId::HTMLImageElement,
1910 )) => Some(self.downcast::<HTMLImageElement>().unwrap() as &dyn FormControl),
1911 NodeTypeId::Element(ElementTypeId::HTMLElement(
1912 HTMLElementTypeId::HTMLInputElement,
1913 )) => Some(self.downcast::<HTMLInputElement>().unwrap() as &dyn FormControl),
1914 NodeTypeId::Element(ElementTypeId::HTMLElement(
1915 HTMLElementTypeId::HTMLLabelElement,
1916 )) => Some(self.downcast::<HTMLLabelElement>().unwrap() as &dyn FormControl),
1917 NodeTypeId::Element(ElementTypeId::HTMLElement(
1918 HTMLElementTypeId::HTMLLegendElement,
1919 )) => Some(self.downcast::<HTMLLegendElement>().unwrap() as &dyn FormControl),
1920 NodeTypeId::Element(ElementTypeId::HTMLElement(
1921 HTMLElementTypeId::HTMLObjectElement,
1922 )) => Some(self.downcast::<HTMLObjectElement>().unwrap() as &dyn FormControl),
1923 NodeTypeId::Element(ElementTypeId::HTMLElement(
1924 HTMLElementTypeId::HTMLOutputElement,
1925 )) => Some(self.downcast::<HTMLOutputElement>().unwrap() as &dyn FormControl),
1926 NodeTypeId::Element(ElementTypeId::HTMLElement(
1927 HTMLElementTypeId::HTMLSelectElement,
1928 )) => Some(self.downcast::<HTMLSelectElement>().unwrap() as &dyn FormControl),
1929 NodeTypeId::Element(ElementTypeId::HTMLElement(
1930 HTMLElementTypeId::HTMLTextAreaElement,
1931 )) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
1932 _ => self.downcast::<HTMLElement>().and_then(|elem| {
1933 if elem.is_form_associated_custom_element() {
1934 Some(elem as &dyn FormControl)
1935 } else {
1936 None
1937 }
1938 }),
1939 }
1940 }
1941}
1942
1943pub(crate) fn encode_multipart_form_data(
1945 form_data: &mut [FormDatum],
1946 boundary: String,
1947 encoding: &'static Encoding,
1948) -> Vec<u8> {
1949 let mut result = vec![];
1950
1951 fn clean_crlf(s: &DOMString) -> DOMString {
1953 let mut buf = "".to_owned();
1954 let mut prev = ' ';
1955 for ch in s.str().chars() {
1956 match ch {
1957 '\n' if prev != '\r' => {
1958 buf.push('\r');
1959 buf.push('\n');
1960 },
1961 '\n' => {
1962 buf.push('\n');
1963 },
1964 _ if prev == '\r' => {
1967 buf.push('\r');
1968 buf.push('\n');
1969 buf.push(ch);
1970 },
1971 _ => buf.push(ch),
1972 };
1973 prev = ch;
1974 }
1975 if prev == '\r' {
1977 buf.push('\n');
1978 }
1979 DOMString::from(buf)
1980 }
1981
1982 for entry in form_data.iter_mut() {
1983 entry.name = clean_crlf(&entry.name);
1985
1986 if let FormDatumValue::String(ref s) = entry.value {
1989 entry.value = FormDatumValue::String(clean_crlf(s));
1990 }
1991
1992 let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
1997 result.append(&mut boundary_bytes);
1998
1999 match entry.value {
2002 FormDatumValue::String(ref s) => {
2003 let content_disposition = format!("form-data; name=\"{}\"", entry.name);
2004 let mut bytes =
2005 format!("Content-Disposition: {content_disposition}\r\n\r\n{s}\r\n",)
2006 .into_bytes();
2007 result.append(&mut bytes);
2008 },
2009 FormDatumValue::File(ref f) => {
2010 let charset = encoding.name();
2011 let extra = if charset.to_lowercase() == "utf-8" {
2012 format!("filename=\"{}\"", String::from(f.name().str()))
2013 } else {
2014 format!(
2015 "filename*=\"{}\"''{}",
2016 charset,
2017 http_percent_encode(&f.name().as_bytes())
2018 )
2019 };
2020
2021 let content_disposition = format!("form-data; name=\"{}\"; {}", entry.name, extra);
2022 let content_type: Mime = f
2024 .upcast::<Blob>()
2025 .Type()
2026 .parse()
2027 .unwrap_or(mime::TEXT_PLAIN);
2028 let mut type_bytes = format!(
2029 "Content-Disposition: {}\r\nContent-Type: {}\r\n\r\n",
2030 content_disposition, content_type
2031 )
2032 .into_bytes();
2033 result.append(&mut type_bytes);
2034
2035 let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
2036
2037 result.append(&mut bytes);
2038 result.extend(b"\r\n");
2039 },
2040 }
2041 }
2042
2043 let mut boundary_bytes = format!("--{boundary}--\r\n").into_bytes();
2044 result.append(&mut boundary_bytes);
2045
2046 result
2047}
2048
2049pub(crate) fn generate_boundary() -> String {
2051 let i1 = random::<u32>();
2052 let i2 = random::<u32>();
2053
2054 format!("---------------------------{0}{1}", i1, i2)
2055}