1use std::borrow::{Cow, ToOwned};
6use std::cell::Cell;
7
8use content_security_policy::sandboxing_directive::SandboxingFlagSet;
9use dom_struct::dom_struct;
10use encoding_rs::{Encoding, UTF_8};
11use headers::{ContentType, HeaderMapExt};
12use html5ever::{LocalName, Prefix, local_name};
13use http::Method;
14use js::context::JSContext;
15use js::rust::HandleObject;
16use mime::{self, Mime};
17use net_traits::request::Referrer;
18use rand::random;
19use rustc_hash::FxBuildHasher;
20use script_bindings::cell::DomRefCell;
21use script_bindings::codegen::GenericBindings::DocumentFragmentBinding::DocumentFragmentMethods;
22use script_bindings::match_domstring_ascii;
23use script_bindings::reflector::DomObject;
24use servo_constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior};
25use style::attr::AttrValue;
26use style::str::split_html_space_chars;
27use stylo_atoms::Atom;
28use stylo_dom::ElementState;
29
30use crate::body::Extractable;
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;
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::attributes::storage::AttrRef;
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::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::html::input_element::HTMLInputElement;
78use crate::dom::input_element::input_type::InputType;
79use crate::dom::node::{Node, NodeFlags, NodeTraits, UnbindContext, VecPreOrderInsertionHelper};
80use crate::dom::nodelist::{NodeList, RadioListMode};
81use crate::dom::radionodelist::RadioNodeList;
82use crate::dom::submitevent::SubmitEvent;
83use crate::dom::types::{DocumentFragment, HTMLIFrameElement};
84use crate::dom::virtualmethods::VirtualMethods;
85use crate::dom::window::Window;
86use crate::links::{LinkRelations, get_element_target, valid_navigable_target_name_or_keyword};
87use crate::navigation::navigate;
88use crate::script_runtime::CanGc;
89use crate::script_thread::ScriptThread;
90
91#[dom_struct]
93pub(crate) struct HTMLFormElement {
94 htmlelement: HTMLElement,
95 marked_for_reset: Cell<bool>,
96 constructing_entry_list: Cell<bool>,
98 elements: DomOnceCell<HTMLFormControlsCollection>,
99 controls: DomRefCell<Vec<Dom<Element>>>,
100
101 #[expect(clippy::type_complexity)]
103 past_names_map:
104 DomRefCell<HashMapTracedValues<Atom, (Dom<Element>, NoTrace<usize>), FxBuildHasher>>,
105
106 current_name_generation: Cell<usize>,
108
109 firing_submission_events: Cell<bool>,
110 rel_list: MutNullableDom<DOMTokenList>,
111
112 planned_navigation: Cell<usize>,
114
115 #[no_trace]
117 relations: Cell<LinkRelations>,
118}
119
120impl HTMLFormElement {
121 fn new_inherited(
122 local_name: LocalName,
123 prefix: Option<Prefix>,
124 document: &Document,
125 ) -> HTMLFormElement {
126 HTMLFormElement {
127 htmlelement: HTMLElement::new_inherited_with_state(
128 ElementState::VALID,
129 local_name,
130 prefix,
131 document,
132 ),
133 marked_for_reset: Cell::new(false),
134 constructing_entry_list: Cell::new(false),
135 elements: Default::default(),
136 controls: DomRefCell::new(Vec::new()),
137 past_names_map: DomRefCell::new(HashMapTracedValues::new_fx()),
138 current_name_generation: Cell::new(0),
139 firing_submission_events: Cell::new(false),
140 rel_list: Default::default(),
141 planned_navigation: Default::default(),
142 relations: Cell::new(LinkRelations::empty()),
143 }
144 }
145
146 pub(crate) fn new(
147 cx: &mut JSContext,
148 local_name: LocalName,
149 prefix: Option<Prefix>,
150 document: &Document,
151 proto: Option<HandleObject>,
152 ) -> DomRoot<HTMLFormElement> {
153 Node::reflect_node_with_proto(
154 cx,
155 Box::new(HTMLFormElement::new_inherited(local_name, prefix, document)),
156 document,
157 proto,
158 )
159 }
160
161 fn filter_for_radio_list(mode: RadioListMode, child: &Element, name: &Atom) -> bool {
162 if let Some(child) = child.downcast::<Element>() {
163 match mode {
164 RadioListMode::ControlsExceptImageInputs => {
165 if child
166 .downcast::<HTMLElement>()
167 .is_some_and(|c| c.is_listed_element()) &&
168 (child.get_id().is_some_and(|i| i == *name) ||
169 child.get_name().is_some_and(|n| n == *name))
170 {
171 if let Some(inp) = child.downcast::<HTMLInputElement>() {
172 return !matches!(*inp.input_type(), InputType::Image(_));
174 } else {
175 return true;
177 }
178 }
179 return false;
180 },
181 RadioListMode::Images => {
182 return child.is::<HTMLImageElement>() &&
183 (child.get_id().is_some_and(|i| i == *name) ||
184 child.get_name().is_some_and(|n| n == *name));
185 },
186 }
187 }
188 false
189 }
190
191 pub(crate) fn nth_for_radio_list(
192 &self,
193 index: u32,
194 mode: RadioListMode,
195 name: &Atom,
196 ) -> Option<DomRoot<Node>> {
197 self.controls
198 .borrow()
199 .iter()
200 .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
201 .nth(index as usize)
202 .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
203 }
204
205 pub(crate) fn count_for_radio_list(&self, mode: RadioListMode, name: &Atom) -> u32 {
206 self.controls
207 .borrow()
208 .iter()
209 .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
210 .count() as u32
211 }
212}
213
214impl HTMLFormElementMethods<crate::DomTypeHolder> for HTMLFormElement {
215 make_getter!(AcceptCharset, "accept-charset");
217
218 make_setter!(SetAcceptCharset, "accept-charset");
220
221 make_form_action_getter!(Action, "action");
223
224 make_setter!(SetAction, "action");
226
227 make_enumerated_getter!(
229 Autocomplete,
230 "autocomplete",
231 "on" | "off",
232 missing => "on",
233 invalid => "on"
234 );
235
236 make_setter!(SetAutocomplete, "autocomplete");
238
239 make_enumerated_getter!(
241 Enctype,
242 "enctype",
243 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
244 missing => "application/x-www-form-urlencoded",
245 invalid => "application/x-www-form-urlencoded"
246 );
247
248 make_setter!(SetEnctype, "enctype");
250
251 fn Encoding(&self) -> DOMString {
253 self.Enctype()
254 }
255
256 fn SetEncoding(&self, cx: &mut JSContext, value: DOMString) {
258 self.SetEnctype(cx, value)
259 }
260
261 make_enumerated_getter!(
263 Method,
264 "method",
265 "get" | "post" | "dialog",
266 missing => "get",
267 invalid => "get"
268 );
269
270 make_setter!(SetMethod, "method");
272
273 make_getter!(Name, "name");
275
276 make_atomic_setter!(SetName, "name");
278
279 make_bool_getter!(NoValidate, "novalidate");
281
282 make_bool_setter!(SetNoValidate, "novalidate");
284
285 make_getter!(Target, "target");
287
288 make_setter!(SetTarget, "target");
290
291 make_getter!(Rel, "rel");
293
294 fn Submit(&self, cx: &mut JSContext) {
296 self.submit(
297 cx,
298 SubmittedFrom::FromForm,
299 FormSubmitterElement::Form(self),
300 );
301 }
302
303 fn RequestSubmit(&self, cx: &mut JSContext, submitter: Option<&HTMLElement>) -> Fallible<()> {
305 let submitter: FormSubmitterElement = match submitter {
306 Some(submitter_element) => {
307 let error_not_a_submit_button =
309 Err(Error::Type(c"submitter must be a submit button".to_owned()));
310
311 let element = match submitter_element.upcast::<Node>().type_id() {
312 NodeTypeId::Element(ElementTypeId::HTMLElement(element)) => element,
313 _ => {
314 return error_not_a_submit_button;
315 },
316 };
317
318 let submit_button = match element {
319 HTMLElementTypeId::HTMLInputElement => FormSubmitterElement::Input(
320 submitter_element
321 .downcast::<HTMLInputElement>()
322 .expect("Failed to downcast submitter elem to HTMLInputElement."),
323 ),
324 HTMLElementTypeId::HTMLButtonElement => FormSubmitterElement::Button(
325 submitter_element
326 .downcast::<HTMLButtonElement>()
327 .expect("Failed to downcast submitter elem to HTMLButtonElement."),
328 ),
329 _ => {
330 return error_not_a_submit_button;
331 },
332 };
333
334 if !submit_button.is_submit_button() {
335 return error_not_a_submit_button;
336 }
337
338 let submitters_owner = submit_button.form_owner();
339
340 let owner = match submitters_owner {
342 Some(owner) => owner,
343 None => {
344 return Err(Error::NotFound(None));
345 },
346 };
347
348 if *owner != *self {
349 return Err(Error::NotFound(None));
350 }
351
352 submit_button
353 },
354 None => {
355 FormSubmitterElement::Form(self)
357 },
358 };
359 self.submit(cx, SubmittedFrom::NotFromForm, submitter);
361 Ok(())
362 }
363
364 fn Reset(&self, cx: &mut JSContext) {
366 self.reset(cx, ResetFrom::FromForm);
367 }
368
369 fn Elements(&self, cx: &mut JSContext) -> DomRoot<HTMLFormControlsCollection> {
371 #[derive(JSTraceable, MallocSizeOf)]
372 struct ElementsFilter {
373 form: DomRoot<HTMLFormElement>,
374 }
375 impl CollectionFilter for ElementsFilter {
376 fn filter<'a>(&self, elem: &'a Element, _root: &'a Node) -> bool {
377 let form_owner = match elem.upcast::<Node>().type_id() {
378 NodeTypeId::Element(ElementTypeId::HTMLElement(t)) => match t {
379 HTMLElementTypeId::HTMLButtonElement => {
380 elem.downcast::<HTMLButtonElement>().unwrap().form_owner()
381 },
382 HTMLElementTypeId::HTMLFieldSetElement => {
383 elem.downcast::<HTMLFieldSetElement>().unwrap().form_owner()
384 },
385 HTMLElementTypeId::HTMLInputElement => {
386 let input_elem = elem.downcast::<HTMLInputElement>().unwrap();
387 if matches!(*input_elem.input_type(), InputType::Image(_)) {
388 return false;
389 }
390 input_elem.form_owner()
391 },
392 HTMLElementTypeId::HTMLObjectElement => {
393 elem.downcast::<HTMLObjectElement>().unwrap().form_owner()
394 },
395 HTMLElementTypeId::HTMLOutputElement => {
396 elem.downcast::<HTMLOutputElement>().unwrap().form_owner()
397 },
398 HTMLElementTypeId::HTMLSelectElement => {
399 elem.downcast::<HTMLSelectElement>().unwrap().form_owner()
400 },
401 HTMLElementTypeId::HTMLTextAreaElement => {
402 elem.downcast::<HTMLTextAreaElement>().unwrap().form_owner()
403 },
404 HTMLElementTypeId::HTMLElement => {
405 let html_element = elem.downcast::<HTMLElement>().unwrap();
406 if html_element.is_form_associated_custom_element() {
407 html_element.form_owner()
408 } else {
409 return false;
410 }
411 },
412 _ => {
413 debug_assert!(
414 !elem.downcast::<HTMLElement>().unwrap().is_listed_element()
415 );
416 return false;
417 },
418 },
419 _ => return false,
420 };
421
422 match form_owner {
423 Some(form_owner) => form_owner == self.form,
424 None => false,
425 }
426 }
427 }
428 DomRoot::from_ref(self.elements.init_once(|| {
429 let filter = Box::new(ElementsFilter {
430 form: DomRoot::from_ref(self),
431 });
432 let window = self.owner_window();
433 HTMLFormControlsCollection::new(cx, &window, self, filter)
434 }))
435 }
436
437 fn Length(&self, cx: &mut JSContext) -> u32 {
439 self.Elements(cx).Length()
440 }
441
442 fn IndexedGetter(&self, cx: &mut JSContext, index: u32) -> Option<DomRoot<Element>> {
444 let elements = self.Elements(cx);
445 elements.IndexedGetter(index)
446 }
447
448 fn NamedGetter(&self, cx: &mut JSContext, name: DOMString) -> Option<RadioNodeListOrElement> {
450 let window = self.owner_window();
451
452 let name = Atom::from(name);
453
454 let mut candidates =
456 RadioNodeList::new_controls_except_image_inputs(cx, &window, self, &name);
457 let mut candidates_length = candidates.Length();
458
459 if candidates_length == 0 {
461 candidates = RadioNodeList::new_images(cx, &window, self, &name);
462 candidates_length = candidates.Length();
463 }
464
465 let mut past_names_map = self.past_names_map.borrow_mut();
466
467 if candidates_length == 0 {
469 if past_names_map.contains_key(&name) {
470 return Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
471 &*past_names_map.get(&name).unwrap().0,
472 )));
473 }
474 return None;
475 }
476
477 if candidates_length > 1 {
479 return Some(RadioNodeListOrElement::RadioNodeList(candidates));
480 }
481
482 let element_node = candidates.upcast::<NodeList>().Item(0).unwrap();
485 past_names_map.insert(
486 name,
487 (
488 Dom::from_ref(element_node.downcast::<Element>().unwrap()),
489 NoTrace(self.current_name_generation.get() + 1),
490 ),
491 );
492 self.current_name_generation
493 .set(self.current_name_generation.get() + 1);
494
495 Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
497 element_node.downcast::<Element>().unwrap(),
498 )))
499 }
500
501 fn SetRel(&self, cx: &mut JSContext, rel: DOMString) {
503 self.upcast::<Element>()
504 .set_tokenlist_attribute(cx, &local_name!("rel"), rel);
505 }
506
507 fn RelList(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
509 self.rel_list.or_init(|| {
510 DOMTokenList::new(
511 cx,
512 self.upcast(),
513 &local_name!("rel"),
514 Some(vec![
515 Atom::from("noopener"),
516 Atom::from("noreferrer"),
517 Atom::from("opener"),
518 ]),
519 )
520 })
521 }
522
523 fn SupportedPropertyNames(&self, _: &mut JSContext) -> Vec<DOMString> {
525 #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
527 enum SourcedNameSource {
528 Id,
529 Name,
530 Past(usize),
531 }
532
533 impl SourcedNameSource {
534 fn is_past(&self) -> bool {
535 matches!(self, SourcedNameSource::Past(..))
536 }
537 }
538
539 struct SourcedName {
540 name: Atom,
541 element: DomRoot<Element>,
542 source: SourcedNameSource,
543 }
544
545 let mut sourced_names_vec: Vec<SourcedName> = Vec::new();
546
547 for child in self.controls.borrow().iter() {
549 if child
550 .downcast::<HTMLElement>()
551 .is_some_and(|c| c.is_listed_element())
552 {
553 if let Some(id_atom) = child.get_id() {
554 let entry = SourcedName {
555 name: id_atom,
556 element: DomRoot::from_ref(child),
557 source: SourcedNameSource::Id,
558 };
559 sourced_names_vec.push(entry);
560 }
561 if let Some(name_atom) = child.get_name() {
562 let entry = SourcedName {
563 name: name_atom,
564 element: DomRoot::from_ref(child),
565 source: SourcedNameSource::Name,
566 };
567 sourced_names_vec.push(entry);
568 }
569 }
570 }
571
572 for child in self.controls.borrow().iter() {
574 if child.is::<HTMLImageElement>() {
575 if let Some(id_atom) = child.get_id() {
576 let entry = SourcedName {
577 name: id_atom,
578 element: DomRoot::from_ref(child),
579 source: SourcedNameSource::Id,
580 };
581 sourced_names_vec.push(entry);
582 }
583 if let Some(name_atom) = child.get_name() {
584 let entry = SourcedName {
585 name: name_atom,
586 element: DomRoot::from_ref(child),
587 source: SourcedNameSource::Name,
588 };
589 sourced_names_vec.push(entry);
590 }
591 }
592 }
593
594 let past_names_map = self.past_names_map.borrow();
596 for (key, val) in past_names_map.iter() {
597 let entry = SourcedName {
598 name: key.clone(),
599 element: DomRoot::from_ref(&*val.0),
600 source: SourcedNameSource::Past(self.current_name_generation.get() - val.1.0),
601 };
602 sourced_names_vec.push(entry);
603 }
604
605 sourced_names_vec.sort_by(|a, b| {
617 if a.element
618 .upcast::<Node>()
619 .CompareDocumentPosition(b.element.upcast::<Node>()) ==
620 0
621 {
622 if a.source.is_past() && b.source.is_past() {
623 b.source.cmp(&a.source)
624 } else {
625 a.source.cmp(&b.source)
626 }
627 } else if a
628 .element
629 .upcast::<Node>()
630 .CompareDocumentPosition(b.element.upcast::<Node>()) &
631 NodeConstants::DOCUMENT_POSITION_FOLLOWING ==
632 NodeConstants::DOCUMENT_POSITION_FOLLOWING
633 {
634 std::cmp::Ordering::Less
635 } else {
636 std::cmp::Ordering::Greater
637 }
638 });
639
640 sourced_names_vec.retain(|sn| !sn.name.to_string().is_empty());
642
643 let mut names_vec: Vec<DOMString> = Vec::new();
645 for elem in sourced_names_vec.iter() {
646 if !names_vec.iter().any(|name| *name == *elem.name) {
647 names_vec.push(DOMString::from(&*elem.name));
648 }
649 }
650
651 names_vec
652 }
653
654 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
656 self.static_validation(cx).is_ok()
657 }
658
659 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
661 self.interactive_validation(cx).is_ok()
662 }
663}
664
665#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
666pub(crate) enum SubmittedFrom {
667 FromForm,
668 NotFromForm,
669}
670
671#[derive(Clone, Copy, MallocSizeOf)]
672pub(crate) enum ResetFrom {
673 FromForm,
674 NotFromForm,
675}
676
677impl HTMLFormElement {
678 fn pick_encoding(&self) -> &'static Encoding {
680 if self
682 .upcast::<Element>()
683 .has_attribute(&local_name!("accept-charset"))
684 {
685 let input = self
687 .upcast::<Element>()
688 .get_string_attribute(&local_name!("accept-charset"));
689
690 let input = input.str();
692 let mut candidate_encodings =
693 split_html_space_chars(&input).filter_map(|c| Encoding::for_label(c.as_bytes()));
694
695 return candidate_encodings.next().unwrap_or(UTF_8);
697 }
698
699 self.owner_document().encoding()
701 }
702
703 pub(crate) fn update_validity(&self, cx: &mut JSContext) {
704 let is_any_invalid = self
705 .controls
706 .borrow()
707 .iter()
708 .any(|control| control.is_invalid(cx, false));
709
710 self.upcast::<Element>()
711 .set_state(ElementState::VALID, !is_any_invalid);
712 self.upcast::<Element>()
713 .set_state(ElementState::INVALID, is_any_invalid);
714 }
715
716 pub(crate) fn submit(
718 &self,
719 cx: &mut JSContext,
720 submit_method_flag: SubmittedFrom,
721 submitter: FormSubmitterElement,
722 ) {
723 if self.upcast::<Element>().cannot_navigate() {
725 return;
726 }
727
728 if self.constructing_entry_list.get() {
730 return;
731 }
732 let doc = self.owner_document();
734
735 if doc.has_active_sandboxing_flag(SandboxingFlagSet::SANDBOXED_FORMS_BROWSING_CONTEXT_FLAG)
738 {
739 return;
740 }
741
742 let base = doc.base_url();
743 if submit_method_flag == SubmittedFrom::NotFromForm {
746 if self.firing_submission_events.get() {
748 return;
749 }
750 self.firing_submission_events.set(true);
752 if !submitter.no_validate(self) && self.interactive_validation(cx).is_err() {
754 self.firing_submission_events.set(false);
755 return;
756 }
757 let submitter_button = match submitter {
761 FormSubmitterElement::Form(f) => {
762 if f == self {
763 None
764 } else {
765 Some(f.upcast::<HTMLElement>())
766 }
767 },
768 FormSubmitterElement::Input(i) => Some(i.upcast::<HTMLElement>()),
769 FormSubmitterElement::Button(b) => Some(b.upcast::<HTMLElement>()),
770 };
771
772 let event = SubmitEvent::new(
774 self.global().as_window(),
775 atom!("submit"),
776 true,
777 true,
778 submitter_button.map(DomRoot::from_ref),
779 CanGc::from_cx(cx),
780 );
781 let event = event.upcast::<Event>();
782 event.fire(cx, self.upcast::<EventTarget>());
783
784 self.firing_submission_events.set(false);
786 if event.DefaultPrevented() {
788 return;
789 }
790 if self.upcast::<Element>().cannot_navigate() {
792 return;
793 }
794 }
795
796 let encoding = self.pick_encoding();
798
799 let mut form_data = match self.get_form_dataset(cx, Some(submitter), Some(encoding)) {
801 Some(form_data) => form_data,
802 None => return,
803 };
804
805 if self.upcast::<Element>().cannot_navigate() {
807 return;
808 }
809
810 let method = submitter.method();
812 let mut action = submitter.action();
817
818 if action.is_empty() {
820 action = DOMString::from(base.as_str());
821 }
822 let action_components = match doc.encoding_parse_a_url(&action.str()) {
824 Ok(url) => url,
825 Err(_) => return,
827 };
828 let scheme = action_components.scheme().to_owned();
830 let enctype = submitter.enctype();
832
833 let form_target_attribute = submitter.target();
836 let form_target = if submitter.is_submit_button() &&
837 valid_navigable_target_name_or_keyword(&form_target_attribute)
838 {
839 Some(form_target_attribute)
840 } else {
841 None
843 };
844 let form_owner = submitter.form_owner();
846 let form = form_owner.as_deref().unwrap_or(self);
847 let target = get_element_target(form.upcast::<Element>(), form_target);
848
849 let noopener = self.relations.get().get_element_noopener(target.as_ref());
851
852 let source = doc.browsing_context().unwrap();
855 let (maybe_chosen, _new) =
856 source.choose_browsing_context(cx, target.unwrap_or_default(), noopener);
857
858 let Some(chosen) = maybe_chosen else {
859 return;
861 };
862 let target_document = match chosen.document() {
863 Some(doc) => doc,
864 None => return,
865 };
866
867 let history_handling = if doc == target_document && !doc.completely_loaded() {
871 NavigationHistoryBehavior::Replace
872 } else {
873 NavigationHistoryBehavior::Auto
874 };
875
876 let target_window = target_document.window();
877 let mut load_data = LoadData::new(
878 LoadOrigin::Script(doc.origin().snapshot()),
879 action_components,
880 target_document.about_base_url(),
881 None,
882 target_window.as_global_scope().get_referrer(),
883 target_document.get_referrer_policy(),
884 Some(target_window.as_global_scope().is_secure_context()),
885 Some(target_document.insecure_requests_policy()),
886 target_document.has_trustworthy_ancestor_origin(),
887 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
888 );
889
890 match (&*scheme, method) {
894 (_, FormMethod::Dialog) => {
895 },
898 ("http", FormMethod::Get) | ("https", FormMethod::Get) | ("data", FormMethod::Get) => {
900 load_data
901 .headers
902 .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
903 self.mutate_action_url(&mut form_data, load_data, target_window, history_handling);
904 },
905 ("http", FormMethod::Post) | ("https", FormMethod::Post) => {
907 load_data.method = Method::POST;
908 self.submit_entity_body(
909 cx,
910 &mut form_data,
911 load_data,
912 enctype,
913 encoding,
914 target_window,
915 history_handling,
916 );
917 },
918 ("file", _) |
920 ("about", _) |
921 ("data", FormMethod::Post) |
922 ("ftp", _) |
923 ("javascript", _) => {
924 self.plan_to_navigate(load_data, target_window, history_handling);
925 },
926 ("mailto", FormMethod::Post) => {
927 },
930 ("mailto", FormMethod::Get) => {
931 },
934 _ => (),
935 }
936 }
937
938 fn mutate_action_url(
940 &self,
941 form_data: &mut [FormDatum],
942 mut load_data: LoadData,
943 target: &Window,
944 history_handling: NavigationHistoryBehavior,
945 ) {
946 self.set_url_query_pairs(
947 &mut load_data.url,
948 form_data.iter().map(|field| {
949 (
950 field.name.normalize_crlf(),
951 field.replace_value().normalize_crlf(),
952 )
953 }),
954 );
955
956 self.plan_to_navigate(load_data, target, history_handling);
957 }
958
959 #[allow(clippy::too_many_arguments)]
961 fn submit_entity_body(
962 &self,
963 cx: &mut JSContext,
964 form_data: &mut [FormDatum],
965 mut load_data: LoadData,
966 enctype: FormEncType,
967 encoding: &'static Encoding,
968 target: &Window,
969 history_handling: NavigationHistoryBehavior,
970 ) {
971 let boundary = generate_boundary();
972 let bytes = match enctype {
973 FormEncType::UrlEncoded => {
974 load_data
975 .headers
976 .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
977
978 let mut url = load_data.url.clone();
979 self.set_url_query_pairs(
980 &mut url,
981 form_data.iter().map(|field| {
985 (
986 field.name.normalize_crlf(),
987 field.replace_value().normalize_crlf(),
988 )
989 }),
990 );
991
992 url.query().unwrap_or("").to_string().into_bytes()
993 },
994 FormEncType::MultipartFormData => {
995 let mime: Mime = format!("multipart/form-data; boundary={}", boundary)
996 .parse()
997 .unwrap();
998 load_data.headers.typed_insert(ContentType::from(mime));
999 encode_multipart_form_data(form_data, boundary, encoding)
1000 },
1001 FormEncType::TextPlain => {
1002 load_data
1003 .headers
1004 .typed_insert(ContentType::from(mime::TEXT_PLAIN));
1005 let pairs = form_data.iter().map(|field| {
1009 (
1010 field.name.normalize_crlf(),
1011 field.replace_value().normalize_crlf(),
1012 )
1013 });
1014 let body = encode_plaintext(pairs);
1017 encode_with_html_fallback(&body, encoding).into_owned()
1019 },
1020 };
1021
1022 let global = self.global();
1023
1024 let request_body = bytes
1025 .extract(cx, &global, false)
1026 .expect("Couldn't extract body.")
1027 .into_net_request_body()
1028 .0;
1029 load_data.data = Some(request_body);
1030
1031 self.plan_to_navigate(load_data, target, history_handling);
1032 }
1033
1034 fn set_url_query_pairs<T>(
1035 &self,
1036 url: &mut servo_url::ServoUrl,
1037 pairs: impl Iterator<Item = (T, String)>,
1038 ) where
1039 T: AsRef<str>,
1040 {
1041 let encoding = self.pick_encoding();
1042 url.as_mut_url()
1043 .query_pairs_mut()
1044 .encoding_override(Some(&|s| encode_with_html_fallback(s, encoding)))
1045 .clear()
1046 .extend_pairs(pairs);
1047 }
1048
1049 fn plan_to_navigate(
1051 &self,
1052 mut load_data: LoadData,
1053 target: &Window,
1054 history_handling: NavigationHistoryBehavior,
1055 ) {
1056 let elem = self.upcast::<Element>();
1061 let referrer = match elem.get_attribute_string_value(&local_name!("rel")) {
1062 Some(link_types) if link_types.contains("noreferrer") => Referrer::NoReferrer,
1063 _ => target.as_global_scope().get_referrer(),
1064 };
1065
1066 self.planned_navigation
1069 .set(self.planned_navigation.get().wrapping_add(1));
1070 let planned_navigation = self.planned_navigation.get();
1071
1072 let ongoing_navigation = target.set_ongoing_navigation();
1088
1089 let referrer_policy = target.Document().get_referrer_policy();
1090 load_data.creator_pipeline_id = Some(target.pipeline_id());
1091 load_data.referrer = referrer;
1092 load_data.referrer_policy = referrer_policy;
1093
1094 if let Some(window_proxy) = target.undiscarded_window_proxy() &&
1097 let Some(frame) = window_proxy
1098 .frame_element()
1099 .and_then(|e| e.downcast::<HTMLIFrameElement>())
1100 {
1101 frame.note_pending_navigation()
1102 }
1103
1104 let form = Trusted::new(self);
1107 let window = Trusted::new(target);
1108 let task = task!(navigate_to_form_planned_navigation: move |cx| {
1109 if planned_navigation != form.root().planned_navigation.get() {
1113 return;
1114 }
1115
1116 if ongoing_navigation != window.root().ongoing_navigation() {
1119 return;
1120 }
1121
1122 navigate(
1124 cx,
1125 &window.root(),
1126 history_handling,
1127 false,
1128 load_data,
1129 )
1130 });
1131
1132 target
1137 .global()
1138 .task_manager()
1139 .dom_manipulation_task_source()
1140 .queue(task)
1141 }
1142
1143 fn interactive_validation(&self, cx: &mut JSContext) -> Result<(), ()> {
1146 let unhandled_invalid_controls = match self.static_validation(cx) {
1151 Ok(()) => return Ok(()),
1152 Err(err) => err,
1153 };
1154
1155 let mut first = true;
1158
1159 for elem in unhandled_invalid_controls {
1160 if let Some(validatable) = elem.as_maybe_validatable() {
1161 error!("Validation error: {}", validatable.validation_message(cx));
1162 }
1163 if first && let Some(html_elem) = elem.downcast::<HTMLElement>() {
1164 html_elem.Focus(cx, &FocusOptions::default());
1171 first = false;
1172 }
1173 }
1174
1175 Err(())
1179 }
1180
1181 fn static_validation(&self, cx: &mut JSContext) -> Result<(), Vec<DomRoot<Element>>> {
1184 let invalid_controls = self
1186 .controls
1187 .borrow()
1188 .iter()
1189 .filter_map(|field| {
1190 if let Some(element) = field.downcast::<Element>() {
1191 if element.is_invalid(cx, true) {
1192 Some(DomRoot::from_ref(element))
1193 } else {
1194 None
1195 }
1196 } else {
1197 None
1198 }
1199 })
1200 .collect::<Vec<DomRoot<Element>>>();
1201 if invalid_controls.is_empty() {
1203 return Ok(());
1204 }
1205 let unhandled_invalid_controls = invalid_controls
1207 .into_iter()
1208 .filter_map(|field| {
1209 let not_canceled = field
1212 .upcast::<EventTarget>()
1213 .fire_cancelable_event(cx, atom!("invalid"));
1214 if not_canceled {
1216 return Some(field);
1217 }
1218 None
1219 })
1220 .collect::<Vec<DomRoot<Element>>>();
1221 Err(unhandled_invalid_controls)
1223 }
1224
1225 fn get_unclean_dataset(
1230 &self,
1231 submitter: Option<FormSubmitterElement>,
1232 encoding: Option<&'static Encoding>,
1233 can_gc: CanGc,
1234 ) -> Vec<FormDatum> {
1235 let mut data_set = Vec::new();
1236 for child in self.controls.borrow().iter() {
1237 if child.disabled_state() {
1239 continue;
1240 }
1241 let child = child.upcast::<Node>();
1242
1243 if child.ancestors().any(|a| a.is::<HTMLDataListElement>()) {
1245 continue;
1246 }
1247 if let NodeTypeId::Element(ElementTypeId::HTMLElement(element)) = child.type_id() {
1248 match element {
1249 HTMLElementTypeId::HTMLInputElement => {
1250 let input = child.downcast::<HTMLInputElement>().unwrap();
1251 data_set.append(&mut input.form_datums(submitter, encoding));
1252 },
1253 HTMLElementTypeId::HTMLButtonElement => {
1254 let button = child.downcast::<HTMLButtonElement>().unwrap();
1255 if let Some(datum) = button.form_datum(submitter) {
1256 data_set.push(datum);
1257 }
1258 },
1259 HTMLElementTypeId::HTMLObjectElement => {
1260 },
1262 HTMLElementTypeId::HTMLSelectElement => {
1263 let select = child.downcast::<HTMLSelectElement>().unwrap();
1264 select.push_form_data(&mut data_set);
1265 },
1266 HTMLElementTypeId::HTMLTextAreaElement => {
1267 let textarea = child.downcast::<HTMLTextAreaElement>().unwrap();
1268 let name = textarea.Name();
1269 if !name.is_empty() {
1270 data_set.push(FormDatum {
1271 ty: textarea.Type(),
1272 name,
1273 value: FormDatumValue::String(textarea.Value()),
1274 });
1275 }
1276 },
1277 HTMLElementTypeId::HTMLElement => {
1278 let custom = child.downcast::<HTMLElement>().unwrap();
1279 if custom.is_form_associated_custom_element() {
1280 let internals =
1282 custom.upcast::<Element>().ensure_element_internals(can_gc);
1283 internals.perform_entry_construction(&mut data_set);
1284 }
1286 },
1287 _ => (),
1288 }
1289 }
1290
1291 let child_element = child.downcast::<Element>().unwrap();
1295 let input_matches = child_element
1296 .downcast::<HTMLInputElement>()
1297 .is_some_and(|input| {
1298 matches!(
1299 *input.input_type(),
1300 InputType::Text(_) | InputType::Search(_)
1301 )
1302 });
1303 let textarea_matches = child_element.is::<HTMLTextAreaElement>();
1304 let dirname = child_element.get_string_attribute(&local_name!("dirname"));
1305 if (input_matches || textarea_matches) && !dirname.is_empty() {
1306 let dir = DOMString::from(child_element.directionality());
1307 data_set.push(FormDatum {
1308 ty: DOMString::from("string"),
1309 name: dirname,
1310 value: FormDatumValue::String(dir),
1311 });
1312 }
1313 }
1314 data_set
1315 }
1316
1317 pub(crate) fn get_form_dataset(
1319 &self,
1320 cx: &mut JSContext,
1321 submitter: Option<FormSubmitterElement>,
1322 encoding: Option<&'static Encoding>,
1323 ) -> Option<Vec<FormDatum>> {
1324 if self.constructing_entry_list.get() {
1326 return None;
1327 }
1328
1329 self.constructing_entry_list.set(true);
1331
1332 let ret = self.get_unclean_dataset(submitter, encoding, CanGc::from_cx(cx));
1334
1335 let window = self.owner_window();
1336
1337 let form_data = FormData::new(Some(ret), &window.global(), CanGc::from_cx(cx));
1339
1340 let event = FormDataEvent::new(
1342 &window,
1343 atom!("formdata"),
1344 EventBubbles::Bubbles,
1345 EventCancelable::NotCancelable,
1346 &form_data,
1347 CanGc::from_cx(cx),
1348 );
1349
1350 event
1351 .upcast::<Event>()
1352 .fire(cx, self.upcast::<EventTarget>());
1353
1354 self.constructing_entry_list.set(false);
1356
1357 Some(form_data.datums())
1359 }
1360
1361 pub(crate) fn reset(&self, cx: &mut JSContext, _reset_method_flag: ResetFrom) {
1363 if self.marked_for_reset.get() {
1365 return;
1366 } else {
1367 self.marked_for_reset.set(true);
1368 }
1369
1370 let reset = self
1374 .upcast::<EventTarget>()
1375 .fire_bubbling_cancelable_event(cx, atom!("reset"));
1376 if !reset {
1377 return;
1378 }
1379
1380 let controls: Vec<_> = self
1381 .controls
1382 .borrow()
1383 .iter()
1384 .map(|c| c.as_rooted())
1385 .collect();
1386
1387 for child in controls {
1388 child.reset(cx);
1389 }
1390 self.marked_for_reset.set(false);
1391 }
1392
1393 fn add_control<T: ?Sized + FormControl>(&self, cx: &mut JSContext, control: &T) {
1394 {
1395 let root = self.upcast::<Element>().root_element();
1396 let root = root.upcast::<Node>();
1397 let mut controls = self.controls.borrow_mut();
1398
1399 let control_element = control.to_element();
1405 if control_element.upcast::<Node>().has_parent() {
1406 controls.insert_pre_order(control_element, root);
1407 } else {
1408 controls.push(Dom::from_ref(control_element));
1409 }
1410 }
1411 self.update_validity(cx);
1412 }
1413
1414 fn remove_control<T: ?Sized + FormControl>(&self, cx: &mut JSContext, control: &T) {
1415 {
1416 let control = control.to_element();
1417 let mut controls = self.controls.borrow_mut();
1418 controls
1419 .iter()
1420 .position(|c| &**c == control)
1421 .map(|idx| controls.remove(idx));
1422
1423 let mut past_names_map = self.past_names_map.borrow_mut();
1428 past_names_map.0.retain(|_k, v| v.0 != control);
1429 }
1430 self.update_validity(cx);
1431 }
1432}
1433
1434impl Element {
1435 pub(crate) fn is_resettable(&self) -> bool {
1436 let NodeTypeId::Element(ElementTypeId::HTMLElement(element_type)) =
1437 self.upcast::<Node>().type_id()
1438 else {
1439 return false;
1440 };
1441 matches!(
1442 element_type,
1443 HTMLElementTypeId::HTMLInputElement |
1444 HTMLElementTypeId::HTMLSelectElement |
1445 HTMLElementTypeId::HTMLTextAreaElement |
1446 HTMLElementTypeId::HTMLOutputElement |
1447 HTMLElementTypeId::HTMLElement
1448 )
1449 }
1450
1451 pub(crate) fn reset(&self, cx: &mut JSContext) {
1452 if !self.is_resettable() {
1453 return;
1454 }
1455
1456 if let Some(input_element) = self.downcast::<HTMLInputElement>() {
1457 input_element.reset(cx);
1458 } else if let Some(select_element) = self.downcast::<HTMLSelectElement>() {
1459 select_element.reset();
1460 } else if let Some(textarea_element) = self.downcast::<HTMLTextAreaElement>() {
1461 textarea_element.reset(cx);
1462 } else if let Some(output_element) = self.downcast::<HTMLOutputElement>() {
1463 output_element.reset(cx);
1464 } else if let Some(html_element) = self.downcast::<HTMLElement>() &&
1465 html_element.is_form_associated_custom_element()
1466 {
1467 ScriptThread::enqueue_callback_reaction(
1468 html_element.upcast::<Element>(),
1469 CallbackReaction::FormReset,
1470 None,
1471 )
1472 }
1473 }
1474}
1475
1476#[derive(Clone, JSTraceable, MallocSizeOf)]
1477pub(crate) enum FormDatumValue {
1478 File(DomRoot<File>),
1479 String(DOMString),
1480}
1481
1482#[derive(Clone, JSTraceable, MallocSizeOf)]
1483pub(crate) struct FormDatum {
1484 pub(crate) ty: DOMString,
1485 pub(crate) name: DOMString,
1486 pub(crate) value: FormDatumValue,
1487}
1488
1489impl FormDatum {
1490 pub(crate) fn replace_value(&self) -> &DOMString {
1491 match self.value {
1492 FormDatumValue::File(ref f) => f.name(),
1493 FormDatumValue::String(ref s) => s,
1494 }
1495 }
1496}
1497
1498#[derive(Clone, Copy, MallocSizeOf)]
1499pub(crate) enum FormEncType {
1500 TextPlain,
1501 UrlEncoded,
1502 MultipartFormData,
1503}
1504
1505#[derive(Clone, Copy, MallocSizeOf)]
1506pub(crate) enum FormMethod {
1507 Get,
1508 Post,
1509 Dialog,
1510}
1511
1512#[derive(Clone, Copy, MallocSizeOf)]
1514pub(crate) enum FormSubmitterElement<'a> {
1515 Form(&'a HTMLFormElement),
1516 Input(&'a HTMLInputElement),
1517 Button(&'a HTMLButtonElement),
1518 }
1521
1522impl FormSubmitterElement<'_> {
1523 fn action(&self) -> DOMString {
1524 match *self {
1525 FormSubmitterElement::Form(form) => form.Action(),
1526 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1527 &local_name!("formaction"),
1528 |i| i.FormAction(),
1529 |f| f.Action(),
1530 ),
1531 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1532 &local_name!("formaction"),
1533 |i| i.FormAction(),
1534 |f| f.Action(),
1535 ),
1536 }
1537 }
1538
1539 fn enctype(&self) -> FormEncType {
1540 let attr = match *self {
1541 FormSubmitterElement::Form(form) => form.Enctype(),
1542 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1543 &local_name!("formenctype"),
1544 |i| i.FormEnctype(),
1545 |f| f.Enctype(),
1546 ),
1547 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1548 &local_name!("formenctype"),
1549 |i| i.FormEnctype(),
1550 |f| f.Enctype(),
1551 ),
1552 };
1553 match_domstring_ascii!(attr,
1556 "multipart/form-data" => FormEncType::MultipartFormData,
1557 "text/plain" => FormEncType::TextPlain,
1558 _ => FormEncType::UrlEncoded,
1559 )
1560 }
1561
1562 fn method(&self) -> FormMethod {
1563 let attr = match *self {
1564 FormSubmitterElement::Form(form) => form.Method(),
1565 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1566 &local_name!("formmethod"),
1567 |i| i.FormMethod(),
1568 |f| f.Method(),
1569 ),
1570 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1571 &local_name!("formmethod"),
1572 |i| i.FormMethod(),
1573 |f| f.Method(),
1574 ),
1575 };
1576 match_domstring_ascii!(attr,
1577 "dialog" => FormMethod::Dialog,
1578 "post" => FormMethod::Post,
1579 _ => FormMethod::Get,
1580 )
1581 }
1582
1583 fn target(&self) -> DOMString {
1584 match *self {
1585 FormSubmitterElement::Form(form) => form.Target(),
1586 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1587 &local_name!("formtarget"),
1588 |i| i.FormTarget(),
1589 |f| f.Target(),
1590 ),
1591 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1592 &local_name!("formtarget"),
1593 |i| i.FormTarget(),
1594 |f| f.Target(),
1595 ),
1596 }
1597 }
1598
1599 fn no_validate(&self, _form_owner: &HTMLFormElement) -> bool {
1600 match *self {
1601 FormSubmitterElement::Form(form) => form.NoValidate(),
1602 FormSubmitterElement::Input(input_element) => input_element.get_form_boolean_attribute(
1603 &local_name!("formnovalidate"),
1604 |i| i.FormNoValidate(),
1605 |f| f.NoValidate(),
1606 ),
1607 FormSubmitterElement::Button(button_element) => button_element
1608 .get_form_boolean_attribute(
1609 &local_name!("formnovalidate"),
1610 |i| i.FormNoValidate(),
1611 |f| f.NoValidate(),
1612 ),
1613 }
1614 }
1615
1616 pub(crate) fn is_submit_button(&self) -> bool {
1618 match *self {
1619 FormSubmitterElement::Input(input_element) => input_element.is_submit_button(),
1622 FormSubmitterElement::Button(button_element) => button_element.is_submit_button(),
1624 _ => false,
1625 }
1626 }
1627
1628 pub(crate) fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1630 match *self {
1631 FormSubmitterElement::Button(button_el) => button_el.form_owner(),
1632 FormSubmitterElement::Input(input_el) => input_el.form_owner(),
1633 _ => None,
1634 }
1635 }
1636}
1637
1638pub(crate) trait FormControl: DomObject<ReflectorType = ()> + NodeTraits {
1639 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>>;
1640
1641 fn set_form_owner(&self, form: Option<&HTMLFormElement>);
1642
1643 fn to_element(&self) -> ∈
1644
1645 fn is_listed(&self) -> bool {
1646 true
1647 }
1648
1649 fn set_form_owner_from_parser(&self, cx: &mut JSContext, form: &HTMLFormElement) {
1654 let elem = self.to_element();
1655 let node = elem.upcast::<Node>();
1656 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, true);
1657 form.add_control(cx, self);
1658 self.set_form_owner(Some(form));
1659 }
1660
1661 fn reset_form_owner(&self, cx: &mut JSContext) {
1663 let elem = self.to_element();
1664 let node = elem.upcast::<Node>();
1665 let old_owner = self.form_owner();
1666 let has_form_id = elem.has_attribute(&local_name!("form"));
1667 let nearest_form_ancestor = node
1668 .ancestors()
1669 .find_map(DomRoot::downcast::<HTMLFormElement>);
1670
1671 if old_owner.is_some() &&
1673 !(self.is_listed() && has_form_id) &&
1674 nearest_form_ancestor == old_owner
1675 {
1676 return;
1677 }
1678
1679 let new_owner = if self.is_listed() && has_form_id && elem.is_connected() {
1681 let form_id = elem.get_string_attribute(&local_name!("form"));
1685 let first_relevant_element = if let Some(shadow_root) = self.containing_shadow_root() {
1686 shadow_root
1687 .upcast::<DocumentFragment>()
1688 .GetElementById(cx, form_id)
1689 } else {
1690 node.owner_document().GetElementById(cx, form_id)
1691 };
1692
1693 first_relevant_element.and_then(DomRoot::downcast::<HTMLFormElement>)
1694 } else {
1695 nearest_form_ancestor
1697 };
1698
1699 if old_owner != new_owner {
1700 if let Some(o) = old_owner {
1701 o.remove_control(cx, self);
1702 }
1703 if let Some(ref new_owner) = new_owner {
1704 new_owner.add_control(cx, self);
1705 }
1706 if let Some(html_elem) = elem.downcast::<HTMLElement>() &&
1708 html_elem.is_form_associated_custom_element()
1709 {
1710 ScriptThread::enqueue_callback_reaction(
1711 elem,
1712 CallbackReaction::FormAssociated(
1713 new_owner.as_ref().map(|form| DomRoot::from_ref(&**form)),
1714 ),
1715 None,
1716 )
1717 }
1718 self.set_form_owner(new_owner.as_deref());
1719 }
1720 }
1721
1722 fn form_attribute_mutated(&self, cx: &mut JSContext, mutation: AttributeMutation) {
1724 match mutation {
1725 AttributeMutation::Set(..) => {
1726 self.register_if_necessary();
1727 },
1728 AttributeMutation::Removed => {
1729 self.unregister_if_necessary();
1730 },
1731 }
1732
1733 self.reset_form_owner(cx);
1734 }
1735
1736 fn register_if_necessary(&self) {
1738 let elem = self.to_element();
1739 let form_id = elem.get_string_attribute(&local_name!("form"));
1740 let node = elem.upcast::<Node>();
1741
1742 if self.is_listed() && !form_id.is_empty() && node.is_connected() {
1743 node.owner_document()
1744 .register_form_id_listener(form_id, self);
1745 }
1746 }
1747
1748 fn unregister_if_necessary(&self) {
1749 let elem = self.to_element();
1750 let form_id = elem.get_string_attribute(&local_name!("form"));
1751
1752 if self.is_listed() && !form_id.is_empty() {
1753 elem.owner_document()
1754 .unregister_form_id_listener(form_id, self);
1755 }
1756 }
1757
1758 fn bind_form_control_to_tree(&self, cx: &mut JSContext) {
1760 let elem = self.to_element();
1761 let node = elem.upcast::<Node>();
1762
1763 let must_skip_reset = node.get_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER);
1768 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, false);
1769
1770 if !must_skip_reset {
1771 self.form_attribute_mutated(
1772 cx,
1773 AttributeMutation::Set(None, AttributeMutationReason::Directly),
1774 );
1775 }
1776 }
1777
1778 fn unbind_form_control_from_tree(&self, cx: &mut JSContext) {
1780 let elem = self.to_element();
1781 let has_form_attr = elem.has_attribute(&local_name!("form"));
1782 let same_subtree = self
1783 .form_owner()
1784 .is_none_or(|form| elem.is_in_same_home_subtree(&*form));
1785
1786 self.unregister_if_necessary();
1787
1788 if !same_subtree || (self.is_listed() && has_form_attr) {
1794 self.reset_form_owner(cx);
1795 }
1796 }
1797
1798 fn get_form_attribute<InputFn, OwnerFn>(
1799 &self,
1800 attr: &LocalName,
1801 input: InputFn,
1802 owner: OwnerFn,
1803 ) -> DOMString
1804 where
1805 InputFn: Fn(&Self) -> DOMString,
1806 OwnerFn: Fn(&HTMLFormElement) -> DOMString,
1807 Self: Sized,
1808 {
1809 if self.to_element().has_attribute(attr) {
1810 input(self)
1811 } else {
1812 self.form_owner().map_or(DOMString::new(), |t| owner(&t))
1813 }
1814 }
1815
1816 fn get_form_boolean_attribute<InputFn, OwnerFn>(
1817 &self,
1818 attr: &LocalName,
1819 input: InputFn,
1820 owner: OwnerFn,
1821 ) -> bool
1822 where
1823 InputFn: Fn(&Self) -> bool,
1824 OwnerFn: Fn(&HTMLFormElement) -> bool,
1825 Self: Sized,
1826 {
1827 if self.to_element().has_attribute(attr) {
1828 input(self)
1829 } else {
1830 self.form_owner().is_some_and(|t| owner(&t))
1831 }
1832 }
1833
1834 fn is_candidate_for_constraint_validation(&self) -> bool {
1836 let element = self.to_element();
1837 let html_element = element.downcast::<HTMLElement>();
1838 if let Some(html_element) = html_element {
1839 html_element.is_submittable_element() || element.is_instance_validatable()
1840 } else {
1841 false
1842 }
1843 }
1844
1845 fn moving_steps(&self, cx: &mut JSContext) {
1846 let same_subtree = self
1849 .form_owner()
1850 .is_none_or(|form| self.to_element().is_in_same_home_subtree(&*form));
1851 if !same_subtree {
1852 self.reset_form_owner(cx)
1853 }
1854 }
1855
1856 }
1859
1860impl VirtualMethods for HTMLFormElement {
1861 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1862 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1863 }
1864
1865 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1866 self.super_type().unwrap().unbind_from_tree(cx, context);
1867
1868 rooted_vec!(let mut to_reset);
1871 to_reset.extend(
1872 self.controls
1873 .borrow()
1874 .iter()
1875 .filter(|c| !c.is_in_same_home_subtree(self))
1876 .cloned(),
1877 );
1878
1879 for control in to_reset.iter() {
1880 control
1881 .as_maybe_form_control()
1882 .expect("Element must be a form control")
1883 .reset_form_owner(cx);
1884 }
1885 }
1886
1887 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1888 match name {
1889 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
1890 _ => self
1891 .super_type()
1892 .unwrap()
1893 .parse_plain_attribute(name, value),
1894 }
1895 }
1896
1897 fn attribute_mutated(
1898 &self,
1899 cx: &mut JSContext,
1900 attr: AttrRef<'_>,
1901 mutation: AttributeMutation,
1902 ) {
1903 self.super_type()
1904 .unwrap()
1905 .attribute_mutated(cx, attr, mutation);
1906
1907 match *attr.local_name() {
1908 local_name!("rel") | local_name!("rev") => {
1909 self.relations
1910 .set(LinkRelations::for_element(self.upcast()));
1911 },
1912 _ => {},
1913 }
1914 }
1915}
1916
1917pub(crate) trait FormControlElementHelpers {
1918 fn as_maybe_form_control(&self) -> Option<&dyn FormControl>;
1919}
1920
1921impl FormControlElementHelpers for Element {
1922 fn as_maybe_form_control(&self) -> Option<&dyn FormControl> {
1923 let node = self.upcast::<Node>();
1924
1925 match node.type_id() {
1926 NodeTypeId::Element(ElementTypeId::HTMLElement(
1927 HTMLElementTypeId::HTMLButtonElement,
1928 )) => Some(self.downcast::<HTMLButtonElement>().unwrap() as &dyn FormControl),
1929 NodeTypeId::Element(ElementTypeId::HTMLElement(
1930 HTMLElementTypeId::HTMLFieldSetElement,
1931 )) => Some(self.downcast::<HTMLFieldSetElement>().unwrap() as &dyn FormControl),
1932 NodeTypeId::Element(ElementTypeId::HTMLElement(
1933 HTMLElementTypeId::HTMLImageElement,
1934 )) => Some(self.downcast::<HTMLImageElement>().unwrap() as &dyn FormControl),
1935 NodeTypeId::Element(ElementTypeId::HTMLElement(
1936 HTMLElementTypeId::HTMLInputElement,
1937 )) => Some(self.downcast::<HTMLInputElement>().unwrap() as &dyn FormControl),
1938 NodeTypeId::Element(ElementTypeId::HTMLElement(
1939 HTMLElementTypeId::HTMLLabelElement,
1940 )) => Some(self.downcast::<HTMLLabelElement>().unwrap() as &dyn FormControl),
1941 NodeTypeId::Element(ElementTypeId::HTMLElement(
1942 HTMLElementTypeId::HTMLLegendElement,
1943 )) => Some(self.downcast::<HTMLLegendElement>().unwrap() as &dyn FormControl),
1944 NodeTypeId::Element(ElementTypeId::HTMLElement(
1945 HTMLElementTypeId::HTMLObjectElement,
1946 )) => Some(self.downcast::<HTMLObjectElement>().unwrap() as &dyn FormControl),
1947 NodeTypeId::Element(ElementTypeId::HTMLElement(
1948 HTMLElementTypeId::HTMLOutputElement,
1949 )) => Some(self.downcast::<HTMLOutputElement>().unwrap() as &dyn FormControl),
1950 NodeTypeId::Element(ElementTypeId::HTMLElement(
1951 HTMLElementTypeId::HTMLSelectElement,
1952 )) => Some(self.downcast::<HTMLSelectElement>().unwrap() as &dyn FormControl),
1953 NodeTypeId::Element(ElementTypeId::HTMLElement(
1954 HTMLElementTypeId::HTMLTextAreaElement,
1955 )) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
1956 _ => self.downcast::<HTMLElement>().and_then(|elem| {
1957 if elem.is_form_associated_custom_element() {
1958 Some(elem as &dyn FormControl)
1959 } else {
1960 None
1961 }
1962 }),
1963 }
1964 }
1965}
1966
1967fn encode_plaintext(pairs: impl Iterator<Item = (String, String)>) -> String {
1969 let mut result = String::new();
1971 for (name, value) in pairs {
1973 result.push_str(&name);
1975 result.push('=');
1977 result.push_str(&value);
1979 result.push_str("\r\n");
1982 }
1983 result
1985}
1986
1987fn encode_with_html_fallback<'a>(input: &'a str, encoding: &'static Encoding) -> Cow<'a, [u8]> {
1993 encoding.encode(input).0
1994}
1995
1996pub(crate) fn encode_multipart_form_data(
1998 form_data: &mut [FormDatum],
1999 boundary: String,
2000 encoding: &'static Encoding,
2001) -> Vec<u8> {
2002 let mut result = vec![];
2003
2004 fn escape_header_bytes(input: &[u8]) -> Vec<u8> {
2011 let mut output = Vec::with_capacity(input.len());
2012 for &b in input {
2013 match b {
2014 b'\n' => output.extend(b"%0A"),
2015 b'\r' => output.extend(b"%0D"),
2016 b'"' => output.extend(b"%22"),
2017 _ => output.push(b),
2018 }
2019 }
2020 output
2021 }
2022
2023 for entry in form_data.iter_mut() {
2024 entry.name = entry.name.normalize_crlf().into();
2028
2029 if let FormDatumValue::String(ref s) = entry.value {
2034 entry.value = FormDatumValue::String(s.normalize_crlf().into());
2035 }
2036
2037 let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
2039 result.append(&mut boundary_bytes);
2040
2041 let name_str = &*entry.name.str();
2043 let encoded_name = encode_with_html_fallback(name_str, encoding);
2044 let escaped_name = escape_header_bytes(&encoded_name);
2046
2047 match entry.value {
2048 FormDatumValue::String(ref s) => {
2049 let value_str = &*s.str();
2051 let encoded_value = encode_with_html_fallback(value_str, encoding);
2052
2053 result.extend(b"Content-Disposition: form-data; name=\"");
2055 result.extend(&escaped_name);
2056 result.extend(b"\"\r\n\r\n");
2057 result.extend_from_slice(&encoded_value);
2058 result.extend(b"\r\n");
2059 },
2060 FormDatumValue::File(ref f) => {
2061 let filename_str = &*f.name().str();
2063 let encoded_filename = encode_with_html_fallback(filename_str, encoding);
2064 let escaped_filename = escape_header_bytes(&encoded_filename);
2066
2067 result.extend(b"Content-Disposition: form-data; name=\"");
2068 result.extend(&escaped_name);
2069 result.extend(b"\"; filename=\"");
2070 result.extend(&escaped_filename);
2071
2072 result.extend(b"\"\r\nContent-Type: ");
2074
2075 let content_type: Mime = f
2076 .upcast::<Blob>()
2077 .Type()
2078 .parse()
2079 .unwrap_or(mime::TEXT_PLAIN);
2080 result.extend(content_type.as_ref().as_bytes());
2081 result.extend(b"\r\n\r\n");
2082
2083 let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or_default();
2084
2085 result.append(&mut bytes);
2086 result.extend(b"\r\n");
2087 },
2088 }
2089 }
2090
2091 let mut boundary_bytes = format!("--{boundary}--\r\n").into_bytes();
2093 result.append(&mut boundary_bytes);
2094
2095 result
2096}
2097
2098pub(crate) fn generate_boundary() -> String {
2100 let i1 = random::<u32>();
2101 let i2 = random::<u32>();
2102
2103 format!("---------------------------{0}{1}", i1, i2)
2104}