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 rustc_hash::FxBuildHasher;
20use servo_rand::random;
21use style::attr::AttrValue;
22use style::str::split_html_space_chars;
23use stylo_atoms::Atom;
24use stylo_dom::ElementState;
25
26use crate::body::Extractable;
27use crate::dom::attr::Attr;
28use crate::dom::bindings::cell::DomRefCell;
29use crate::dom::bindings::codegen::Bindings::AttrBinding::Attr_Binding::AttrMethods;
30use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
31use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
32use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
33use crate::dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
34use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
35use crate::dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
37use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
38use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
39use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
40use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
41use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
42use crate::dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
43use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
44use crate::dom::bindings::codegen::UnionTypes::RadioNodeListOrElement;
45use crate::dom::bindings::error::{Error, Fallible};
46use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
47use crate::dom::bindings::refcounted::Trusted;
48use crate::dom::bindings::reflector::{DomGlobal, DomObject};
49use crate::dom::bindings::root::{Dom, DomOnceCell, DomRoot, MutNullableDom};
50use crate::dom::bindings::str::DOMString;
51use crate::dom::bindings::trace::{HashMapTracedValues, NoTrace};
52use crate::dom::blob::Blob;
53use crate::dom::customelementregistry::CallbackReaction;
54use crate::dom::document::Document;
55use crate::dom::domtokenlist::DOMTokenList;
56use crate::dom::element::{AttributeMutation, Element};
57use crate::dom::event::{Event, EventBubbles, EventCancelable};
58use crate::dom::eventtarget::EventTarget;
59use crate::dom::file::File;
60use crate::dom::formdata::FormData;
61use crate::dom::formdataevent::FormDataEvent;
62use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
63use crate::dom::html::htmlcollection::CollectionFilter;
64use crate::dom::html::htmldatalistelement::HTMLDataListElement;
65use crate::dom::html::htmlelement::HTMLElement;
66use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
67use crate::dom::html::htmlformcontrolscollection::HTMLFormControlsCollection;
68use crate::dom::html::htmlimageelement::HTMLImageElement;
69use crate::dom::html::htmlinputelement::{HTMLInputElement, InputType};
70use crate::dom::html::htmllabelelement::HTMLLabelElement;
71use crate::dom::html::htmllegendelement::HTMLLegendElement;
72use crate::dom::html::htmlobjectelement::HTMLObjectElement;
73use crate::dom::html::htmloutputelement::HTMLOutputElement;
74use crate::dom::html::htmlselectelement::HTMLSelectElement;
75use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
76use crate::dom::node::{
77 BindContext, Node, NodeFlags, NodeTraits, UnbindContext, VecPreOrderInsertionHelper,
78};
79use crate::dom::nodelist::{NodeList, RadioListMode};
80use crate::dom::radionodelist::RadioNodeList;
81use crate::dom::submitevent::SubmitEvent;
82use crate::dom::virtualmethods::VirtualMethods;
83use crate::dom::window::Window;
84use crate::links::{LinkRelations, get_element_target};
85use crate::script_runtime::CanGc;
86use crate::script_thread::ScriptThread;
87
88#[dom_struct]
89pub(crate) struct HTMLFormElement {
90 htmlelement: HTMLElement,
91 marked_for_reset: Cell<bool>,
92 constructing_entry_list: Cell<bool>,
94 elements: DomOnceCell<HTMLFormControlsCollection>,
95 controls: DomRefCell<Vec<Dom<Element>>>,
96
97 #[allow(clippy::type_complexity)]
99 past_names_map:
100 DomRefCell<HashMapTracedValues<Atom, (Dom<Element>, NoTrace<usize>), FxBuildHasher>>,
101
102 current_name_generation: Cell<usize>,
104
105 firing_submission_events: Cell<bool>,
106 rel_list: MutNullableDom<DOMTokenList>,
107
108 planned_navigation: Cell<usize>,
110
111 #[no_trace]
112 relations: Cell<LinkRelations>,
113}
114
115impl HTMLFormElement {
116 fn new_inherited(
117 local_name: LocalName,
118 prefix: Option<Prefix>,
119 document: &Document,
120 ) -> HTMLFormElement {
121 HTMLFormElement {
122 htmlelement: HTMLElement::new_inherited_with_state(
123 ElementState::VALID,
124 local_name,
125 prefix,
126 document,
127 ),
128 marked_for_reset: Cell::new(false),
129 constructing_entry_list: Cell::new(false),
130 elements: Default::default(),
131 controls: DomRefCell::new(Vec::new()),
132 past_names_map: DomRefCell::new(HashMapTracedValues::new_fx()),
133 current_name_generation: Cell::new(0),
134 firing_submission_events: Cell::new(false),
135 rel_list: Default::default(),
136 planned_navigation: Default::default(),
137 relations: Cell::new(LinkRelations::empty()),
138 }
139 }
140
141 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
142 pub(crate) fn new(
143 local_name: LocalName,
144 prefix: Option<Prefix>,
145 document: &Document,
146 proto: Option<HandleObject>,
147 can_gc: CanGc,
148 ) -> DomRoot<HTMLFormElement> {
149 Node::reflect_node_with_proto(
150 Box::new(HTMLFormElement::new_inherited(local_name, prefix, document)),
151 document,
152 proto,
153 can_gc,
154 )
155 }
156
157 fn filter_for_radio_list(mode: RadioListMode, child: &Element, name: &Atom) -> bool {
158 if let Some(child) = child.downcast::<Element>() {
159 match mode {
160 RadioListMode::ControlsExceptImageInputs => {
161 if child
162 .downcast::<HTMLElement>()
163 .is_some_and(|c| c.is_listed_element()) &&
164 (child.get_id().is_some_and(|i| i == *name) ||
165 child.get_name().is_some_and(|n| n == *name))
166 {
167 if let Some(inp) = child.downcast::<HTMLInputElement>() {
168 return inp.input_type() != InputType::Image;
170 } else {
171 return true;
173 }
174 }
175 return false;
176 },
177 RadioListMode::Images => {
178 return child.is::<HTMLImageElement>() &&
179 (child.get_id().is_some_and(|i| i == *name) ||
180 child.get_name().is_some_and(|n| n == *name));
181 },
182 }
183 }
184 false
185 }
186
187 pub(crate) fn nth_for_radio_list(
188 &self,
189 index: u32,
190 mode: RadioListMode,
191 name: &Atom,
192 ) -> Option<DomRoot<Node>> {
193 self.controls
194 .borrow()
195 .iter()
196 .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
197 .nth(index as usize)
198 .map(|n| DomRoot::from_ref(n.upcast::<Node>()))
199 }
200
201 pub(crate) fn count_for_radio_list(&self, mode: RadioListMode, name: &Atom) -> u32 {
202 self.controls
203 .borrow()
204 .iter()
205 .filter(|n| HTMLFormElement::filter_for_radio_list(mode, n, name))
206 .count() as u32
207 }
208}
209
210impl HTMLFormElementMethods<crate::DomTypeHolder> for HTMLFormElement {
211 make_getter!(AcceptCharset, "accept-charset");
213
214 make_setter!(SetAcceptCharset, "accept-charset");
216
217 make_form_action_getter!(Action, "action");
219
220 make_setter!(SetAction, "action");
222
223 make_enumerated_getter!(
225 Autocomplete,
226 "autocomplete",
227 "on" | "off",
228 missing => "on",
229 invalid => "on"
230 );
231
232 make_setter!(SetAutocomplete, "autocomplete");
234
235 make_enumerated_getter!(
237 Enctype,
238 "enctype",
239 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
240 missing => "application/x-www-form-urlencoded",
241 invalid => "application/x-www-form-urlencoded"
242 );
243
244 make_setter!(SetEnctype, "enctype");
246
247 fn Encoding(&self) -> DOMString {
249 self.Enctype()
250 }
251
252 fn SetEncoding(&self, value: DOMString) {
254 self.SetEnctype(value)
255 }
256
257 make_enumerated_getter!(
259 Method,
260 "method",
261 "get" | "post" | "dialog",
262 missing => "get",
263 invalid => "get"
264 );
265
266 make_setter!(SetMethod, "method");
268
269 make_getter!(Name, "name");
271
272 make_atomic_setter!(SetName, "name");
274
275 make_bool_getter!(NoValidate, "novalidate");
277
278 make_bool_setter!(SetNoValidate, "novalidate");
280
281 make_getter!(Target, "target");
283
284 make_setter!(SetTarget, "target");
286
287 make_getter!(Rel, "rel");
289
290 fn Submit(&self, can_gc: CanGc) {
292 self.submit(
293 SubmittedFrom::FromForm,
294 FormSubmitterElement::Form(self),
295 can_gc,
296 );
297 }
298
299 fn RequestSubmit(&self, submitter: Option<&HTMLElement>, can_gc: CanGc) -> Fallible<()> {
301 let submitter: FormSubmitterElement = match submitter {
302 Some(submitter_element) => {
303 let error_not_a_submit_button =
305 Err(Error::Type("submitter must be a submit button".to_string()));
306
307 let element = match submitter_element.upcast::<Node>().type_id() {
308 NodeTypeId::Element(ElementTypeId::HTMLElement(element)) => element,
309 _ => {
310 return error_not_a_submit_button;
311 },
312 };
313
314 let submit_button = match element {
315 HTMLElementTypeId::HTMLInputElement => FormSubmitterElement::Input(
316 submitter_element
317 .downcast::<HTMLInputElement>()
318 .expect("Failed to downcast submitter elem to HTMLInputElement."),
319 ),
320 HTMLElementTypeId::HTMLButtonElement => FormSubmitterElement::Button(
321 submitter_element
322 .downcast::<HTMLButtonElement>()
323 .expect("Failed to downcast submitter elem to HTMLButtonElement."),
324 ),
325 _ => {
326 return error_not_a_submit_button;
327 },
328 };
329
330 if !submit_button.is_submit_button() {
331 return error_not_a_submit_button;
332 }
333
334 let submitters_owner = submit_button.form_owner();
335
336 let owner = match submitters_owner {
338 Some(owner) => owner,
339 None => {
340 return Err(Error::NotFound(None));
341 },
342 };
343
344 if *owner != *self {
345 return Err(Error::NotFound(None));
346 }
347
348 submit_button
349 },
350 None => {
351 FormSubmitterElement::Form(self)
353 },
354 };
355 self.submit(SubmittedFrom::NotFromForm, submitter, can_gc);
357 Ok(())
358 }
359
360 fn Reset(&self, can_gc: CanGc) {
362 self.reset(ResetFrom::FromForm, can_gc);
363 }
364
365 fn Elements(&self, can_gc: CanGc) -> DomRoot<HTMLFormControlsCollection> {
367 #[derive(JSTraceable, MallocSizeOf)]
368 struct ElementsFilter {
369 form: DomRoot<HTMLFormElement>,
370 }
371 impl CollectionFilter for ElementsFilter {
372 fn filter<'a>(&self, elem: &'a Element, _root: &'a Node) -> bool {
373 let form_owner = match elem.upcast::<Node>().type_id() {
374 NodeTypeId::Element(ElementTypeId::HTMLElement(t)) => match t {
375 HTMLElementTypeId::HTMLButtonElement => {
376 elem.downcast::<HTMLButtonElement>().unwrap().form_owner()
377 },
378 HTMLElementTypeId::HTMLFieldSetElement => {
379 elem.downcast::<HTMLFieldSetElement>().unwrap().form_owner()
380 },
381 HTMLElementTypeId::HTMLInputElement => {
382 let input_elem = elem.downcast::<HTMLInputElement>().unwrap();
383 if input_elem.input_type() == InputType::Image {
384 return false;
385 }
386 input_elem.form_owner()
387 },
388 HTMLElementTypeId::HTMLObjectElement => {
389 elem.downcast::<HTMLObjectElement>().unwrap().form_owner()
390 },
391 HTMLElementTypeId::HTMLOutputElement => {
392 elem.downcast::<HTMLOutputElement>().unwrap().form_owner()
393 },
394 HTMLElementTypeId::HTMLSelectElement => {
395 elem.downcast::<HTMLSelectElement>().unwrap().form_owner()
396 },
397 HTMLElementTypeId::HTMLTextAreaElement => {
398 elem.downcast::<HTMLTextAreaElement>().unwrap().form_owner()
399 },
400 HTMLElementTypeId::HTMLElement => {
401 let html_element = elem.downcast::<HTMLElement>().unwrap();
402 if html_element.is_form_associated_custom_element() {
403 html_element.form_owner()
404 } else {
405 return false;
406 }
407 },
408 _ => {
409 debug_assert!(
410 !elem.downcast::<HTMLElement>().unwrap().is_listed_element()
411 );
412 return false;
413 },
414 },
415 _ => return false,
416 };
417
418 match form_owner {
419 Some(form_owner) => form_owner == self.form,
420 None => false,
421 }
422 }
423 }
424 DomRoot::from_ref(self.elements.init_once(|| {
425 let filter = Box::new(ElementsFilter {
426 form: DomRoot::from_ref(self),
427 });
428 let window = self.owner_window();
429 HTMLFormControlsCollection::new(&window, self, filter, can_gc)
430 }))
431 }
432
433 fn Length(&self) -> u32 {
435 self.Elements(CanGc::note()).Length()
436 }
437
438 fn IndexedGetter(&self, index: u32, can_gc: CanGc) -> Option<DomRoot<Element>> {
440 let elements = self.Elements(can_gc);
441 elements.IndexedGetter(index)
442 }
443
444 fn NamedGetter(&self, name: DOMString, can_gc: CanGc) -> Option<RadioNodeListOrElement> {
446 let window = self.owner_window();
447
448 let name = Atom::from(name);
449
450 let mut candidates =
452 RadioNodeList::new_controls_except_image_inputs(&window, self, &name, can_gc);
453 let mut candidates_length = candidates.Length();
454
455 if candidates_length == 0 {
457 candidates = RadioNodeList::new_images(&window, self, &name, can_gc);
458 candidates_length = candidates.Length();
459 }
460
461 let mut past_names_map = self.past_names_map.borrow_mut();
462
463 if candidates_length == 0 {
465 if past_names_map.contains_key(&name) {
466 return Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
467 &*past_names_map.get(&name).unwrap().0,
468 )));
469 }
470 return None;
471 }
472
473 if candidates_length > 1 {
475 return Some(RadioNodeListOrElement::RadioNodeList(candidates));
476 }
477
478 let element_node = candidates.upcast::<NodeList>().Item(0).unwrap();
481 past_names_map.insert(
482 name,
483 (
484 Dom::from_ref(element_node.downcast::<Element>().unwrap()),
485 NoTrace(self.current_name_generation.get() + 1),
486 ),
487 );
488 self.current_name_generation
489 .set(self.current_name_generation.get() + 1);
490
491 Some(RadioNodeListOrElement::Element(DomRoot::from_ref(
493 element_node.downcast::<Element>().unwrap(),
494 )))
495 }
496
497 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
499 self.upcast::<Element>()
500 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
501 }
502
503 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
505 self.rel_list.or_init(|| {
506 DOMTokenList::new(
507 self.upcast(),
508 &local_name!("rel"),
509 Some(vec![
510 Atom::from("noopener"),
511 Atom::from("noreferrer"),
512 Atom::from("opener"),
513 ]),
514 can_gc,
515 )
516 })
517 }
518
519 #[allow(non_snake_case)]
521 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().immutable().clone()),
876 action_components,
877 None,
878 target_window.as_global_scope().get_referrer(),
879 target_document.get_referrer_policy(),
880 Some(target_window.as_global_scope().is_secure_context()),
881 Some(target_document.insecure_requests_policy()),
882 target_document.has_trustworthy_ancestor_origin(),
883 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
884 );
885
886 match (&*scheme, method) {
888 (_, FormMethod::Dialog) => {
889 },
892 ("http", FormMethod::Get) | ("https", FormMethod::Get) | ("data", FormMethod::Get) => {
894 load_data
895 .headers
896 .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
897 self.mutate_action_url(&mut form_data, load_data, encoding, target_window);
898 },
899 ("http", FormMethod::Post) | ("https", FormMethod::Post) => {
901 load_data.method = Method::POST;
902 self.submit_entity_body(
903 &mut form_data,
904 load_data,
905 enctype,
906 encoding,
907 target_window,
908 can_gc,
909 );
910 },
911 ("file", _) |
913 ("about", _) |
914 ("data", FormMethod::Post) |
915 ("ftp", _) |
916 ("javascript", _) => {
917 self.plan_to_navigate(load_data, target_window);
918 },
919 ("mailto", FormMethod::Post) => {
920 },
923 ("mailto", FormMethod::Get) => {
924 },
927 _ => (),
928 }
929 }
930
931 fn mutate_action_url(
933 &self,
934 form_data: &mut [FormDatum],
935 mut load_data: LoadData,
936 encoding: &'static Encoding,
937 target: &Window,
938 ) {
939 let charset = encoding.name();
940
941 self.set_url_query_pairs(
942 &mut load_data.url,
943 form_data
944 .iter()
945 .map(|field| (field.name.str(), field.replace_value(charset))),
946 );
947
948 self.plan_to_navigate(load_data, target);
949 }
950
951 fn submit_entity_body(
953 &self,
954 form_data: &mut [FormDatum],
955 mut load_data: LoadData,
956 enctype: FormEncType,
957 encoding: &'static Encoding,
958 target: &Window,
959 can_gc: CanGc,
960 ) {
961 let boundary = generate_boundary();
962 let bytes = match enctype {
963 FormEncType::UrlEncoded => {
964 let charset = encoding.name();
965 load_data
966 .headers
967 .typed_insert(ContentType::from(mime::APPLICATION_WWW_FORM_URLENCODED));
968
969 let mut url = load_data.url.clone();
970 self.set_url_query_pairs(
971 &mut url,
972 form_data
973 .iter()
974 .map(|field| (field.name.str(), field.replace_value(charset))),
975 );
976
977 url.query().unwrap_or("").to_string().into_bytes()
978 },
979 FormEncType::MultipartFormData => {
980 let mime: Mime = format!("multipart/form-data; boundary={}", boundary)
981 .parse()
982 .unwrap();
983 load_data.headers.typed_insert(ContentType::from(mime));
984 encode_multipart_form_data(form_data, boundary, encoding)
985 },
986 FormEncType::TextPlain => {
987 load_data
988 .headers
989 .typed_insert(ContentType::from(mime::TEXT_PLAIN));
990 self.encode_plaintext(form_data).into_bytes()
991 },
992 };
993
994 let global = self.global();
995
996 let request_body = bytes
997 .extract(&global, can_gc)
998 .expect("Couldn't extract body.")
999 .into_net_request_body()
1000 .0;
1001 load_data.data = Some(request_body);
1002
1003 self.plan_to_navigate(load_data, target);
1004 }
1005
1006 fn set_url_query_pairs<T>(
1007 &self,
1008 url: &mut servo_url::ServoUrl,
1009 pairs: impl Iterator<Item = (T, String)>,
1010 ) where
1011 T: AsRef<str>,
1012 {
1013 let encoding = self.pick_encoding();
1014 url.as_mut_url()
1015 .query_pairs_mut()
1016 .encoding_override(Some(&|s| encoding.encode(s).0))
1017 .clear()
1018 .extend_pairs(pairs);
1019 }
1020
1021 fn plan_to_navigate(&self, mut load_data: LoadData, target: &Window) {
1023 let elem = self.upcast::<Element>();
1028 let referrer = match elem.get_attribute(&ns!(), &local_name!("rel")) {
1029 Some(ref link_types) if link_types.Value().contains("noreferrer") => {
1030 Referrer::NoReferrer
1031 },
1032 _ => target.as_global_scope().get_referrer(),
1033 };
1034
1035 self.planned_navigation
1038 .set(self.planned_navigation.get().wrapping_add(1));
1039 let planned_navigation = self.planned_navigation.get();
1040
1041 let ongoing_navigation = target.set_ongoing_navigation();
1057
1058 let referrer_policy = target.Document().get_referrer_policy();
1059 load_data.creator_pipeline_id = Some(target.pipeline_id());
1060 load_data.referrer = referrer;
1061 load_data.referrer_policy = referrer_policy;
1062
1063 let form = Trusted::new(self);
1066 let window = Trusted::new(target);
1067 let task = task!(navigate_to_form_planned_navigation: move || {
1068 if planned_navigation != form.root().planned_navigation.get() {
1072 return;
1073 }
1074
1075 if ongoing_navigation != window.root().ongoing_navigation() {
1078 return;
1079 }
1080
1081 window
1083 .root()
1084 .load_url(
1085 NavigationHistoryBehavior::Push,
1086 false,
1087 load_data,
1088 CanGc::note(),
1089 );
1090 });
1091
1092 target
1097 .global()
1098 .task_manager()
1099 .dom_manipulation_task_source()
1100 .queue(task)
1101 }
1102
1103 fn interactive_validation(&self, can_gc: CanGc) -> Result<(), ()> {
1106 let unhandled_invalid_controls = match self.static_validation(can_gc) {
1111 Ok(()) => return Ok(()),
1112 Err(err) => err,
1113 };
1114
1115 let mut first = true;
1118
1119 for elem in unhandled_invalid_controls {
1120 if let Some(validatable) = elem.as_maybe_validatable() {
1121 println!("Validation error: {}", validatable.validation_message());
1122 }
1123 if first {
1124 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1125 html_elem.Focus(&FocusOptions::default(), can_gc);
1132 first = false;
1133 }
1134 }
1135 }
1136
1137 Err(())
1141 }
1142
1143 fn static_validation(&self, can_gc: CanGc) -> Result<(), Vec<DomRoot<Element>>> {
1146 let invalid_controls = self
1148 .controls
1149 .borrow()
1150 .iter()
1151 .filter_map(|field| {
1152 if let Some(element) = field.downcast::<Element>() {
1153 if element.is_invalid(true, can_gc) {
1154 Some(DomRoot::from_ref(element))
1155 } else {
1156 None
1157 }
1158 } else {
1159 None
1160 }
1161 })
1162 .collect::<Vec<DomRoot<Element>>>();
1163 if invalid_controls.is_empty() {
1165 return Ok(());
1166 }
1167 let unhandled_invalid_controls = invalid_controls
1169 .into_iter()
1170 .filter_map(|field| {
1171 let not_canceled = field
1174 .upcast::<EventTarget>()
1175 .fire_cancelable_event(atom!("invalid"), can_gc);
1176 if not_canceled {
1178 return Some(field);
1179 }
1180 None
1181 })
1182 .collect::<Vec<DomRoot<Element>>>();
1183 Err(unhandled_invalid_controls)
1185 }
1186
1187 fn get_unclean_dataset(
1192 &self,
1193 submitter: Option<FormSubmitterElement>,
1194 encoding: Option<&'static Encoding>,
1195 can_gc: CanGc,
1196 ) -> Vec<FormDatum> {
1197 let mut data_set = Vec::new();
1198 for child in self.controls.borrow().iter() {
1199 if child.disabled_state() {
1201 continue;
1202 }
1203 let child = child.upcast::<Node>();
1204
1205 if child.ancestors().any(|a| a.is::<HTMLDataListElement>()) {
1207 continue;
1208 }
1209 if let NodeTypeId::Element(ElementTypeId::HTMLElement(element)) = child.type_id() {
1210 match element {
1211 HTMLElementTypeId::HTMLInputElement => {
1212 let input = child.downcast::<HTMLInputElement>().unwrap();
1213 data_set.append(&mut input.form_datums(submitter, encoding));
1214 },
1215 HTMLElementTypeId::HTMLButtonElement => {
1216 let button = child.downcast::<HTMLButtonElement>().unwrap();
1217 if let Some(datum) = button.form_datum(submitter) {
1218 data_set.push(datum);
1219 }
1220 },
1221 HTMLElementTypeId::HTMLObjectElement => {
1222 },
1224 HTMLElementTypeId::HTMLSelectElement => {
1225 let select = child.downcast::<HTMLSelectElement>().unwrap();
1226 select.push_form_data(&mut data_set);
1227 },
1228 HTMLElementTypeId::HTMLTextAreaElement => {
1229 let textarea = child.downcast::<HTMLTextAreaElement>().unwrap();
1230 let name = textarea.Name();
1231 if !name.is_empty() {
1232 data_set.push(FormDatum {
1233 ty: textarea.Type(),
1234 name,
1235 value: FormDatumValue::String(textarea.Value()),
1236 });
1237 }
1238 },
1239 HTMLElementTypeId::HTMLElement => {
1240 let custom = child.downcast::<HTMLElement>().unwrap();
1241 if custom.is_form_associated_custom_element() {
1242 let internals =
1244 custom.upcast::<Element>().ensure_element_internals(can_gc);
1245 internals.perform_entry_construction(&mut data_set);
1246 }
1248 },
1249 _ => (),
1250 }
1251 }
1252
1253 let child_element = child.downcast::<Element>().unwrap();
1257 let input_matches = child_element
1258 .downcast::<HTMLInputElement>()
1259 .is_some_and(|input| {
1260 matches!(input.input_type(), InputType::Text | InputType::Search)
1261 });
1262 let textarea_matches = child_element.is::<HTMLTextAreaElement>();
1263 let dirname = child_element.get_string_attribute(&local_name!("dirname"));
1264 if (input_matches || textarea_matches) && !dirname.is_empty() {
1265 let dir = DOMString::from(child_element.directionality());
1266 data_set.push(FormDatum {
1267 ty: DOMString::from("string"),
1268 name: dirname,
1269 value: FormDatumValue::String(dir),
1270 });
1271 }
1272 }
1273 data_set
1274 }
1275
1276 pub(crate) fn get_form_dataset(
1278 &self,
1279 submitter: Option<FormSubmitterElement>,
1280 encoding: Option<&'static Encoding>,
1281 can_gc: CanGc,
1282 ) -> Option<Vec<FormDatum>> {
1283 if self.constructing_entry_list.get() {
1285 return None;
1286 }
1287
1288 self.constructing_entry_list.set(true);
1290
1291 let ret = self.get_unclean_dataset(submitter, encoding, can_gc);
1293
1294 let window = self.owner_window();
1295
1296 let form_data = FormData::new(Some(ret), &window.global(), can_gc);
1298
1299 let event = FormDataEvent::new(
1301 &window,
1302 atom!("formdata"),
1303 EventBubbles::Bubbles,
1304 EventCancelable::NotCancelable,
1305 &form_data,
1306 can_gc,
1307 );
1308
1309 event
1310 .upcast::<Event>()
1311 .fire(self.upcast::<EventTarget>(), can_gc);
1312
1313 self.constructing_entry_list.set(false);
1315
1316 Some(form_data.datums())
1318 }
1319
1320 pub(crate) fn reset(&self, _reset_method_flag: ResetFrom, can_gc: CanGc) {
1322 if self.marked_for_reset.get() {
1324 return;
1325 } else {
1326 self.marked_for_reset.set(true);
1327 }
1328
1329 let reset = self
1333 .upcast::<EventTarget>()
1334 .fire_bubbling_cancelable_event(atom!("reset"), can_gc);
1335 if !reset {
1336 return;
1337 }
1338
1339 let controls: Vec<_> = self
1340 .controls
1341 .borrow()
1342 .iter()
1343 .map(|c| c.as_rooted())
1344 .collect();
1345
1346 for child in controls {
1347 let child = child.upcast::<Node>();
1348
1349 match child.type_id() {
1350 NodeTypeId::Element(ElementTypeId::HTMLElement(
1351 HTMLElementTypeId::HTMLInputElement,
1352 )) => {
1353 child.downcast::<HTMLInputElement>().unwrap().reset(can_gc);
1354 },
1355 NodeTypeId::Element(ElementTypeId::HTMLElement(
1356 HTMLElementTypeId::HTMLSelectElement,
1357 )) => {
1358 child.downcast::<HTMLSelectElement>().unwrap().reset();
1359 },
1360 NodeTypeId::Element(ElementTypeId::HTMLElement(
1361 HTMLElementTypeId::HTMLTextAreaElement,
1362 )) => {
1363 child.downcast::<HTMLTextAreaElement>().unwrap().reset();
1364 },
1365 NodeTypeId::Element(ElementTypeId::HTMLElement(
1366 HTMLElementTypeId::HTMLOutputElement,
1367 )) => {
1368 child.downcast::<HTMLOutputElement>().unwrap().reset(can_gc);
1369 },
1370 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
1371 let html_element = child.downcast::<HTMLElement>().unwrap();
1372 if html_element.is_form_associated_custom_element() {
1373 ScriptThread::enqueue_callback_reaction(
1374 html_element.upcast::<Element>(),
1375 CallbackReaction::FormReset,
1376 None,
1377 )
1378 }
1379 },
1380 _ => {},
1381 }
1382 }
1383 self.marked_for_reset.set(false);
1384 }
1385
1386 fn add_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1387 {
1388 let root = self.upcast::<Element>().root_element();
1389 let root = root.upcast::<Node>();
1390 let mut controls = self.controls.borrow_mut();
1391 controls.insert_pre_order(control.to_element(), root);
1392 }
1393 self.update_validity(can_gc);
1394 }
1395
1396 fn remove_control<T: ?Sized + FormControl>(&self, control: &T, can_gc: CanGc) {
1397 {
1398 let control = control.to_element();
1399 let mut controls = self.controls.borrow_mut();
1400 controls
1401 .iter()
1402 .position(|c| &**c == control)
1403 .map(|idx| controls.remove(idx));
1404
1405 let mut past_names_map = self.past_names_map.borrow_mut();
1410 past_names_map.0.retain(|_k, v| v.0 != control);
1411 }
1412 self.update_validity(can_gc);
1413 }
1414}
1415
1416#[derive(Clone, JSTraceable, MallocSizeOf)]
1417pub(crate) enum FormDatumValue {
1418 #[allow(dead_code)]
1419 File(DomRoot<File>),
1420 String(DOMString),
1421}
1422
1423#[derive(Clone, JSTraceable, MallocSizeOf)]
1424pub(crate) struct FormDatum {
1425 pub(crate) ty: DOMString,
1426 pub(crate) name: DOMString,
1427 pub(crate) value: FormDatumValue,
1428}
1429
1430impl FormDatum {
1431 pub(crate) fn replace_value(&self, charset: &str) -> String {
1432 if self.name.to_ascii_lowercase() == "_charset_" && self.ty == "hidden" {
1433 return charset.to_string();
1434 }
1435
1436 match self.value {
1437 FormDatumValue::File(ref f) => String::from(f.name().clone()),
1438 FormDatumValue::String(ref s) => String::from(s.clone()),
1439 }
1440 }
1441}
1442
1443#[derive(Clone, Copy, MallocSizeOf)]
1444pub(crate) enum FormEncType {
1445 TextPlain,
1446 UrlEncoded,
1447 MultipartFormData,
1448}
1449
1450#[derive(Clone, Copy, MallocSizeOf)]
1451pub(crate) enum FormMethod {
1452 Get,
1453 Post,
1454 Dialog,
1455}
1456
1457#[derive(Clone, Copy, MallocSizeOf)]
1459pub(crate) enum FormSubmitterElement<'a> {
1460 Form(&'a HTMLFormElement),
1461 Input(&'a HTMLInputElement),
1462 Button(&'a HTMLButtonElement),
1463 }
1466
1467impl FormSubmitterElement<'_> {
1468 fn action(&self) -> DOMString {
1469 match *self {
1470 FormSubmitterElement::Form(form) => form.Action(),
1471 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1472 &local_name!("formaction"),
1473 |i| i.FormAction(),
1474 |f| f.Action(),
1475 ),
1476 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1477 &local_name!("formaction"),
1478 |i| i.FormAction(),
1479 |f| f.Action(),
1480 ),
1481 }
1482 }
1483
1484 fn enctype(&self) -> FormEncType {
1485 let attr = match *self {
1486 FormSubmitterElement::Form(form) => form.Enctype(),
1487 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1488 &local_name!("formenctype"),
1489 |i| i.FormEnctype(),
1490 |f| f.Enctype(),
1491 ),
1492 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1493 &local_name!("formenctype"),
1494 |i| i.FormEnctype(),
1495 |f| f.Enctype(),
1496 ),
1497 };
1498 match &*attr.str() {
1499 "multipart/form-data" => FormEncType::MultipartFormData,
1500 "text/plain" => FormEncType::TextPlain,
1501 _ => FormEncType::UrlEncoded,
1504 }
1505 }
1506
1507 fn method(&self) -> FormMethod {
1508 let attr = match *self {
1509 FormSubmitterElement::Form(form) => form.Method(),
1510 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1511 &local_name!("formmethod"),
1512 |i| i.FormMethod(),
1513 |f| f.Method(),
1514 ),
1515 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1516 &local_name!("formmethod"),
1517 |i| i.FormMethod(),
1518 |f| f.Method(),
1519 ),
1520 };
1521 match &*attr.str() {
1522 "dialog" => FormMethod::Dialog,
1523 "post" => FormMethod::Post,
1524 _ => FormMethod::Get,
1525 }
1526 }
1527
1528 fn target(&self) -> DOMString {
1529 match *self {
1530 FormSubmitterElement::Form(form) => form.Target(),
1531 FormSubmitterElement::Input(input_element) => input_element.get_form_attribute(
1532 &local_name!("formtarget"),
1533 |i| i.FormTarget(),
1534 |f| f.Target(),
1535 ),
1536 FormSubmitterElement::Button(button_element) => button_element.get_form_attribute(
1537 &local_name!("formtarget"),
1538 |i| i.FormTarget(),
1539 |f| f.Target(),
1540 ),
1541 }
1542 }
1543
1544 fn no_validate(&self, _form_owner: &HTMLFormElement) -> bool {
1545 match *self {
1546 FormSubmitterElement::Form(form) => form.NoValidate(),
1547 FormSubmitterElement::Input(input_element) => input_element.get_form_boolean_attribute(
1548 &local_name!("formnovalidate"),
1549 |i| i.FormNoValidate(),
1550 |f| f.NoValidate(),
1551 ),
1552 FormSubmitterElement::Button(button_element) => button_element
1553 .get_form_boolean_attribute(
1554 &local_name!("formnovalidate"),
1555 |i| i.FormNoValidate(),
1556 |f| f.NoValidate(),
1557 ),
1558 }
1559 }
1560
1561 pub(crate) fn is_submit_button(&self) -> bool {
1563 match *self {
1564 FormSubmitterElement::Input(input_element) => input_element.is_submit_button(),
1567 FormSubmitterElement::Button(button_element) => button_element.is_submit_button(),
1569 _ => false,
1570 }
1571 }
1572
1573 pub(crate) fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1575 match *self {
1576 FormSubmitterElement::Button(button_el) => button_el.form_owner(),
1577 FormSubmitterElement::Input(input_el) => input_el.form_owner(),
1578 _ => None,
1579 }
1580 }
1581}
1582
1583pub(crate) trait FormControl: DomObject {
1584 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>>;
1585
1586 fn set_form_owner(&self, form: Option<&HTMLFormElement>);
1587
1588 fn to_element(&self) -> ∈
1589
1590 fn is_listed(&self) -> bool {
1591 true
1592 }
1593
1594 fn set_form_owner_from_parser(&self, form: &HTMLFormElement, can_gc: CanGc) {
1599 let elem = self.to_element();
1600 let node = elem.upcast::<Node>();
1601 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, true);
1602 form.add_control(self, can_gc);
1603 self.set_form_owner(Some(form));
1604 }
1605
1606 fn reset_form_owner(&self, can_gc: CanGc) {
1608 let elem = self.to_element();
1609 let node = elem.upcast::<Node>();
1610 let old_owner = self.form_owner();
1611 let has_form_id = elem.has_attribute(&local_name!("form"));
1612 let nearest_form_ancestor = node
1613 .ancestors()
1614 .filter_map(DomRoot::downcast::<HTMLFormElement>)
1615 .next();
1616
1617 if old_owner.is_some() &&
1619 !(self.is_listed() && has_form_id) &&
1620 nearest_form_ancestor == old_owner
1621 {
1622 return;
1623 }
1624
1625 let new_owner = if self.is_listed() && has_form_id && elem.is_connected() {
1626 let doc = node.owner_document();
1628 let form_id = elem.get_string_attribute(&local_name!("form"));
1629 doc.GetElementById(form_id)
1630 .and_then(DomRoot::downcast::<HTMLFormElement>)
1631 } else {
1632 nearest_form_ancestor
1634 };
1635
1636 if old_owner != new_owner {
1637 if let Some(o) = old_owner {
1638 o.remove_control(self, can_gc);
1639 }
1640 if let Some(ref new_owner) = new_owner {
1641 new_owner.add_control(self, can_gc);
1642 }
1643 if let Some(html_elem) = elem.downcast::<HTMLElement>() {
1645 if html_elem.is_form_associated_custom_element() {
1646 ScriptThread::enqueue_callback_reaction(
1647 elem,
1648 CallbackReaction::FormAssociated(
1649 new_owner.as_ref().map(|form| DomRoot::from_ref(&**form)),
1650 ),
1651 None,
1652 )
1653 }
1654 }
1655 self.set_form_owner(new_owner.as_deref());
1656 }
1657 }
1658
1659 fn form_attribute_mutated(&self, mutation: AttributeMutation, can_gc: CanGc) {
1661 match mutation {
1662 AttributeMutation::Set(_) => {
1663 self.register_if_necessary();
1664 },
1665 AttributeMutation::Removed => {
1666 self.unregister_if_necessary();
1667 },
1668 }
1669
1670 self.reset_form_owner(can_gc);
1671 }
1672
1673 fn register_if_necessary(&self) {
1675 let elem = self.to_element();
1676 let form_id = elem.get_string_attribute(&local_name!("form"));
1677 let node = elem.upcast::<Node>();
1678
1679 if self.is_listed() && !form_id.is_empty() && node.is_connected() {
1680 node.owner_document()
1681 .register_form_id_listener(form_id, self);
1682 }
1683 }
1684
1685 fn unregister_if_necessary(&self) {
1686 let elem = self.to_element();
1687 let form_id = elem.get_string_attribute(&local_name!("form"));
1688
1689 if self.is_listed() && !form_id.is_empty() {
1690 elem.owner_document()
1691 .unregister_form_id_listener(form_id, self);
1692 }
1693 }
1694
1695 fn bind_form_control_to_tree(&self, can_gc: CanGc) {
1697 let elem = self.to_element();
1698 let node = elem.upcast::<Node>();
1699
1700 let must_skip_reset = node.get_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER);
1705 node.set_flag(NodeFlags::PARSER_ASSOCIATED_FORM_OWNER, false);
1706
1707 if !must_skip_reset {
1708 self.form_attribute_mutated(AttributeMutation::Set(None), can_gc);
1709 }
1710 }
1711
1712 fn unbind_form_control_from_tree(&self, can_gc: CanGc) {
1714 let elem = self.to_element();
1715 let has_form_attr = elem.has_attribute(&local_name!("form"));
1716 let same_subtree = self
1717 .form_owner()
1718 .is_none_or(|form| elem.is_in_same_home_subtree(&*form));
1719
1720 self.unregister_if_necessary();
1721
1722 if !same_subtree || (self.is_listed() && has_form_attr) {
1728 self.reset_form_owner(can_gc);
1729 }
1730 }
1731
1732 fn get_form_attribute<InputFn, OwnerFn>(
1733 &self,
1734 attr: &LocalName,
1735 input: InputFn,
1736 owner: OwnerFn,
1737 ) -> DOMString
1738 where
1739 InputFn: Fn(&Self) -> DOMString,
1740 OwnerFn: Fn(&HTMLFormElement) -> DOMString,
1741 Self: Sized,
1742 {
1743 if self.to_element().has_attribute(attr) {
1744 input(self)
1745 } else {
1746 self.form_owner().map_or(DOMString::new(), |t| owner(&t))
1747 }
1748 }
1749
1750 fn get_form_boolean_attribute<InputFn, OwnerFn>(
1751 &self,
1752 attr: &LocalName,
1753 input: InputFn,
1754 owner: OwnerFn,
1755 ) -> bool
1756 where
1757 InputFn: Fn(&Self) -> bool,
1758 OwnerFn: Fn(&HTMLFormElement) -> bool,
1759 Self: Sized,
1760 {
1761 if self.to_element().has_attribute(attr) {
1762 input(self)
1763 } else {
1764 self.form_owner().is_some_and(|t| owner(&t))
1765 }
1766 }
1767
1768 fn is_candidate_for_constraint_validation(&self) -> bool {
1770 let element = self.to_element();
1771 let html_element = element.downcast::<HTMLElement>();
1772 if let Some(html_element) = html_element {
1773 html_element.is_submittable_element() || element.is_instance_validatable()
1774 } else {
1775 false
1776 }
1777 }
1778
1779 }
1782
1783impl VirtualMethods for HTMLFormElement {
1784 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1785 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1786 }
1787
1788 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1789 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1790
1791 rooted_vec!(let mut to_reset);
1794 to_reset.extend(
1795 self.controls
1796 .borrow()
1797 .iter()
1798 .filter(|c| !c.is_in_same_home_subtree(self))
1799 .cloned(),
1800 );
1801
1802 for control in to_reset.iter() {
1803 control
1804 .as_maybe_form_control()
1805 .expect("Element must be a form control")
1806 .reset_form_owner(can_gc);
1807 }
1808 }
1809
1810 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1811 match name {
1812 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
1813 _ => self
1814 .super_type()
1815 .unwrap()
1816 .parse_plain_attribute(name, value),
1817 }
1818 }
1819
1820 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1821 self.super_type()
1822 .unwrap()
1823 .attribute_mutated(attr, mutation, can_gc);
1824
1825 match *attr.local_name() {
1826 local_name!("rel") | local_name!("rev") => {
1827 self.relations
1828 .set(LinkRelations::for_element(self.upcast()));
1829 },
1830 _ => {},
1831 }
1832 }
1833
1834 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1835 if let Some(s) = self.super_type() {
1836 s.bind_to_tree(context, can_gc);
1837 }
1838
1839 self.relations
1840 .set(LinkRelations::for_element(self.upcast()));
1841 }
1842}
1843
1844pub(crate) trait FormControlElementHelpers {
1845 fn as_maybe_form_control(&self) -> Option<&dyn FormControl>;
1846}
1847
1848impl FormControlElementHelpers for Element {
1849 fn as_maybe_form_control(&self) -> Option<&dyn FormControl> {
1850 let node = self.upcast::<Node>();
1851
1852 match node.type_id() {
1853 NodeTypeId::Element(ElementTypeId::HTMLElement(
1854 HTMLElementTypeId::HTMLButtonElement,
1855 )) => Some(self.downcast::<HTMLButtonElement>().unwrap() as &dyn FormControl),
1856 NodeTypeId::Element(ElementTypeId::HTMLElement(
1857 HTMLElementTypeId::HTMLFieldSetElement,
1858 )) => Some(self.downcast::<HTMLFieldSetElement>().unwrap() as &dyn FormControl),
1859 NodeTypeId::Element(ElementTypeId::HTMLElement(
1860 HTMLElementTypeId::HTMLImageElement,
1861 )) => Some(self.downcast::<HTMLImageElement>().unwrap() as &dyn FormControl),
1862 NodeTypeId::Element(ElementTypeId::HTMLElement(
1863 HTMLElementTypeId::HTMLInputElement,
1864 )) => Some(self.downcast::<HTMLInputElement>().unwrap() as &dyn FormControl),
1865 NodeTypeId::Element(ElementTypeId::HTMLElement(
1866 HTMLElementTypeId::HTMLLabelElement,
1867 )) => Some(self.downcast::<HTMLLabelElement>().unwrap() as &dyn FormControl),
1868 NodeTypeId::Element(ElementTypeId::HTMLElement(
1869 HTMLElementTypeId::HTMLLegendElement,
1870 )) => Some(self.downcast::<HTMLLegendElement>().unwrap() as &dyn FormControl),
1871 NodeTypeId::Element(ElementTypeId::HTMLElement(
1872 HTMLElementTypeId::HTMLObjectElement,
1873 )) => Some(self.downcast::<HTMLObjectElement>().unwrap() as &dyn FormControl),
1874 NodeTypeId::Element(ElementTypeId::HTMLElement(
1875 HTMLElementTypeId::HTMLOutputElement,
1876 )) => Some(self.downcast::<HTMLOutputElement>().unwrap() as &dyn FormControl),
1877 NodeTypeId::Element(ElementTypeId::HTMLElement(
1878 HTMLElementTypeId::HTMLSelectElement,
1879 )) => Some(self.downcast::<HTMLSelectElement>().unwrap() as &dyn FormControl),
1880 NodeTypeId::Element(ElementTypeId::HTMLElement(
1881 HTMLElementTypeId::HTMLTextAreaElement,
1882 )) => Some(self.downcast::<HTMLTextAreaElement>().unwrap() as &dyn FormControl),
1883 _ => self.downcast::<HTMLElement>().and_then(|elem| {
1884 if elem.is_form_associated_custom_element() {
1885 Some(elem as &dyn FormControl)
1886 } else {
1887 None
1888 }
1889 }),
1890 }
1891 }
1892}
1893
1894pub(crate) fn encode_multipart_form_data(
1896 form_data: &mut [FormDatum],
1897 boundary: String,
1898 encoding: &'static Encoding,
1899) -> Vec<u8> {
1900 let mut result = vec![];
1901
1902 fn clean_crlf(s: &DOMString) -> DOMString {
1904 let mut buf = "".to_owned();
1905 let mut prev = ' ';
1906 for ch in s.str().chars() {
1907 match ch {
1908 '\n' if prev != '\r' => {
1909 buf.push('\r');
1910 buf.push('\n');
1911 },
1912 '\n' => {
1913 buf.push('\n');
1914 },
1915 _ if prev == '\r' => {
1918 buf.push('\r');
1919 buf.push('\n');
1920 buf.push(ch);
1921 },
1922 _ => buf.push(ch),
1923 };
1924 prev = ch;
1925 }
1926 if prev == '\r' {
1928 buf.push('\n');
1929 }
1930 DOMString::from(buf)
1931 }
1932
1933 for entry in form_data.iter_mut() {
1934 entry.name = clean_crlf(&entry.name);
1936
1937 if let FormDatumValue::String(ref s) = entry.value {
1940 entry.value = FormDatumValue::String(clean_crlf(s));
1941 }
1942
1943 let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
1948 result.append(&mut boundary_bytes);
1949
1950 match entry.value {
1953 FormDatumValue::String(ref s) => {
1954 let content_disposition = format!("form-data; name=\"{}\"", entry.name);
1955 let mut bytes =
1956 format!("Content-Disposition: {}\r\n\r\n{}", content_disposition, s)
1957 .into_bytes();
1958 result.append(&mut bytes);
1959 },
1960 FormDatumValue::File(ref f) => {
1961 let charset = encoding.name();
1962 let extra = if charset.to_lowercase() == "utf-8" {
1963 format!("filename=\"{}\"", String::from(f.name().str()))
1964 } else {
1965 format!(
1966 "filename*=\"{}\"''{}",
1967 charset,
1968 http_percent_encode(&f.name().as_bytes())
1969 )
1970 };
1971
1972 let content_disposition = format!("form-data; name=\"{}\"; {}", entry.name, extra);
1973 let content_type: Mime = f
1975 .upcast::<Blob>()
1976 .Type()
1977 .parse()
1978 .unwrap_or(mime::TEXT_PLAIN);
1979 let mut type_bytes = format!(
1980 "Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n",
1981 content_disposition, content_type
1982 )
1983 .into_bytes();
1984 result.append(&mut type_bytes);
1985
1986 let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
1987
1988 result.append(&mut bytes);
1989 },
1990 }
1991 }
1992
1993 let mut boundary_bytes = format!("\r\n--{}--\r\n", boundary).into_bytes();
1994 result.append(&mut boundary_bytes);
1995
1996 result
1997}
1998
1999pub(crate) fn generate_boundary() -> String {
2001 let i1 = random::<u32>();
2002 let i2 = random::<u32>();
2003
2004 format!("---------------------------{0}{1}", i1, i2)
2005}