1use std::borrow::ToOwned;
6use std::cell::Cell;
7
8use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
9use content_security_policy::sandboxing_directive::SandboxingFlagSet;
10use dom_struct::dom_struct;
11use encoding_rs::{Encoding, UTF_8};
12use headers::{ContentType, HeaderMapExt};
13use html5ever::{LocalName, Prefix, local_name, ns};
14use http::Method;
15use js::rust::HandleObject;
16use mime::{self, Mime};
17use net_traits::http_percent_encode;
18use net_traits::request::Referrer;
19use rand::random;
20use rustc_hash::FxBuildHasher;
21use script_bindings::match_domstring_ascii;
22use style::attr::AttrValue;
23use style::str::split_html_space_chars;
24use stylo_atoms::Atom;
25use stylo_dom::ElementState;
26
27use crate::body::Extractable;
28use crate::dom::attr::Attr;
29use crate::dom::bindings::cell::DomRefCell;
30use crate::dom::bindings::codegen::Bindings::AttrBinding::Attr_Binding::AttrMethods;
31use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
32use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
33use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
34use crate::dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
35use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
37use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
38use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
39use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
40use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
41use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
42use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
43use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
44use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
45use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement;
46use crate::dom::bindings::error::{Error, Fallible};
47use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
48use crate::dom::bindings::refcounted::Trusted;
49use crate::dom::bindings::reflector::{DomGlobal, DomObject};
50use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, MutNullableDom};
51use crate::dom::bindings::str::DOMString;
52use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
53use crate::dom::blob::Blob;
54use crate::dom::customelementregistry::CallbackReaction;
55use crate::dom::document::Document;
56use crate::dom::domtokenlist::DOMTokenList;
57use crate::dom::element::{AttributeMutation, AttributeMutationReason, Element};
58use crate::dom::event::{Event, EventBubbles, EventCancelable};
59use crate::dom::eventtarget::EventTarget;
60use crate::dom::file::File;
61use crate::dom::formdata::FormData;
62use crate::dom::formdataevent::FormDataEvent;
63use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
64use crate::dom::html::htmlcollection::CollectionFilter;
65use crate::dom::html::htmldatalistelement::HTMLDataListElement;
66use crate::dom::html::htmlelement::HTMLElement;
67use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
68use crate::dom::html::htmlformcontrolscollection::HTMLFormControlsCollection;
69use crate::dom::html::htmlimageelement::HTMLImageElement;
70use crate::dom::html::htmlinputelement::{HTMLInputElement, InputType};
71use crate::dom::html::htmllabelelement::HTMLLabelElement;
72use crate::dom::html::htmllegendelement::HTMLLegendElement;
73use crate::dom::html::htmlobjectelement::HTMLObjectElement;
74use crate::dom::html::htmloutputelement::HTMLOutputElement;
75use crate::dom::html::htmlselectelement::HTMLSelectElement;
76use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
77use crate::dom::node::{
78 BindContext, Node, NodeFlags, NodeTraits, UnbindContext, VecPreOrderInsertionHelper,
79};
80use crate::dom::nodelist::{NodeList, RadioListMode};
81use crate::dom::radionodelist::RadioNodeList;
82use crate::dom::submitevent::SubmitEvent;
83use crate::dom::types::HTMLIFrameElement;
84use crate::dom::virtualmethods::VirtualMethods;
85use crate::dom::window::Window;
86use crate::links::{LinkRelations, get_element_target};
87use crate::script_runtime::CanGc;
88use crate::script_thread::ScriptThread;
89
90#[dom_struct]
91pub(crate) struct HTMLFormElement {
92 htmlelement: HTMLElement,
93 marked_for_reset: Cell<bool>,
94 constructing_entry_list: Cell<bool>,
96 elements: DomOnceCell<HTMLFormControlsCollection>,
97 controls: DomRefCell<Vec<Dom<Element>>>,
98
99 #[expect(clippy::type_complexity)]
101 past_names_map:
102 DomRefCell<HashMapTracedValues<Atom, (Dom<Element>, NoTrace<usize>), FxBuildHasher>>,
103
104 current_name_generation: Cell<usize>,
106
107 firing_submission_events: Cell<bool>,
108 rel_list: MutNullableDom<DOMTokenList>,
109
110 planned_navigation: Cell<usize>,
112
113 #[no_trace]
114 relations: Cell<LinkRelations>,
115}
116
117impl HTMLFormElement {
118 fn new_inherited(
119 local_name: LocalName,
120 prefix: Option<Prefix>,
121 document: &Document,
122 ) -> HTMLFormElement {
123 HTMLFormElement {
124 htmlelement: HTMLElement::new_inherited_with_state(
125 ElementState::VALID,
126 local_name,
127 prefix,
128 document,
129 ),
130 marked_for_reset: Cell::new(false),
131 constructing_entry_list: Cell::new(false),
132 elements: Default::default(),
133 controls: DomRefCell::new(Vec::new()),
134 past_names_map: DomRefCell::new(HashMapTracedValues::new_fx()),
135 current_name_generation: Cell::new(0),
136 firing_submission_events: Cell::new(false),
137 rel_list: Default::default(),
138 planned_navigation: Default::default(),
139 relations: Cell::new(LinkRelations::empty()),
140 }
141 }
142
143 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 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
522 #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
524 enum SourcedNameSource {
525 Id,
526 Name,
527 Past(usize),
528 }
529
530 impl SourcedNameSource {
531 fn is_past(&self) -> bool {
532 matches!(self, SourcedNameSource::Past(..))
533 }
534 }
535
536 struct SourcedName {
537 name: Atom,
538 element: DomRoot<Element>,
539 source: SourcedNameSource,
540 }
541
542 let mut sourced_names_vec: Vec<SourcedName> = Vec::new();
543
544 for child in self.controls.borrow().iter() {
546 if child
547 .downcast::<HTMLElement>()
548 .is_some_and(|c| c.is_listed_element())
549 {
550 if let Some(id_atom) = child.get_id() {
551 let entry = SourcedName {
552 name: id_atom,
553 element: DomRoot::from_ref(child),
554 source: SourcedNameSource::Id,
555 };
556 sourced_names_vec.push(entry);
557 }
558 if let Some(name_atom) = child.get_name() {
559 let entry = SourcedName {
560 name: name_atom,
561 element: DomRoot::from_ref(child),
562 source: SourcedNameSource::Name,
563 };
564 sourced_names_vec.push(entry);
565 }
566 }
567 }
568
569 for child in self.controls.borrow().iter() {
571 if child.is::<HTMLImageElement>() {
572 if let Some(id_atom) = child.get_id() {
573 let entry = SourcedName {
574 name: id_atom,
575 element: DomRoot::from_ref(child),
576 source: SourcedNameSource::Id,
577 };
578 sourced_names_vec.push(entry);
579 }
580 if let Some(name_atom) = child.get_name() {
581 let entry = SourcedName {
582 name: name_atom,
583 element: DomRoot::from_ref(child),
584 source: SourcedNameSource::Name,
585 };
586 sourced_names_vec.push(entry);
587 }
588 }
589 }
590
591 let past_names_map = self.past_names_map.borrow();
593 for (key, val) in past_names_map.iter() {
594 let entry = SourcedName {
595 name: key.clone(),
596 element: DomRoot::from_ref(&*val.0),
597 source: SourcedNameSource::Past(self.current_name_generation.get() - val.1.0),
598 };
599 sourced_names_vec.push(entry);
600 }
601
602 sourced_names_vec.sort_by(|a, b| {
614 if a.element
615 .upcast::<Node>()
616 .CompareDocumentPosition(b.element.upcast::<Node>()) ==
617 0
618 {
619 if a.source.is_past() && b.source.is_past() {
620 b.source.cmp(&a.source)
621 } else {
622 a.source.cmp(&b.source)
623 }
624 } else if a
625 .element
626 .upcast::<Node>()
627 .CompareDocumentPosition(b.element.upcast::<Node>()) &
628 NodeConstants::DOCUMENT_POSITION_FOLLOWING ==
629 NodeConstants::DOCUMENT_POSITION_FOLLOWING
630 {
631 std::cmp::Ordering::Less
632 } else {
633 std::cmp::Ordering::Greater
634 }
635 });
636
637 sourced_names_vec.retain(|sn| !sn.name.to_string().is_empty());
639
640 let mut names_vec: Vec<DOMString> = Vec::new();
642 for elem in sourced_names_vec.iter() {
643 if !names_vec.iter().any(|name| *name == *elem.name) {
644 names_vec.push(DOMString::from(&*elem.name));
645 }
646 }
647
648 names_vec
649 }
650
651 fn CheckValidity(&self, can_gc: CanGc) -> bool {
653 self.static_validation(can_gc).is_ok()
654 }
655
656 fn ReportValidity(&self, can_gc: CanGc) -> bool {
658 self.interactive_validation(can_gc).is_ok()
659 }
660}
661
662#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
663pub(crate) enum SubmittedFrom {
664 FromForm,
665 NotFromForm,
666}
667
668#[derive(Clone, Copy, MallocSizeOf)]
669pub(crate) enum ResetFrom {
670 FromForm,
671 NotFromForm,
672}
673
674impl HTMLFormElement {
675 fn pick_encoding(&self) -> &'static Encoding {
677 if self
679 .upcast::<Element>()
680 .has_attribute(&local_name!("accept-charset"))
681 {
682 let input = self
684 .upcast::<Element>()
685 .get_string_attribute(&local_name!("accept-charset"));
686
687 let input = input.str();
689 let mut candidate_encodings =
690 split_html_space_chars(&input).filter_map(|c| Encoding::for_label(c.as_bytes()));
691
692 return candidate_encodings.next().unwrap_or(UTF_8);
694 }
695
696 self.owner_document().encoding()
698 }
699
700 fn encode_plaintext(&self, form_data: &mut [FormDatum]) -> String {
702 let mut result = String::new();
704
705 for entry in form_data.iter() {
707 let value = match &entry.value {
708 FormDatumValue::File(f) => f.name(),
709 FormDatumValue::String(s) => s,
710 };
711 result.push_str(&format!("{}={}\r\n", entry.name, value));
712 }
713
714 result
716 }
717
718 pub(crate) fn update_validity(&self, can_gc: CanGc) {
719 let is_any_invalid = self
720 .controls
721 .borrow()
722 .iter()
723 .any(|control| control.is_invalid(false, can_gc));
724
725 self.upcast::<Element>()
726 .set_state(ElementState::VALID, !is_any_invalid);
727 self.upcast::<Element>()
728 .set_state(ElementState::INVALID, is_any_invalid);
729 }
730
731 pub(crate) fn submit(
733 &self,
734 submit_method_flag: SubmittedFrom,
735 submitter: FormSubmitterElement,
736 can_gc: CanGc,
737 ) {
738 if self.upcast::<Element>().cannot_navigate() {
740 return;
741 }
742
743 if self.constructing_entry_list.get() {
745 return;
746 }
747 let doc = self.owner_document();
749
750 if doc.has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_FORMS_BROWSING_CONTEXT_FLAG)
753 {
754 return;
755 }
756
757 let base = doc.base_url();
758 if submit_method_flag == SubmittedFrom::NotFromForm {
761 if self.firing_submission_events.get() {
763 return;
764 }
765 self.firing_submission_events.set(true);
767 if !submitter.no_validate(self) && self.interactive_validation(can_gc).is_err() {
769 self.firing_submission_events.set(false);
770 return;
771 }
772 let submitter_button = match submitter {
776 FormSubmitterElement::Form(f) => {
777 if f == self {
778 None
779 } else {
780 Some(f.upcast::<HTMLElement>())
781 }
782 },
783 FormSubmitterElement::Input(i) => Some(i.upcast::<HTMLElement>()),
784 FormSubmitterElement::Button(b) => Some(b.upcast::<HTMLElement>()),
785 };
786
787 let event = SubmitEvent::new(
789 self.global().as_window(),
790 atom!("submit"),
791 true,
792 true,
793 submitter_button.map(DomRoot::from_ref),
794 can_gc,
795 );
796 let event = event.upcast::<Event>();
797 event.fire(self.upcast::<EventTarget>(), can_gc);
798
799 self.firing_submission_events.set(false);
801 if event.DefaultPrevented() {
803 return;
804 }
805 if self.upcast::<Element>().cannot_navigate() {
807 return;
808 }
809 }
810
811 let encoding = self.pick_encoding();
813
814 let mut form_data = match self.get_form_dataset(Some(submitter), Some(encoding), can_gc) {
816 Some(form_data) => form_data,
817 None => return,
818 };
819
820 if self.upcast::<Element>().cannot_navigate() {
822 return;
823 }
824
825 let mut action = submitter.action();
827
828 if action.is_empty() {
830 action = DOMString::from(base.as_str());
831 }
832 let action_components = match base.join(&action.str()) {
834 Ok(url) => url,
835 Err(_) => return,
836 };
837 let scheme = action_components.scheme().to_owned();
839 let enctype = submitter.enctype();
840 let method = submitter.method();
841
842 let target_attribute_value =
844 if submitter.is_submit_button() && submitter.target() != DOMString::new() {
845 Some(submitter.target())
846 } else {
847 let form_owner = submitter.form_owner();
848 let form = form_owner.as_deref().unwrap_or(self);
849 get_element_target(form.upcast::<Element>())
850 };
851
852 let noopener = self
854 .relations
855 .get()
856 .get_element_noopener(target_attribute_value.as_ref());
857
858 let source = doc.browsing_context().unwrap();
860 let (maybe_chosen, _new) = source
861 .choose_browsing_context(target_attribute_value.unwrap_or(DOMString::new()), noopener);
862
863 let chosen = match maybe_chosen {
865 Some(proxy) => proxy,
866 None => return,
867 };
868 let target_document = match chosen.document() {
869 Some(doc) => doc,
870 None => return,
871 };
872 let target_window = target_document.window();
874 let mut load_data = LoadData::new(
875 LoadOrigin::Script(doc.origin().snapshot()),
876 action_components,
877 target_document.about_base_url(),
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, false, 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 if let Some(window_proxy) = target.undiscarded_window_proxy() {
1067 if let Some(frame) = window_proxy
1068 .frame_element()
1069 .and_then(|e| e.downcast::<HTMLIFrameElement>())
1070 {
1071 frame.note_pending_navigation()
1072 }
1073 }
1074
1075 let form = Trusted::new(self);
1078 let window = Trusted::new(target);
1079 let task = task!(navigate_to_form_planned_navigation: move || {
1080 if planned_navigation != form.root().planned_navigation.get() {
1084 return;
1085 }
1086
1087 if ongoing_navigation != window.root().ongoing_navigation() {
1090 return;
1091 }
1092
1093 window
1095 .root()
1096 .load_url(
1097 NavigationHistoryBehavior::Push,
1098 false,
1099 load_data,
1100 CanGc::note(),
1101 );
1102 });
1103
1104 target
1109 .global()
1110 .task_manager()
1111 .dom_manipulation_task_source()
1112 .queue(task)
1113 }
1114
1115 fn interactive_validation(&self, can_gc: CanGc) -> Result<(), ()> {
1118 let unhandled_invalid_controls = match self.static_validation(can_gc) {
1123 Ok(()) => return Ok(()),
1124 Err(err) => err,
1125 };
1126
1127 let mut first = true;
1130
1131 for elem in unhandled_invalid_controls {
1132 if let Some(validatable) = elem.as_maybe_validatable() {
1133 error!("Validation error: {}", validatable.validation_message());
1134 }
1135 if first {
1136 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1137 html_elem.Focus(&FocusOptions::default(), can_gc);
1144 first = false;
1145 }
1146 }
1147 }
1148
1149 Err(())
1153 }
1154
1155 fn static_validation(&self, can_gc: CanGc) -> Result<(), Vec<DomRoot<Element>>> {
1158 let invalid_controls = self
1160 .controls
1161 .borrow()
1162 .iter()
1163 .filter_map(|field| {
1164 if let Some(element) = field.downcast::<Element>() {
1165 if element.is_invalid(true, can_gc) {
1166 Some(DomRoot::from_ref(element))
1167 } else {
1168 None
1169 }
1170 } else {
1171 None
1172 }
1173 })
1174 .collect::<Vec<DomRoot<Element>>>();
1175 if invalid_controls.is_empty() {
1177 return Ok(());
1178 }
1179 let unhandled_invalid_controls = invalid_controls
1181 .into_iter()
1182 .filter_map(|field| {
1183 let not_canceled = field
1186 .upcast::<EventTarget>()
1187 .fire_cancelable_event(atom!("invalid"), can_gc);
1188 if not_canceled {
1190 return Some(field);
1191 }
1192 None
1193 })
1194 .collect::<Vec<DomRoot<Element>>>();
1195 Err(unhandled_invalid_controls)
1197 }
1198
1199 fn get_unclean_dataset(
1204 &self,
1205 submitter: Option<FormSubmitterElement>,
1206 encoding: Option<&'static Encoding>,
1207 can_gc: CanGc,
1208 ) -> Vec<FormDatum> {
1209 let mut data_set = Vec::new();
1210 for child in self.controls.borrow().iter() {
1211 if child.disabled_state() {
1213 continue;
1214 }
1215 let child = child.upcast::<Node>();
1216
1217 if child.ancestors().any(|a| a.is::<HTMLDataListElement>()) {
1219 continue;
1220 }
1221 if let NodeTypeId::Element(ElementTypeId::HTMLElement(element)) = child.type_id() {
1222 match element {
1223 HTMLElementTypeId::HTMLInputElement => {
1224 let input = child.downcast::<HTMLInputElement>().unwrap();
1225 data_set.append(&mut input.form_datums(submitter, encoding));
1226 },
1227 HTMLElementTypeId::HTMLButtonElement => {
1228 let button = child.downcast::<HTMLButtonElement>().unwrap();
1229 if let Some(datum) = button.form_datum(submitter) {
1230 data_set.push(datum);
1231 }
1232 },
1233 HTMLElementTypeId::HTMLObjectElement => {
1234 },
1236 HTMLElementTypeId::HTMLSelectElement => {
1237 let select = child.downcast::<HTMLSelectElement>().unwrap();
1238 select.push_form_data(&mut data_set);
1239 },
1240 HTMLElementTypeId::HTMLTextAreaElement => {
1241 let textarea = child.downcast::<HTMLTextAreaElement>().unwrap();
1242 let name = textarea.Name();
1243 if !name.is_empty() {
1244 data_set.push(FormDatum {
1245 ty: textarea.Type(),
1246 name,
1247 value: FormDatumValue::String(textarea.Value()),
1248 });
1249 }
1250 },
1251 HTMLElementTypeId::HTMLElement => {
1252 let custom = child.downcast::<HTMLElement>().unwrap();
1253 if custom.is_form_associated_custom_element() {
1254 let internals =
1256 custom.upcast::<Element>().ensure_element_internals(can_gc);
1257 internals.perform_entry_construction(&mut data_set);
1258 }
1260 },
1261 _ => (),
1262 }
1263 }
1264
1265 let child_element = child.downcast::<Element>().unwrap();
1269 let input_matches = child_element
1270 .downcast::<HTMLInputElement>()
1271 .is_some_and(|input| {
1272 matches!(input.input_type(), InputType::Text | InputType::Search)
1273 });
1274 let textarea_matches = child_element.is::<HTMLTextAreaElement>();
1275 let dirname = child_element.get_string_attribute(&local_name!("dirname"));
1276 if (input_matches || textarea_matches) && !dirname.is_empty() {
1277 let dir = DOMString::from(child_element.directionality());
1278 data_set.push(FormDatum {
1279 ty: DOMString::from("string"),
1280 name: dirname,
1281 value: FormDatumValue::String(dir),
1282 });
1283 }
1284 }
1285 data_set
1286 }
1287
1288 pub(crate) fn get_form_dataset(
1290 &self,
1291 submitter: Option<FormSubmitterElement>,
1292 encoding: Option<&'static Encoding>,
1293 can_gc: CanGc,
1294 ) -> Option<Vec<FormDatum>> {
1295 if self.constructing_entry_list.get() {
1297 return None;
1298 }
1299
1300 self.constructing_entry_list.set(true);
1302
1303 let ret = self.get_unclean_dataset(submitter, encoding, can_gc);
1305
1306 let window = self.owner_window();
1307
1308 let form_data = FormData::new(Some(ret), &window.global(), can_gc);
1310
1311 let event = FormDataEvent::new(
1313 &window,
1314 atom!("formdata"),
1315 EventBubbles::Bubbles,
1316 EventCancelable::NotCancelable,
1317 &form_data,
1318 can_gc,
1319 );
1320
1321 event
1322 .upcast::<Event>()
1323 .fire(self.upcast::<EventTarget>(), can_gc);
1324
1325 self.constructing_entry_list.set(false);
1327
1328 Some(form_data.datums())
1330 }
1331
1332 pub(crate) fn reset(&self, _reset_method_flag: ResetFrom, can_gc: CanGc) {
1334 if self.marked_for_reset.get() {
1336 return;
1337 } else {
1338 self.marked_for_reset.set(true);
1339 }
1340
1341 let reset = self
1345 .upcast::<EventTarget>()
1346 .fire_bubbling_cancelable_event(atom!("reset"), can_gc);
1347 if !reset {
1348 return;
1349 }
1350
1351 let controls: Vec<_> = self
1352 .controls
1353 .borrow()
1354 .iter()
1355 .map(|c| c.as_rooted())
1356 .collect();
1357
1358 for child in controls {
1359 child.reset(can_gc);
1360 }
1361 self.marked_for_reset.set(false);
1362 }
1363
1364 fn add_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1365 {
1366 let root = self.upcast::<Element>().root_element();
1367 let root = root.upcast::<Node>();
1368 let mut controls = self.controls.borrow_mut();
1369 controls.insert_pre_order(control.to_element(), root);
1370 }
1371 self.update_validity(can_gc);
1372 }
1373
1374 fn remove_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1375 {
1376 let control = control.to_element();
1377 let mut controls = self.controls.borrow_mut();
1378 controls
1379 .iter()
1380 .position(|c| &**c == control)
1381 .map(|idx| controls.remove(idx));
1382
1383 let mut past_names_map = self.past_names_map.borrow_mut();
1388 past_names_map.0.retain(|_k, v| v.0 != control);
1389 }
1390 self.update_validity(can_gc);
1391 }
1392}
1393
1394impl Element {
1395 pub(crate) fn is_resettable(&self) -> bool {
1396 let NodeTypeId::Element(ElementTypeId::HTMLElement(element_type)) =
1397 self.upcast::<Node>().type_id()
1398 else {
1399 return false;
1400 };
1401 matches!(
1402 element_type,
1403 HTMLElementTypeId::HTMLInputElement |
1404 HTMLElementTypeId::HTMLSelectElement |
1405 HTMLElementTypeId::HTMLTextAreaElement |
1406 HTMLElementTypeId::HTMLOutputElement |
1407 HTMLElementTypeId::HTMLElement
1408 )
1409 }
1410
1411 pub(crate) fn reset(&self, can_gc: CanGc) {
1412 if !self.is_resettable() {
1413 return;
1414 }
1415
1416 if let Some(input_element) = self.downcast::<HTMLInputElement>() {
1417 input_element.reset(can_gc);
1418 } else if let Some(select_element) = self.downcast::<HTMLSelectElement>() {
1419 select_element.reset();
1420 } else if let Some(textarea_element) = self.downcast::<HTMLTextAreaElement>() {
1421 textarea_element.reset(can_gc);
1422 } else if let Some(output_element) = self.downcast::<HTMLOutputElement>() {
1423 output_element.reset(can_gc);
1424 } else if let Some(html_element) = self.downcast::<HTMLElement>() {
1425 if html_element.is_form_associated_custom_element() {
1426 ScriptThread::enqueue_callback_reaction(
1427 html_element.upcast::<Element>(),
1428 CallbackReaction::FormReset,
1429 None,
1430 )
1431 }
1432 }
1433 }
1434}
1435
1436#[derive(Clone, JSTraceable, MallocSizeOf)]
1437pub(crate) enum FormDatumValue {
1438 File(DomRoot<File>),
1439 String(DOMString),
1440}
1441
1442#[derive(Clone, JSTraceable, MallocSizeOf)]
1443pub(crate) struct FormDatum {
1444 pub(crate) ty: DOMString,
1445 pub(crate) name: DOMString,
1446 pub(crate) value: FormDatumValue,
1447}
1448
1449impl FormDatum {
1450 pub(crate) fn replace_value(&self, charset: &str) -> String {
1451 if self.name.to_ascii_lowercase() == "_charset_" && self.ty == "hidden" {
1452 return charset.to_string();
1453 }
1454
1455 match self.value {
1456 FormDatumValue::File(ref f) => String::from(f.name().clone()),
1457 FormDatumValue::String(ref s) => String::from(s.clone()),
1458 }
1459 }
1460}
1461
1462#[derive(Clone, Copy, MallocSizeOf)]
1463pub(crate) enum FormEncType {
1464 TextPlain,
1465 UrlEncoded,
1466 MultipartFormData,
1467}
1468
1469#[derive(Clone, Copy, MallocSizeOf)]
1470pub(crate) enum FormMethod {
1471 Get,
1472 Post,
1473 Dialog,
1474}
1475
1476#[derive(Clone, Copy, MallocSizeOf)]
1478pub(crate) enum FormSubmitterElement<'a> {
1479 Form(&'a HTMLFormElement),
1480 Input(&'a HTMLInputElement),
1481 Button(&'a HTMLButtonElement),
1482 }
1485
1486impl FormSubmitterElement<'_> {
1487 fn action(&self) -> DOMString {
1488 match *self {
1489 FormSubmitterElement::Form(form) => form.Action(),
1490 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1491 &local_name!("formaction"),
1492 |i| i.FormAction(),
1493 |f| f.Action(),
1494 ),
1495 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1496 &local_name!("formaction"),
1497 |i| i.FormAction(),
1498 |f| f.Action(),
1499 ),
1500 }
1501 }
1502
1503 fn enctype(&self) -> FormEncType {
1504 let attr = match *self {
1505 FormSubmitterElement::Form(form) => form.Enctype(),
1506 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1507 &local_name!("formenctype"),
1508 |i| i.FormEnctype(),
1509 |f| f.Enctype(),
1510 ),
1511 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1512 &local_name!("formenctype"),
1513 |i| i.FormEnctype(),
1514 |f| f.Enctype(),
1515 ),
1516 };
1517 match_domstring_ascii!(attr,
1520 "multipart/form-data" => FormEncType::MultipartFormData,
1521 "text/plain" => FormEncType::TextPlain,
1522 _ => FormEncType::UrlEncoded,
1523 )
1524 }
1525
1526 fn method(&self) -> FormMethod {
1527 let attr = match *self {
1528 FormSubmitterElement::Form(form) => form.Method(),
1529 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1530 &local_name!("formmethod"),
1531 |i| i.FormMethod(),
1532 |f| f.Method(),
1533 ),
1534 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1535 &local_name!("formmethod"),
1536 |i| i.FormMethod(),
1537 |f| f.Method(),
1538 ),
1539 };
1540 match_domstring_ascii!(attr,
1541 "dialog" => FormMethod::Dialog,
1542 "post" => FormMethod::Post,
1543 _ => FormMethod::Get,
1544 )
1545 }
1546
1547 fn target(&self) -> DOMString {
1548 match *self {
1549 FormSubmitterElement::Form(form) => form.Target(),
1550 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1551 &local_name!("formtarget"),
1552 |i| i.FormTarget(),
1553 |f| f.Target(),
1554 ),
1555 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1556 &local_name!("formtarget"),
1557 |i| i.FormTarget(),
1558 |f| f.Target(),
1559 ),
1560 }
1561 }
1562
1563 fn no_validate(&self, _form_owner: &HTMLFormElement) -> bool {
1564 match *self {
1565 FormSubmitterElement::Form(form) => form.NoValidate(),
1566 FormSubmitterElement::Input(input_element) => input_element.get_form_boolean_attribute(
1567 &local_name!("formnovalidate"),
1568 |i| i.FormNoValidate(),
1569 |f| f.NoValidate(),
1570 ),
1571 FormSubmitterElement::Button(button_element) => button_element
1572 .get_form_boolean_attribute(
1573 &local_name!("formnovalidate"),
1574 |i| i.FormNoValidate(),
1575 |f| f.NoValidate(),
1576 ),
1577 }
1578 }
1579
1580 pub(crate) fn is_submit_button(&self) -> bool {
1582 match *self {
1583 FormSubmitterElement::Input(input_element) => input_element.is_submit_button(),
1586 FormSubmitterElement::Button(button_element) => button_element.is_submit_button(),
1588 _ => false,
1589 }
1590 }
1591
1592 pub(crate) fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1594 match *self {
1595 FormSubmitterElement::Button(button_el) => button_el.form_owner(),
1596 FormSubmitterElement::Input(input_el) => input_el.form_owner(),
1597 _ => None,
1598 }
1599 }
1600}
1601
1602pub(crate) trait FormControl: DomObject {
1603 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>>;
1604
1605 fn set_form_owner(&self, form: Option<&HTMLFormElement>);
1606
1607 fn to_element(&self) -> ∈
1608
1609 fn is_listed(&self) -> bool {
1610 true
1611 }
1612
1613 fn set_form_owner_from_parser(&self, form: &HTMLFormElement, can_gc: CanGc) {
1618 let elem = self.to_element();
1619 let node = elem.upcast::<Node>();
1620 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, true);
1621 form.add_control(self, can_gc);
1622 self.set_form_owner(Some(form));
1623 }
1624
1625 fn reset_form_owner(&self, can_gc: CanGc) {
1627 let elem = self.to_element();
1628 let node = elem.upcast::<Node>();
1629 let old_owner = self.form_owner();
1630 let has_form_id = elem.has_attribute(&local_name!("form"));
1631 let nearest_form_ancestor = node
1632 .ancestors()
1633 .find_map(DomRoot::downcast::<HTMLFormElement>);
1634
1635 if old_owner.is_some() &&
1637 !(self.is_listed() && has_form_id) &&
1638 nearest_form_ancestor == old_owner
1639 {
1640 return;
1641 }
1642
1643 let new_owner = if self.is_listed() && has_form_id && elem.is_connected() {
1644 let doc = node.owner_document();
1646 let form_id = elem.get_string_attribute(&local_name!("form"));
1647 doc.GetElementById(form_id)
1648 .and_then(DomRoot::downcast::<HTMLFormElement>)
1649 } else {
1650 nearest_form_ancestor
1652 };
1653
1654 if old_owner != new_owner {
1655 if let Some(o) = old_owner {
1656 o.remove_control(self, can_gc);
1657 }
1658 if let Some(ref new_owner) = new_owner {
1659 new_owner.add_control(self, can_gc);
1660 }
1661 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1663 if html_elem.is_form_associated_custom_element() {
1664 ScriptThread::enqueue_callback_reaction(
1665 elem,
1666 CallbackReaction::FormAssociated(
1667 new_owner.as_ref().map(|form| DomRoot::from_ref(&**form)),
1668 ),
1669 None,
1670 )
1671 }
1672 }
1673 self.set_form_owner(new_owner.as_deref());
1674 }
1675 }
1676
1677 fn form_attribute_mutated(&self, mutation: AttributeMutation, can_gc: CanGc) {
1679 match mutation {
1680 AttributeMutation::Set(..) => {
1681 self.register_if_necessary();
1682 },
1683 AttributeMutation::Removed => {
1684 self.unregister_if_necessary();
1685 },
1686 }
1687
1688 self.reset_form_owner(can_gc);
1689 }
1690
1691 fn register_if_necessary(&self) {
1693 let elem = self.to_element();
1694 let form_id = elem.get_string_attribute(&local_name!("form"));
1695 let node = elem.upcast::<Node>();
1696
1697 if self.is_listed() && !form_id.is_empty() && node.is_connected() {
1698 node.owner_document()
1699 .register_form_id_listener(form_id, self);
1700 }
1701 }
1702
1703 fn unregister_if_necessary(&self) {
1704 let elem = self.to_element();
1705 let form_id = elem.get_string_attribute(&local_name!("form"));
1706
1707 if self.is_listed() && !form_id.is_empty() {
1708 elem.owner_document()
1709 .unregister_form_id_listener(form_id, self);
1710 }
1711 }
1712
1713 fn bind_form_control_to_tree(&self, can_gc: CanGc) {
1715 let elem = self.to_element();
1716 let node = elem.upcast::<Node>();
1717
1718 let must_skip_reset = node.get_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER);
1723 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, false);
1724
1725 if !must_skip_reset {
1726 self.form_attribute_mutated(
1727 AttributeMutation::Set(None, AttributeMutationReason::Directly),
1728 can_gc,
1729 );
1730 }
1731 }
1732
1733 fn unbind_form_control_from_tree(&self, can_gc: CanGc) {
1735 let elem = self.to_element();
1736 let has_form_attr = elem.has_attribute(&local_name!("form"));
1737 let same_subtree = self
1738 .form_owner()
1739 .is_none_or(|form| elem.is_in_same_home_subtree(&*form));
1740
1741 self.unregister_if_necessary();
1742
1743 if !same_subtree || (self.is_listed() && has_form_attr) {
1749 self.reset_form_owner(can_gc);
1750 }
1751 }
1752
1753 fn get_form_attribute<InputFn, OwnerFn>(
1754 &self,
1755 attr: &LocalName,
1756 input: InputFn,
1757 owner: OwnerFn,
1758 ) -> DOMString
1759 where
1760 InputFn: Fn(&Self) -> DOMString,
1761 OwnerFn: Fn(&HTMLFormElement) -> DOMString,
1762 Self: Sized,
1763 {
1764 if self.to_element().has_attribute(attr) {
1765 input(self)
1766 } else {
1767 self.form_owner().map_or(DOMString::new(), |t| owner(&t))
1768 }
1769 }
1770
1771 fn get_form_boolean_attribute<InputFn, OwnerFn>(
1772 &self,
1773 attr: &LocalName,
1774 input: InputFn,
1775 owner: OwnerFn,
1776 ) -> bool
1777 where
1778 InputFn: Fn(&Self) -> bool,
1779 OwnerFn: Fn(&HTMLFormElement) -> bool,
1780 Self: Sized,
1781 {
1782 if self.to_element().has_attribute(attr) {
1783 input(self)
1784 } else {
1785 self.form_owner().is_some_and(|t| owner(&t))
1786 }
1787 }
1788
1789 fn is_candidate_for_constraint_validation(&self) -> bool {
1791 let element = self.to_element();
1792 let html_element = element.downcast::<HTMLElement>();
1793 if let Some(html_element) = html_element {
1794 html_element.is_submittable_element() || element.is_instance_validatable()
1795 } else {
1796 false
1797 }
1798 }
1799
1800 }
1803
1804impl VirtualMethods for HTMLFormElement {
1805 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1806 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1807 }
1808
1809 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1810 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1811
1812 rooted_vec!(let mut to_reset);
1815 to_reset.extend(
1816 self.controls
1817 .borrow()
1818 .iter()
1819 .filter(|c| !c.is_in_same_home_subtree(self))
1820 .cloned(),
1821 );
1822
1823 for control in to_reset.iter() {
1824 control
1825 .as_maybe_form_control()
1826 .expect("Element must be a form control")
1827 .reset_form_owner(can_gc);
1828 }
1829 }
1830
1831 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1832 match name {
1833 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
1834 _ => self
1835 .super_type()
1836 .unwrap()
1837 .parse_plain_attribute(name, value),
1838 }
1839 }
1840
1841 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1842 self.super_type()
1843 .unwrap()
1844 .attribute_mutated(attr, mutation, can_gc);
1845
1846 match *attr.local_name() {
1847 local_name!("rel") | local_name!("rev") => {
1848 self.relations
1849 .set(LinkRelations::for_element(self.upcast()));
1850 },
1851 _ => {},
1852 }
1853 }
1854
1855 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1856 if let Some(s) = self.super_type() {
1857 s.bind_to_tree(context, can_gc);
1858 }
1859
1860 self.relations
1861 .set(LinkRelations::for_element(self.upcast()));
1862 }
1863}
1864
1865pub(crate) trait FormControlElementHelpers {
1866 fn as_maybe_form_control(&self) -> Option<&dyn FormControl>;
1867}
1868
1869impl FormControlElementHelpers for Element {
1870 fn as_maybe_form_control(&self) -> Option<&dyn FormControl> {
1871 let node = self.upcast::<Node>();
1872
1873 match node.type_id() {
1874 NodeTypeId::Element(ElementTypeId::HTMLElement(
1875 HTMLElementTypeId::HTMLButtonElement,
1876 )) => Some(self.downcast::<HTMLButtonElement>().unwrap() as &dyn FormControl),
1877 NodeTypeId::Element(ElementTypeId::HTMLElement(
1878 HTMLElementTypeId::HTMLFieldSetElement,
1879 )) => Some(self.downcast::<HTMLFieldSetElement>().unwrap() as &dyn FormControl),
1880 NodeTypeId::Element(ElementTypeId::HTMLElement(
1881 HTMLElementTypeId::HTMLImageElement,
1882 )) => Some(self.downcast::<HTMLImageElement>().unwrap() as &dyn FormControl),
1883 NodeTypeId::Element(ElementTypeId::HTMLElement(
1884 HTMLElementTypeId::HTMLInputElement,
1885 )) => Some(self.downcast::<HTMLInputElement>().unwrap() as &dyn FormControl),
1886 NodeTypeId::Element(ElementTypeId::HTMLElement(
1887 HTMLElementTypeId::HTMLLabelElement,
1888 )) => Some(self.downcast::<HTMLLabelElement>().unwrap() as &dyn FormControl),
1889 NodeTypeId::Element(ElementTypeId::HTMLElement(
1890 HTMLElementTypeId::HTMLLegendElement,
1891 )) => Some(self.downcast::<HTMLLegendElement>().unwrap() as &dyn FormControl),
1892 NodeTypeId::Element(ElementTypeId::HTMLElement(
1893 HTMLElementTypeId::HTMLObjectElement,
1894 )) => Some(self.downcast::<HTMLObjectElement>().unwrap() as &dyn FormControl),
1895 NodeTypeId::Element(ElementTypeId::HTMLElement(
1896 HTMLElementTypeId::HTMLOutputElement,
1897 )) => Some(self.downcast::<HTMLOutputElement>().unwrap() as &dyn FormControl),
1898 NodeTypeId::Element(ElementTypeId::HTMLElement(
1899 HTMLElementTypeId::HTMLSelectElement,
1900 )) => Some(self.downcast::<HTMLSelectElement>().unwrap() as &dyn FormControl),
1901 NodeTypeId::Element(ElementTypeId::HTMLElement(
1902 HTMLElementTypeId::HTMLTextAreaElement,
1903 )) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
1904 _ => self.downcast::<HTMLElement>().and_then(|elem| {
1905 if elem.is_form_associated_custom_element() {
1906 Some(elem as &dyn FormControl)
1907 } else {
1908 None
1909 }
1910 }),
1911 }
1912 }
1913}
1914
1915pub(crate) fn encode_multipart_form_data(
1917 form_data: &mut [FormDatum],
1918 boundary: String,
1919 encoding: &'static Encoding,
1920) -> Vec<u8> {
1921 let mut result = vec![];
1922
1923 fn clean_crlf(s: &DOMString) -> DOMString {
1925 let mut buf = "".to_owned();
1926 let mut prev = ' ';
1927 for ch in s.str().chars() {
1928 match ch {
1929 '\n' if prev != '\r' => {
1930 buf.push('\r');
1931 buf.push('\n');
1932 },
1933 '\n' => {
1934 buf.push('\n');
1935 },
1936 _ if prev == '\r' => {
1939 buf.push('\r');
1940 buf.push('\n');
1941 buf.push(ch);
1942 },
1943 _ => buf.push(ch),
1944 };
1945 prev = ch;
1946 }
1947 if prev == '\r' {
1949 buf.push('\n');
1950 }
1951 DOMString::from(buf)
1952 }
1953
1954 for entry in form_data.iter_mut() {
1955 entry.name = clean_crlf(&entry.name);
1957
1958 if let FormDatumValue::String(ref s) = entry.value {
1961 entry.value = FormDatumValue::String(clean_crlf(s));
1962 }
1963
1964 let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
1969 result.append(&mut boundary_bytes);
1970
1971 match entry.value {
1974 FormDatumValue::String(ref s) => {
1975 let content_disposition = format!("form-data; name=\"{}\"", entry.name);
1976 let mut bytes =
1977 format!("Content-Disposition: {content_disposition}\r\n\r\n{s}\r\n",)
1978 .into_bytes();
1979 result.append(&mut bytes);
1980 },
1981 FormDatumValue::File(ref f) => {
1982 let charset = encoding.name();
1983 let extra = if charset.to_lowercase() == "utf-8" {
1984 format!("filename=\"{}\"", String::from(f.name().str()))
1985 } else {
1986 format!(
1987 "filename*=\"{}\"''{}",
1988 charset,
1989 http_percent_encode(&f.name().as_bytes())
1990 )
1991 };
1992
1993 let content_disposition = format!("form-data; name=\"{}\"; {}", entry.name, extra);
1994 let content_type: Mime = f
1996 .upcast::<Blob>()
1997 .Type()
1998 .parse()
1999 .unwrap_or(mime::TEXT_PLAIN);
2000 let mut type_bytes = format!(
2001 "Content-Disposition: {}\r\nContent-Type: {}\r\n\r\n",
2002 content_disposition, content_type
2003 )
2004 .into_bytes();
2005 result.append(&mut type_bytes);
2006
2007 let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
2008
2009 result.append(&mut bytes);
2010 result.extend(b"\r\n");
2011 },
2012 }
2013 }
2014
2015 let mut boundary_bytes = format!("--{boundary}--\r\n").into_bytes();
2016 result.append(&mut boundary_bytes);
2017
2018 result
2019}
2020
2021pub(crate) fn generate_boundary() -> String {
2023 let i1 = random::<u32>();
2024 let i2 = random::<u32>();
2025
2026 format!("---------------------------{0}{1}", i1, i2)
2027}