1use std::borrow::Cow;
6use std::cell::Cell;
7use std::cmp::Ordering;
8use std::ops::Range;
9use std::path::PathBuf;
10use std::ptr::NonNull;
11use std::str::FromStr;
12use std::{f64, ptr};
13
14use base::IpcSend;
15use dom_struct::dom_struct;
16use embedder_traits::{
17 FilterPattern, FormControlRequest as EmbedderFormControl, InputMethodType, RgbColor,
18};
19use encoding_rs::Encoding;
20use euclid::{Point2D, Rect, Size2D};
21use html5ever::{LocalName, Prefix, QualName, local_name, ns};
22use itertools::Itertools;
23use js::jsapi::{
24 ClippedTime, DateGetMsecSinceEpoch, Handle, JS_ClearPendingException, JSObject, NewDateObject,
25 NewUCRegExpObject, ObjectIsDate, RegExpFlag_UnicodeSets, RegExpFlags,
26};
27use js::jsval::UndefinedValue;
28use js::rust::wrappers::{CheckRegExpSyntax, ExecuteRegExpNoStatics, ObjectIsRegExp};
29use js::rust::{HandleObject, MutableHandleObject};
30use net_traits::CoreResourceMsg;
31use net_traits::blob_url_store::get_blob_origin;
32use net_traits::filemanager_thread::{FileManagerResult, FileManagerThreadMsg};
33use script_bindings::codegen::GenericBindings::CharacterDataBinding::CharacterDataMethods;
34use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
35use servo_config::pref;
36use style::attr::AttrValue;
37use style::selector_parser::PseudoElement;
38use style::str::split_commas;
39use stylo_atoms::Atom;
40use stylo_dom::ElementState;
41use time::{Month, OffsetDateTime, Time};
42use unicode_bidi::{BidiClass, bidi_class};
43use url::Url;
44use webrender_api::units::DeviceIntRect;
45
46use crate::clipboard_provider::EmbedderClipboardProvider;
47use crate::dom::activation::Activatable;
48use crate::dom::attr::Attr;
49use crate::dom::bindings::cell::{DomRefCell, Ref};
50use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
51use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
52use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
53use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
54use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
55use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
56use crate::dom::bindings::error::{Error, ErrorResult};
57use crate::dom::bindings::inheritance::Castable;
58use crate::dom::bindings::reflector::DomGlobal;
59use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
60use crate::dom::bindings::str::{DOMString, FromInputValueString, ToInputValueString, USVString};
61use crate::dom::clipboardevent::ClipboardEvent;
62use crate::dom::compositionevent::CompositionEvent;
63use crate::dom::document::Document;
64use crate::dom::document_embedder_controls::ControlElement;
65use crate::dom::element::{
66 AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers,
67};
68use crate::dom::event::{Event, EventBubbles, EventCancelable};
69use crate::dom::eventtarget::EventTarget;
70use crate::dom::file::File;
71use crate::dom::filelist::{FileList, LayoutFileListHelpers};
72use crate::dom::globalscope::GlobalScope;
73use crate::dom::html::htmldatalistelement::HTMLDataListElement;
74use crate::dom::html::htmlelement::HTMLElement;
75use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
76use crate::dom::html::htmlformelement::{
77 FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, ResetFrom,
78 SubmittedFrom,
79};
80use crate::dom::keyboardevent::KeyboardEvent;
81use crate::dom::mouseevent::MouseEvent;
82use crate::dom::node::{
83 BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext,
84};
85use crate::dom::nodelist::NodeList;
86use crate::dom::shadowroot::ShadowRoot;
87use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
88use crate::dom::types::CharacterData;
89use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
90use crate::dom::validitystate::{ValidationFlags, ValidityState};
91use crate::dom::virtualmethods::VirtualMethods;
92use crate::realms::enter_realm;
93use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
94use crate::textinput::KeyReaction::{
95 DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction,
96};
97use crate::textinput::Lines::Single;
98use crate::textinput::{
99 ClipboardEventReaction, Direction, SelectionDirection, TextInput, UTF8Bytes, UTF16CodeUnits,
100};
101
102const DEFAULT_SUBMIT_VALUE: &str = "Submit";
103const DEFAULT_RESET_VALUE: &str = "Reset";
104const PASSWORD_REPLACEMENT_CHAR: char = '●';
105const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen";
106
107#[derive(Clone, JSTraceable, MallocSizeOf)]
108#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
109struct InputTypeTextShadowTree {
129 inner_container: Dom<Element>,
130 text_container: Dom<Element>,
131 placeholder_container: DomRefCell<Option<Dom<Element>>>,
132}
133
134impl InputTypeTextShadowTree {
135 fn init_placeholder_container_if_necessary(&self, host: &HTMLInputElement, can_gc: CanGc) {
138 if self.placeholder_container.borrow().is_some() || host.placeholder.borrow().is_empty() {
141 return;
142 }
143
144 *self.placeholder_container.borrow_mut() = Some(
145 create_ua_widget_div_with_text_node(
146 &host.owner_document(),
147 self.inner_container.upcast::<Node>(),
148 PseudoElement::Placeholder,
149 true,
150 can_gc,
151 )
152 .as_traced(),
153 );
154 }
155}
156
157#[derive(Clone, JSTraceable, MallocSizeOf)]
158#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
159struct InputTypeColorShadowTree {
164 color_value: Dom<Element>,
165}
166
167#[derive(Clone, JSTraceable, MallocSizeOf)]
168#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
169#[non_exhaustive]
170enum ShadowTree {
171 Text(InputTypeTextShadowTree),
172 Color(InputTypeColorShadowTree),
173 }
175
176fn create_ua_widget_div_with_text_node(
179 document: &Document,
180 parent: &Node,
181 implemented_pseudo: PseudoElement,
182 as_first_child: bool,
183 can_gc: CanGc,
184) -> DomRoot<Element> {
185 let el = Element::create(
186 QualName::new(None, ns!(html), local_name!("div")),
187 None,
188 document,
189 ElementCreator::ScriptCreated,
190 CustomElementCreationMode::Asynchronous,
191 None,
192 can_gc,
193 );
194
195 parent
196 .upcast::<Node>()
197 .AppendChild(el.upcast::<Node>(), can_gc)
198 .unwrap();
199 el.upcast::<Node>()
200 .set_implemented_pseudo_element(implemented_pseudo);
201 let text_node = document.CreateTextNode("".into(), can_gc);
202
203 if !as_first_child {
204 el.upcast::<Node>()
205 .AppendChild(text_node.upcast::<Node>(), can_gc)
206 .unwrap();
207 } else {
208 el.upcast::<Node>()
209 .InsertBefore(
210 text_node.upcast::<Node>(),
211 el.upcast::<Node>().GetFirstChild().as_deref(),
212 can_gc,
213 )
214 .unwrap();
215 }
216 el
217}
218
219#[derive(Clone, Copy, Debug, Default, JSTraceable, PartialEq)]
221#[allow(dead_code)]
222#[derive(MallocSizeOf)]
223pub(crate) enum InputType {
224 Button,
226
227 Checkbox,
229
230 Color,
232
233 Date,
235
236 DatetimeLocal,
238
239 Email,
241
242 File,
244
245 Hidden,
247
248 Image,
250
251 Month,
253
254 Number,
256
257 Password,
259
260 Radio,
262
263 Range,
265
266 Reset,
268
269 Search,
271
272 Submit,
274
275 Tel,
277
278 #[default]
280 Text,
281
282 Time,
284
285 Url,
287
288 Week,
290}
291
292impl InputType {
293 pub(crate) fn is_textual(&self) -> bool {
298 matches!(
299 *self,
300 InputType::Date |
301 InputType::DatetimeLocal |
302 InputType::Email |
303 InputType::Hidden |
304 InputType::Month |
305 InputType::Number |
306 InputType::Range |
307 InputType::Search |
308 InputType::Tel |
309 InputType::Text |
310 InputType::Time |
311 InputType::Url |
312 InputType::Week
313 )
314 }
315
316 fn is_textual_or_password(&self) -> bool {
317 self.is_textual() || *self == InputType::Password
318 }
319
320 fn has_periodic_domain(&self) -> bool {
322 *self == InputType::Time
323 }
324
325 fn as_str(&self) -> &str {
326 match *self {
327 InputType::Button => "button",
328 InputType::Checkbox => "checkbox",
329 InputType::Color => "color",
330 InputType::Date => "date",
331 InputType::DatetimeLocal => "datetime-local",
332 InputType::Email => "email",
333 InputType::File => "file",
334 InputType::Hidden => "hidden",
335 InputType::Image => "image",
336 InputType::Month => "month",
337 InputType::Number => "number",
338 InputType::Password => "password",
339 InputType::Radio => "radio",
340 InputType::Range => "range",
341 InputType::Reset => "reset",
342 InputType::Search => "search",
343 InputType::Submit => "submit",
344 InputType::Tel => "tel",
345 InputType::Text => "text",
346 InputType::Time => "time",
347 InputType::Url => "url",
348 InputType::Week => "week",
349 }
350 }
351
352 pub(crate) fn as_ime_type(&self) -> Option<InputMethodType> {
353 match *self {
354 InputType::Color => Some(InputMethodType::Color),
355 InputType::Date => Some(InputMethodType::Date),
356 InputType::DatetimeLocal => Some(InputMethodType::DatetimeLocal),
357 InputType::Email => Some(InputMethodType::Email),
358 InputType::Month => Some(InputMethodType::Month),
359 InputType::Number => Some(InputMethodType::Number),
360 InputType::Password => Some(InputMethodType::Password),
361 InputType::Search => Some(InputMethodType::Search),
362 InputType::Tel => Some(InputMethodType::Tel),
363 InputType::Text => Some(InputMethodType::Text),
364 InputType::Time => Some(InputMethodType::Time),
365 InputType::Url => Some(InputMethodType::Url),
366 InputType::Week => Some(InputMethodType::Week),
367 _ => None,
368 }
369 }
370}
371
372impl From<&Atom> for InputType {
373 fn from(value: &Atom) -> InputType {
374 match value.to_ascii_lowercase() {
375 atom!("button") => InputType::Button,
376 atom!("checkbox") => InputType::Checkbox,
377 atom!("color") => InputType::Color,
378 atom!("date") => InputType::Date,
379 atom!("datetime-local") => InputType::DatetimeLocal,
380 atom!("email") => InputType::Email,
381 atom!("file") => InputType::File,
382 atom!("hidden") => InputType::Hidden,
383 atom!("image") => InputType::Image,
384 atom!("month") => InputType::Month,
385 atom!("number") => InputType::Number,
386 atom!("password") => InputType::Password,
387 atom!("radio") => InputType::Radio,
388 atom!("range") => InputType::Range,
389 atom!("reset") => InputType::Reset,
390 atom!("search") => InputType::Search,
391 atom!("submit") => InputType::Submit,
392 atom!("tel") => InputType::Tel,
393 atom!("text") => InputType::Text,
394 atom!("time") => InputType::Time,
395 atom!("url") => InputType::Url,
396 atom!("week") => InputType::Week,
397 _ => Self::default(),
398 }
399 }
400}
401
402#[derive(Debug, PartialEq)]
403enum ValueMode {
404 Value,
406
407 Default,
409
410 DefaultOn,
412
413 Filename,
415}
416
417#[derive(Debug, PartialEq)]
418enum StepDirection {
419 Up,
420 Down,
421}
422
423#[dom_struct]
424pub(crate) struct HTMLInputElement {
425 htmlelement: HTMLElement,
426 input_type: Cell<InputType>,
427
428 checked_changed: Cell<bool>,
430 placeholder: DomRefCell<DOMString>,
431 size: Cell<u32>,
432 maxlength: Cell<i32>,
433 minlength: Cell<i32>,
434 #[no_trace]
435 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
436 value_dirty: Cell<bool>,
438 sanitization_flag: Cell<bool>,
442
443 filelist: MutNullableDom<FileList>,
444 form_owner: MutNullableDom<HTMLFormElement>,
445 labels_node_list: MutNullableDom<NodeList>,
446 validity_state: MutNullableDom<ValidityState>,
447 shadow_tree: DomRefCell<Option<ShadowTree>>,
448}
449
450#[derive(JSTraceable)]
451pub(crate) struct InputActivationState {
452 indeterminate: bool,
453 checked: bool,
454 checked_radio: Option<DomRoot<HTMLInputElement>>,
455 old_type: InputType,
457 }
459
460static DEFAULT_INPUT_SIZE: u32 = 20;
461static DEFAULT_MAX_LENGTH: i32 = -1;
462static DEFAULT_MIN_LENGTH: i32 = -1;
463
464#[allow(non_snake_case)]
465impl HTMLInputElement {
466 fn new_inherited(
467 local_name: LocalName,
468 prefix: Option<Prefix>,
469 document: &Document,
470 ) -> HTMLInputElement {
471 let embedder_sender = document
472 .window()
473 .as_global_scope()
474 .script_to_embedder_chan()
475 .clone();
476 HTMLInputElement {
477 htmlelement: HTMLElement::new_inherited_with_state(
478 ElementState::ENABLED | ElementState::READWRITE,
479 local_name,
480 prefix,
481 document,
482 ),
483 input_type: Cell::new(Default::default()),
484 placeholder: DomRefCell::new(DOMString::new()),
485 checked_changed: Cell::new(false),
486 maxlength: Cell::new(DEFAULT_MAX_LENGTH),
487 minlength: Cell::new(DEFAULT_MIN_LENGTH),
488 size: Cell::new(DEFAULT_INPUT_SIZE),
489 textinput: DomRefCell::new(TextInput::new(
490 Single,
491 DOMString::new(),
492 EmbedderClipboardProvider {
493 embedder_sender,
494 webview_id: document.webview_id(),
495 },
496 None,
497 None,
498 SelectionDirection::None,
499 )),
500 value_dirty: Cell::new(false),
501 sanitization_flag: Cell::new(true),
502 filelist: MutNullableDom::new(None),
503 form_owner: Default::default(),
504 labels_node_list: MutNullableDom::new(None),
505 validity_state: Default::default(),
506 shadow_tree: Default::default(),
507 }
508 }
509
510 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
511 pub(crate) fn new(
512 local_name: LocalName,
513 prefix: Option<Prefix>,
514 document: &Document,
515 proto: Option<HandleObject>,
516 can_gc: CanGc,
517 ) -> DomRoot<HTMLInputElement> {
518 Node::reflect_node_with_proto(
519 Box::new(HTMLInputElement::new_inherited(
520 local_name, prefix, document,
521 )),
522 document,
523 proto,
524 can_gc,
525 )
526 }
527
528 pub(crate) fn auto_directionality(&self) -> Option<String> {
529 match self.input_type() {
530 InputType::Text | InputType::Search | InputType::Url | InputType::Email => {
531 let value: String = self.Value().to_string();
532 Some(HTMLInputElement::directionality_from_value(&value))
533 },
534 _ => None,
535 }
536 }
537
538 pub(crate) fn directionality_from_value(value: &str) -> String {
539 if HTMLInputElement::is_first_strong_character_rtl(value) {
540 "rtl".to_owned()
541 } else {
542 "ltr".to_owned()
543 }
544 }
545
546 fn is_first_strong_character_rtl(value: &str) -> bool {
547 for ch in value.chars() {
548 return match bidi_class(ch) {
549 BidiClass::L => false,
550 BidiClass::AL => true,
551 BidiClass::R => true,
552 _ => continue,
553 };
554 }
555 false
556 }
557
558 fn value_mode(&self) -> ValueMode {
561 match self.input_type() {
562 InputType::Submit |
563 InputType::Reset |
564 InputType::Button |
565 InputType::Image |
566 InputType::Hidden => ValueMode::Default,
567
568 InputType::Checkbox | InputType::Radio => ValueMode::DefaultOn,
569
570 InputType::Color |
571 InputType::Date |
572 InputType::DatetimeLocal |
573 InputType::Email |
574 InputType::Month |
575 InputType::Number |
576 InputType::Password |
577 InputType::Range |
578 InputType::Search |
579 InputType::Tel |
580 InputType::Text |
581 InputType::Time |
582 InputType::Url |
583 InputType::Week => ValueMode::Value,
584
585 InputType::File => ValueMode::Filename,
586 }
587 }
588
589 #[inline]
590 pub(crate) fn input_type(&self) -> InputType {
591 self.input_type.get()
592 }
593
594 pub(crate) fn is_nontypeable(&self) -> bool {
596 matches!(
597 self.input_type(),
598 InputType::Button |
599 InputType::Checkbox |
600 InputType::Color |
601 InputType::File |
602 InputType::Hidden |
603 InputType::Image |
604 InputType::Radio |
605 InputType::Range |
606 InputType::Reset |
607 InputType::Submit
608 )
609 }
610
611 #[inline]
612 pub(crate) fn is_submit_button(&self) -> bool {
613 let input_type = self.input_type.get();
614 input_type == InputType::Submit || input_type == InputType::Image
615 }
616
617 pub(crate) fn disable_sanitization(&self) {
618 self.sanitization_flag.set(false);
619 }
620
621 pub(crate) fn enable_sanitization(&self) {
622 self.sanitization_flag.set(true);
623 let mut textinput = self.textinput.borrow_mut();
624 let mut value = textinput.single_line_content().clone();
625 self.sanitize_value(&mut value);
626 textinput.set_content(value);
627 self.upcast::<Node>().dirty(NodeDamage::Other);
628 }
629
630 fn does_minmaxlength_apply(&self) -> bool {
631 matches!(
632 self.input_type(),
633 InputType::Text |
634 InputType::Search |
635 InputType::Url |
636 InputType::Tel |
637 InputType::Email |
638 InputType::Password
639 )
640 }
641
642 fn does_pattern_apply(&self) -> bool {
643 matches!(
644 self.input_type(),
645 InputType::Text |
646 InputType::Search |
647 InputType::Url |
648 InputType::Tel |
649 InputType::Email |
650 InputType::Password
651 )
652 }
653
654 fn does_multiple_apply(&self) -> bool {
655 self.input_type() == InputType::Email
656 }
657
658 fn does_value_as_number_apply(&self) -> bool {
661 matches!(
662 self.input_type(),
663 InputType::Date |
664 InputType::Month |
665 InputType::Week |
666 InputType::Time |
667 InputType::DatetimeLocal |
668 InputType::Number |
669 InputType::Range
670 )
671 }
672
673 fn does_value_as_date_apply(&self) -> bool {
674 matches!(
675 self.input_type(),
676 InputType::Date | InputType::Month | InputType::Week | InputType::Time
677 )
678 }
679
680 fn allowed_value_step(&self) -> Option<f64> {
682 if let Some(attr) = self
683 .upcast::<Element>()
684 .get_attribute(&ns!(), &local_name!("step"))
685 {
686 if let Some(step) =
687 DOMString::from(attr.summarize().value).parse_floating_point_number()
688 {
689 if step > 0.0 {
690 return Some(step * self.step_scale_factor());
691 }
692 }
693 }
694 self.default_step()
695 .map(|step| step * self.step_scale_factor())
696 }
697
698 fn minimum(&self) -> Option<f64> {
700 if let Some(attr) = self
701 .upcast::<Element>()
702 .get_attribute(&ns!(), &local_name!("min"))
703 {
704 if let Some(min) =
705 self.convert_string_to_number(&DOMString::from(attr.summarize().value))
706 {
707 return Some(min);
708 }
709 }
710 self.default_minimum()
711 }
712
713 fn maximum(&self) -> Option<f64> {
715 if let Some(attr) = self
716 .upcast::<Element>()
717 .get_attribute(&ns!(), &local_name!("max"))
718 {
719 if let Some(max) =
720 self.convert_string_to_number(&DOMString::from(attr.summarize().value))
721 {
722 return Some(max);
723 }
724 }
725 self.default_maximum()
726 }
727
728 fn stepped_minimum(&self) -> Option<f64> {
731 match (self.minimum(), self.allowed_value_step()) {
732 (Some(min), Some(allowed_step)) => {
733 let step_base = self.step_base();
734 let nsteps = (min - step_base) / allowed_step;
736 Some(step_base + (allowed_step * nsteps.ceil()))
738 },
739 (_, _) => None,
740 }
741 }
742
743 fn stepped_maximum(&self) -> Option<f64> {
746 match (self.maximum(), self.allowed_value_step()) {
747 (Some(max), Some(allowed_step)) => {
748 let step_base = self.step_base();
749 let nsteps = (max - step_base) / allowed_step;
751 Some(step_base + (allowed_step * nsteps.floor()))
753 },
754 (_, _) => None,
755 }
756 }
757
758 fn default_minimum(&self) -> Option<f64> {
760 match self.input_type() {
761 InputType::Range => Some(0.0),
762 _ => None,
763 }
764 }
765
766 fn default_maximum(&self) -> Option<f64> {
768 match self.input_type() {
769 InputType::Range => Some(100.0),
770 _ => None,
771 }
772 }
773
774 fn default_range_value(&self) -> f64 {
776 let min = self.minimum().unwrap_or(0.0);
777 let max = self.maximum().unwrap_or(100.0);
778 if max < min {
779 min
780 } else {
781 min + (max - min) * 0.5
782 }
783 }
784
785 fn default_step(&self) -> Option<f64> {
787 match self.input_type() {
788 InputType::Date => Some(1.0),
789 InputType::Month => Some(1.0),
790 InputType::Week => Some(1.0),
791 InputType::Time => Some(60.0),
792 InputType::DatetimeLocal => Some(60.0),
793 InputType::Number => Some(1.0),
794 InputType::Range => Some(1.0),
795 _ => None,
796 }
797 }
798
799 fn step_scale_factor(&self) -> f64 {
801 match self.input_type() {
802 InputType::Date => 86400000.0,
803 InputType::Month => 1.0,
804 InputType::Week => 604800000.0,
805 InputType::Time => 1000.0,
806 InputType::DatetimeLocal => 1000.0,
807 InputType::Number => 1.0,
808 InputType::Range => 1.0,
809 _ => unreachable!(),
810 }
811 }
812
813 fn step_base(&self) -> f64 {
815 if let Some(attr) = self
816 .upcast::<Element>()
817 .get_attribute(&ns!(), &local_name!("min"))
818 {
819 let minstr = &DOMString::from(attr.summarize().value);
820 if let Some(min) = self.convert_string_to_number(minstr) {
821 return min;
822 }
823 }
824 if let Some(attr) = self
825 .upcast::<Element>()
826 .get_attribute(&ns!(), &local_name!("value"))
827 {
828 if let Some(value) =
829 self.convert_string_to_number(&DOMString::from(attr.summarize().value))
830 {
831 return value;
832 }
833 }
834 self.default_step_base().unwrap_or(0.0)
835 }
836
837 fn default_step_base(&self) -> Option<f64> {
839 match self.input_type() {
840 InputType::Week => Some(-259200000.0),
841 _ => None,
842 }
843 }
844
845 fn step_up_or_down(&self, n: i32, dir: StepDirection, can_gc: CanGc) -> ErrorResult {
848 if !self.does_value_as_number_apply() {
850 return Err(Error::InvalidState(None));
851 }
852 let step_base = self.step_base();
853 let allowed_value_step = match self.allowed_value_step() {
855 Some(avs) => avs,
856 None => return Err(Error::InvalidState(None)),
857 };
858 let minimum = self.minimum();
859 let maximum = self.maximum();
860 if let (Some(min), Some(max)) = (minimum, maximum) {
861 if min > max {
863 return Ok(());
864 }
865 if let Some(smin) = self.stepped_minimum() {
867 if smin > max {
868 return Ok(());
869 }
870 }
871 }
872 let mut value: f64 = self.convert_string_to_number(&self.Value()).unwrap_or(0.0);
874
875 let valueBeforeStepping = value;
877
878 if (value - step_base) % allowed_value_step != 0.0 {
880 value = match dir {
881 StepDirection::Down =>
882 {
884 let intervals_from_base = ((value - step_base) / allowed_value_step).floor();
885 intervals_from_base * allowed_value_step + step_base
886 },
887 StepDirection::Up =>
888 {
890 let intervals_from_base = ((value - step_base) / allowed_value_step).ceil();
891 intervals_from_base * allowed_value_step + step_base
892 },
893 };
894 } else {
895 value += match dir {
896 StepDirection::Down => -f64::from(n) * allowed_value_step,
897 StepDirection::Up => f64::from(n) * allowed_value_step,
898 };
899 }
900
901 if let Some(min) = minimum {
903 if value < min {
904 value = self.stepped_minimum().unwrap_or(value);
905 }
906 }
907
908 if let Some(max) = maximum {
910 if value > max {
911 value = self.stepped_maximum().unwrap_or(value);
912 }
913 }
914
915 match dir {
917 StepDirection::Down => {
918 if value > valueBeforeStepping {
919 return Ok(());
920 }
921 },
922 StepDirection::Up => {
923 if value < valueBeforeStepping {
924 return Ok(());
925 }
926 },
927 }
928
929 self.SetValueAsNumber(value, can_gc)
931 }
932
933 fn suggestions_source_element(&self) -> Option<DomRoot<HTMLDataListElement>> {
935 let list_string = self
936 .upcast::<Element>()
937 .get_string_attribute(&local_name!("list"));
938 if list_string.is_empty() {
939 return None;
940 }
941 let ancestor = self
942 .upcast::<Node>()
943 .GetRootNode(&GetRootNodeOptions::empty());
944 let first_with_id = &ancestor
945 .traverse_preorder(ShadowIncluding::No)
946 .find(|node| {
947 node.downcast::<Element>()
948 .is_some_and(|e| e.Id() == list_string)
949 });
950 first_with_id
951 .as_ref()
952 .and_then(|el| el.downcast::<HTMLDataListElement>())
953 .map(DomRoot::from_ref)
954 }
955
956 fn suffers_from_being_missing(&self, value: &DOMString) -> bool {
958 match self.input_type() {
959 InputType::Checkbox => self.Required() && !self.Checked(),
961 InputType::Radio => {
963 if self.radio_group_name().is_none() {
964 return false;
965 }
966 let mut is_required = self.Required();
967 let mut is_checked = self.Checked();
968 let root = self
969 .upcast::<Node>()
970 .GetRootNode(&GetRootNodeOptions::empty());
971 let form = self.form_owner();
972 for other in radio_group_iter(
973 self,
974 self.radio_group_name().as_ref(),
975 form.as_deref(),
976 &root,
977 ) {
978 is_required = is_required || other.Required();
979 is_checked = is_checked || other.Checked();
980 }
981 is_required && !is_checked
982 },
983 InputType::File => {
985 self.Required() && self.filelist.get().is_none_or(|files| files.Length() == 0)
986 },
987 _ => {
989 self.Required() &&
990 self.value_mode() == ValueMode::Value &&
991 self.is_mutable() &&
992 value.is_empty()
993 },
994 }
995 }
996
997 fn suffers_from_type_mismatch(&self, value: &DOMString) -> bool {
999 if value.is_empty() {
1000 return false;
1001 }
1002
1003 match self.input_type() {
1004 InputType::Url => Url::parse(&value.str()).is_err(),
1006 InputType::Email => {
1009 if self.Multiple() {
1010 !split_commas(&value.str()).all(|string| string.is_valid_email_address_string())
1011 } else {
1012 !value.str().is_valid_email_address_string()
1013 }
1014 },
1015 _ => false,
1017 }
1018 }
1019
1020 fn suffers_from_pattern_mismatch(&self, value: &DOMString) -> bool {
1022 let pattern_str = self.Pattern();
1025 if value.is_empty() || pattern_str.is_empty() || !self.does_pattern_apply() {
1026 return false;
1027 }
1028
1029 let cx = GlobalScope::get_cx();
1031 let _ac = enter_realm(self);
1032 rooted!(in(*cx) let mut pattern = ptr::null_mut::<JSObject>());
1033
1034 if compile_pattern(cx, &pattern_str.str(), pattern.handle_mut()) {
1035 if self.Multiple() && self.does_multiple_apply() {
1036 !split_commas(&value.str())
1037 .all(|s| matches_js_regex(cx, pattern.handle(), s).unwrap_or(true))
1038 } else {
1039 !matches_js_regex(cx, pattern.handle(), &value.str()).unwrap_or(true)
1040 }
1041 } else {
1042 false
1044 }
1045 }
1046
1047 fn suffers_from_bad_input(&self, value: &DOMString) -> bool {
1049 if value.is_empty() {
1050 return false;
1051 }
1052
1053 match self.input_type() {
1054 InputType::Email => {
1057 false
1061 },
1062 InputType::Date => !value.str().is_valid_date_string(),
1064 InputType::Month => !value.str().is_valid_month_string(),
1066 InputType::Week => !value.str().is_valid_week_string(),
1068 InputType::Time => !value.str().is_valid_time_string(),
1070 InputType::DatetimeLocal => !value.str().is_valid_local_date_time_string(),
1072 InputType::Number | InputType::Range => !value.is_valid_floating_point_number_string(),
1075 InputType::Color => !value.str().is_valid_simple_color_string(),
1077 _ => false,
1079 }
1080 }
1081
1082 fn suffers_from_length_issues(&self, value: &DOMString) -> ValidationFlags {
1085 let value_dirty = self.value_dirty.get();
1088 let textinput = self.textinput.borrow();
1089 let edit_by_user = !textinput.was_last_change_by_set_content();
1090
1091 if value.is_empty() || !value_dirty || !edit_by_user || !self.does_minmaxlength_apply() {
1092 return ValidationFlags::empty();
1093 }
1094
1095 let mut failed_flags = ValidationFlags::empty();
1096 let UTF16CodeUnits(value_len) = textinput.utf16_len();
1097 let min_length = self.MinLength();
1098 let max_length = self.MaxLength();
1099
1100 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
1101 failed_flags.insert(ValidationFlags::TOO_SHORT);
1102 }
1103
1104 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
1105 failed_flags.insert(ValidationFlags::TOO_LONG);
1106 }
1107
1108 failed_flags
1109 }
1110
1111 fn suffers_from_range_issues(&self, value: &DOMString) -> ValidationFlags {
1115 if value.is_empty() || !self.does_value_as_number_apply() {
1116 return ValidationFlags::empty();
1117 }
1118
1119 let Some(value_as_number) = self.convert_string_to_number(value) else {
1120 return ValidationFlags::empty();
1121 };
1122
1123 let mut failed_flags = ValidationFlags::empty();
1124 let min_value = self.minimum();
1125 let max_value = self.maximum();
1126
1127 let has_reversed_range = match (min_value, max_value) {
1129 (Some(min), Some(max)) => self.input_type().has_periodic_domain() && min > max,
1130 _ => false,
1131 };
1132
1133 if has_reversed_range {
1134 if value_as_number > max_value.unwrap() && value_as_number < min_value.unwrap() {
1136 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
1137 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
1138 }
1139 } else {
1140 if let Some(min_value) = min_value {
1142 if value_as_number < min_value {
1143 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
1144 }
1145 }
1146 if let Some(max_value) = max_value {
1148 if value_as_number > max_value {
1149 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
1150 }
1151 }
1152 }
1153
1154 if let Some(step) = self.allowed_value_step() {
1156 let diff = (self.step_base() - value_as_number) % step / value_as_number;
1160 if diff.abs() > 1e-12 {
1161 failed_flags.insert(ValidationFlags::STEP_MISMATCH);
1162 }
1163 }
1164
1165 failed_flags
1166 }
1167
1168 fn shadow_root(&self, can_gc: CanGc) -> DomRoot<ShadowRoot> {
1173 self.upcast::<Element>()
1174 .shadow_root()
1175 .unwrap_or_else(|| self.upcast::<Element>().attach_ua_shadow_root(true, can_gc))
1176 }
1177
1178 fn create_text_shadow_tree(&self, can_gc: CanGc) {
1179 let document = self.owner_document();
1180 let shadow_root = self.shadow_root(can_gc);
1181 Node::replace_all(None, shadow_root.upcast::<Node>(), can_gc);
1182
1183 let inner_container = Element::create(
1184 QualName::new(None, ns!(html), local_name!("div")),
1185 None,
1186 &document,
1187 ElementCreator::ScriptCreated,
1188 CustomElementCreationMode::Asynchronous,
1189 None,
1190 can_gc,
1191 );
1192 shadow_root
1193 .upcast::<Node>()
1194 .AppendChild(inner_container.upcast::<Node>(), can_gc)
1195 .unwrap();
1196 inner_container
1197 .upcast::<Node>()
1198 .set_implemented_pseudo_element(PseudoElement::ServoTextControlInnerContainer);
1199
1200 let text_container = create_ua_widget_div_with_text_node(
1201 &document,
1202 inner_container.upcast::<Node>(),
1203 PseudoElement::ServoTextControlInnerEditor,
1204 false,
1205 can_gc,
1206 );
1207
1208 let _ = self
1209 .shadow_tree
1210 .borrow_mut()
1211 .insert(ShadowTree::Text(InputTypeTextShadowTree {
1212 inner_container: inner_container.as_traced(),
1213 text_container: text_container.as_traced(),
1214 placeholder_container: DomRefCell::new(None),
1215 }));
1216 }
1217
1218 fn text_shadow_tree(&self, can_gc: CanGc) -> Ref<'_, InputTypeTextShadowTree> {
1219 let has_text_shadow_tree = self
1220 .shadow_tree
1221 .borrow()
1222 .as_ref()
1223 .is_some_and(|shadow_tree| matches!(shadow_tree, ShadowTree::Text(_)));
1224 if !has_text_shadow_tree {
1225 self.create_text_shadow_tree(can_gc);
1226 }
1227
1228 let shadow_tree = self.shadow_tree.borrow();
1229 Ref::filter_map(shadow_tree, |shadow_tree| {
1230 let shadow_tree = shadow_tree.as_ref()?;
1231 match shadow_tree {
1232 ShadowTree::Text(text_tree) => Some(text_tree),
1233 _ => None,
1234 }
1235 })
1236 .ok()
1237 .expect("UA shadow tree was not created")
1238 }
1239
1240 fn create_color_shadow_tree(&self, can_gc: CanGc) {
1241 let document = self.owner_document();
1242 let shadow_root = self.shadow_root(can_gc);
1243 Node::replace_all(None, shadow_root.upcast::<Node>(), can_gc);
1244
1245 let color_value = Element::create(
1246 QualName::new(None, ns!(html), local_name!("div")),
1247 None,
1248 &document,
1249 ElementCreator::ScriptCreated,
1250 CustomElementCreationMode::Asynchronous,
1251 None,
1252 can_gc,
1253 );
1254 shadow_root
1255 .upcast::<Node>()
1256 .AppendChild(color_value.upcast::<Node>(), can_gc)
1257 .unwrap();
1258 color_value
1259 .upcast::<Node>()
1260 .set_implemented_pseudo_element(PseudoElement::ColorSwatch);
1261
1262 let _ = self
1263 .shadow_tree
1264 .borrow_mut()
1265 .insert(ShadowTree::Color(InputTypeColorShadowTree {
1266 color_value: color_value.as_traced(),
1267 }));
1268 }
1269
1270 fn color_shadow_tree(&self, can_gc: CanGc) -> Ref<'_, InputTypeColorShadowTree> {
1277 let has_color_shadow_tree = self
1278 .shadow_tree
1279 .borrow()
1280 .as_ref()
1281 .is_some_and(|shadow_tree| matches!(shadow_tree, ShadowTree::Color(_)));
1282 if !has_color_shadow_tree {
1283 self.create_color_shadow_tree(can_gc);
1284 }
1285
1286 let shadow_tree = self.shadow_tree.borrow();
1287 Ref::filter_map(shadow_tree, |shadow_tree| {
1288 let shadow_tree = shadow_tree.as_ref()?;
1289 match shadow_tree {
1290 ShadowTree::Color(color_tree) => Some(color_tree),
1291 _ => None,
1292 }
1293 })
1294 .ok()
1295 .expect("UA shadow tree was not created")
1296 }
1297
1298 pub(crate) fn is_textual_widget(&self) -> bool {
1303 matches!(
1304 self.input_type(),
1305 InputType::Date |
1306 InputType::DatetimeLocal |
1307 InputType::Email |
1308 InputType::Month |
1309 InputType::Number |
1310 InputType::Password |
1311 InputType::Range |
1312 InputType::Search |
1313 InputType::Tel |
1314 InputType::Text |
1315 InputType::Time |
1316 InputType::Url |
1317 InputType::Week
1318 )
1319 }
1320
1321 fn update_textual_shadow_tree(&self, can_gc: CanGc) {
1325 debug_assert!(self.is_textual_widget());
1327
1328 let text_shadow_tree = self.text_shadow_tree(can_gc);
1329 let value = self.Value();
1330
1331 let value_text = match (value.is_empty(), self.input_type()) {
1340 (false, InputType::Password) => value
1342 .str()
1343 .chars()
1344 .map(|_| PASSWORD_REPLACEMENT_CHAR)
1345 .collect::<String>()
1346 .into(),
1347 (false, _) => value,
1348 (true, _) => "\u{200B}".into(),
1349 };
1350
1351 text_shadow_tree
1353 .text_container
1354 .upcast::<Node>()
1355 .GetFirstChild()
1356 .expect("UA widget text container without child")
1357 .downcast::<CharacterData>()
1358 .expect("First child is not a CharacterData node")
1359 .SetData(value_text);
1360 }
1361
1362 fn update_color_shadow_tree(&self, can_gc: CanGc) {
1363 debug_assert_eq!(self.input_type(), InputType::Color);
1365
1366 let color_shadow_tree = self.color_shadow_tree(can_gc);
1367 let mut value = self.Value();
1368 if value.str().is_valid_simple_color_string() {
1369 value.make_ascii_lowercase();
1370 } else {
1371 value = DOMString::from("#000000");
1372 }
1373 let style = format!("background-color: {value}");
1374 color_shadow_tree.color_value.set_string_attribute(
1375 &local_name!("style"),
1376 style.into(),
1377 can_gc,
1378 );
1379 }
1380
1381 fn update_shadow_tree(&self, can_gc: CanGc) {
1382 match self.input_type() {
1383 _ if self.is_textual_widget() => self.update_textual_shadow_tree(can_gc),
1384 InputType::Color => self.update_color_shadow_tree(can_gc),
1385 _ => {},
1386 }
1387 }
1388}
1389
1390pub(crate) trait LayoutHTMLInputElementHelpers<'dom> {
1391 fn value_for_layout(self) -> Cow<'dom, str>;
1393 fn size_for_layout(self) -> u32;
1394 fn selection_for_layout(self) -> Option<Range<usize>>;
1395}
1396
1397#[allow(unsafe_code)]
1398impl<'dom> LayoutDom<'dom, HTMLInputElement> {
1399 fn get_raw_textinput_value(self) -> DOMString {
1400 unsafe {
1401 self.unsafe_get()
1402 .textinput
1403 .borrow_for_layout()
1404 .get_content()
1405 }
1406 }
1407 fn get_filelist(self) -> Option<LayoutDom<'dom, FileList>> {
1408 unsafe { self.unsafe_get().filelist.get_inner_as_layout() }
1409 }
1410
1411 fn input_type(self) -> InputType {
1412 self.unsafe_get().input_type.get()
1413 }
1414
1415 fn textinput_sorted_selection_offsets_range(self) -> Range<UTF8Bytes> {
1416 unsafe {
1417 self.unsafe_get()
1418 .textinput
1419 .borrow_for_layout()
1420 .sorted_selection_offsets_range()
1421 }
1422 }
1423}
1424
1425impl<'dom> LayoutHTMLInputElementHelpers<'dom> for LayoutDom<'dom, HTMLInputElement> {
1426 fn value_for_layout(self) -> Cow<'dom, str> {
1430 fn get_raw_attr_value<'dom>(
1431 input: LayoutDom<'dom, HTMLInputElement>,
1432 default: &'static str,
1433 ) -> Cow<'dom, str> {
1434 input
1435 .upcast::<Element>()
1436 .get_attr_val_for_layout(&ns!(), &local_name!("value"))
1437 .unwrap_or(default)
1438 .into()
1439 }
1440
1441 match self.input_type() {
1442 InputType::Checkbox | InputType::Radio | InputType::Image | InputType::Hidden => {
1443 "".into()
1444 },
1445 InputType::File => {
1446 let filelist = self.get_filelist();
1447 match filelist {
1448 Some(filelist) => {
1449 let length = filelist.len();
1450 if length == 0 {
1451 DEFAULT_FILE_INPUT_VALUE.into()
1452 } else if length == 1 {
1453 match filelist.file_for_layout(0) {
1454 Some(file) => file.name().to_string().into(),
1455 None => DEFAULT_FILE_INPUT_VALUE.into(),
1456 }
1457 } else {
1458 format!("{} files", length).into()
1459 }
1460 },
1461 None => DEFAULT_FILE_INPUT_VALUE.into(),
1462 }
1463 },
1464 InputType::Button => get_raw_attr_value(self, ""),
1465 InputType::Submit => get_raw_attr_value(self, DEFAULT_SUBMIT_VALUE),
1466 InputType::Reset => get_raw_attr_value(self, DEFAULT_RESET_VALUE),
1467 InputType::Range => "".into(),
1469 _ => {
1470 unreachable!("Input with shadow tree should use internal shadow tree for layout");
1471 },
1472 }
1473 }
1474
1475 fn size_for_layout(self) -> u32 {
1485 self.unsafe_get().size.get()
1486 }
1487
1488 fn selection_for_layout(self) -> Option<Range<usize>> {
1489 if !self.upcast::<Element>().focus_state() {
1490 return None;
1491 }
1492
1493 let sorted_selection_offsets_range = self.textinput_sorted_selection_offsets_range();
1494
1495 match self.input_type() {
1496 InputType::Password => {
1497 let text = self.get_raw_textinput_value();
1498 let sel = UTF8Bytes::unwrap_range(sorted_selection_offsets_range);
1499
1500 let char_start = text.str()[..sel.start].chars().count();
1502 let char_end = char_start + text.str()[sel].chars().count();
1503
1504 let bytes_per_char = PASSWORD_REPLACEMENT_CHAR.len_utf8();
1505 Some(char_start * bytes_per_char..char_end * bytes_per_char)
1506 },
1507 input_type if input_type.is_textual() => {
1508 Some(UTF8Bytes::unwrap_range(sorted_selection_offsets_range))
1509 },
1510 _ => None,
1511 }
1512 }
1513}
1514
1515impl TextControlElement for HTMLInputElement {
1516 fn selection_api_applies(&self) -> bool {
1518 matches!(
1519 self.input_type(),
1520 InputType::Text |
1521 InputType::Search |
1522 InputType::Url |
1523 InputType::Tel |
1524 InputType::Password
1525 )
1526 }
1527
1528 fn has_selectable_text(&self) -> bool {
1536 match self.input_type() {
1537 InputType::Text |
1538 InputType::Search |
1539 InputType::Url |
1540 InputType::Tel |
1541 InputType::Password |
1542 InputType::Email |
1543 InputType::Date |
1544 InputType::Month |
1545 InputType::Week |
1546 InputType::Time |
1547 InputType::DatetimeLocal |
1548 InputType::Number => true,
1549
1550 InputType::Button |
1551 InputType::Checkbox |
1552 InputType::Color |
1553 InputType::File |
1554 InputType::Hidden |
1555 InputType::Image |
1556 InputType::Radio |
1557 InputType::Range |
1558 InputType::Reset |
1559 InputType::Submit => false,
1560 }
1561 }
1562
1563 fn set_dirty_value_flag(&self, value: bool) {
1564 self.value_dirty.set(value)
1565 }
1566}
1567
1568#[allow(non_snake_case)]
1569impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
1570 make_getter!(Accept, "accept");
1572
1573 make_setter!(SetAccept, "accept");
1575
1576 make_getter!(Alt, "alt");
1578
1579 make_setter!(SetAlt, "alt");
1581
1582 make_getter!(DirName, "dirname");
1584
1585 make_setter!(SetDirName, "dirname");
1587
1588 make_bool_getter!(Disabled, "disabled");
1590
1591 make_bool_setter!(SetDisabled, "disabled");
1593
1594 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
1596 self.form_owner()
1597 }
1598
1599 fn GetFiles(&self) -> Option<DomRoot<FileList>> {
1601 self.filelist.get().as_ref().cloned()
1602 }
1603
1604 fn SetFiles(&self, files: Option<&FileList>) {
1606 if self.input_type() == InputType::File && files.is_some() {
1607 self.filelist.set(files);
1608 }
1609 }
1610
1611 make_bool_getter!(DefaultChecked, "checked");
1613
1614 make_bool_setter!(SetDefaultChecked, "checked");
1616
1617 fn Checked(&self) -> bool {
1619 self.upcast::<Element>()
1620 .state()
1621 .contains(ElementState::CHECKED)
1622 }
1623
1624 fn SetChecked(&self, checked: bool) {
1626 self.update_checked_state(checked, true);
1627 self.value_changed(CanGc::note());
1628 }
1629
1630 make_bool_getter!(ReadOnly, "readonly");
1632
1633 make_bool_setter!(SetReadOnly, "readonly");
1635
1636 make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE);
1638
1639 make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
1641
1642 fn Type(&self) -> DOMString {
1644 DOMString::from(self.input_type().as_str())
1645 }
1646
1647 make_atomic_setter!(SetType, "type");
1649
1650 fn Value(&self) -> DOMString {
1652 match self.value_mode() {
1653 ValueMode::Value => self.textinput.borrow().get_content(),
1654 ValueMode::Default => self
1655 .upcast::<Element>()
1656 .get_attribute(&ns!(), &local_name!("value"))
1657 .map_or(DOMString::from(""), |a| {
1658 DOMString::from(a.summarize().value)
1659 }),
1660 ValueMode::DefaultOn => self
1661 .upcast::<Element>()
1662 .get_attribute(&ns!(), &local_name!("value"))
1663 .map_or(DOMString::from("on"), |a| {
1664 DOMString::from(a.summarize().value)
1665 }),
1666 ValueMode::Filename => {
1667 let mut path = DOMString::from("");
1668 match self.filelist.get() {
1669 Some(ref fl) => match fl.Item(0) {
1670 Some(ref f) => {
1671 path.push_str("C:\\fakepath\\");
1672 path.push_str(&f.name().str());
1673 path
1674 },
1675 None => path,
1676 },
1677 None => path,
1678 }
1679 },
1680 }
1681 }
1682
1683 fn SetValue(&self, mut value: DOMString, can_gc: CanGc) -> ErrorResult {
1685 match self.value_mode() {
1686 ValueMode::Value => {
1687 {
1688 self.value_dirty.set(true);
1690
1691 self.sanitize_value(&mut value);
1693
1694 let mut textinput = self.textinput.borrow_mut();
1695
1696 if *textinput.single_line_content() != value {
1698 textinput.set_content(value);
1700
1701 textinput.clear_selection_to_limit(Direction::Forward);
1703 }
1704 }
1705
1706 self.update_placeholder_shown_state();
1710 },
1711 ValueMode::Default | ValueMode::DefaultOn => {
1712 self.upcast::<Element>()
1713 .set_string_attribute(&local_name!("value"), value, can_gc);
1714 },
1715 ValueMode::Filename => {
1716 if value.is_empty() {
1717 let window = self.owner_window();
1718 let fl = FileList::new(&window, vec![], can_gc);
1719 self.filelist.set(Some(&fl));
1720 } else {
1721 return Err(Error::InvalidState(None));
1722 }
1723 },
1724 }
1725
1726 self.value_changed(can_gc);
1727 self.upcast::<Node>().dirty(NodeDamage::Other);
1728 Ok(())
1729 }
1730
1731 make_getter!(DefaultValue, "value");
1733
1734 make_setter!(SetDefaultValue, "value");
1736
1737 make_getter!(Min, "min");
1739
1740 make_setter!(SetMin, "min");
1742
1743 fn GetList(&self) -> Option<DomRoot<HTMLDataListElement>> {
1745 self.suggestions_source_element()
1746 }
1747
1748 #[allow(unsafe_code)]
1750 fn GetValueAsDate(&self, cx: SafeJSContext) -> Option<NonNull<JSObject>> {
1751 self.convert_string_to_naive_datetime(self.Value())
1752 .map(|date_time| unsafe {
1753 let time = ClippedTime {
1754 t: (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64,
1755 };
1756 NonNull::new_unchecked(NewDateObject(*cx, time))
1757 })
1758 }
1759
1760 #[allow(unsafe_code, non_snake_case)]
1762 fn SetValueAsDate(
1763 &self,
1764 cx: SafeJSContext,
1765 value: *mut JSObject,
1766 can_gc: CanGc,
1767 ) -> ErrorResult {
1768 rooted!(in(*cx) let value = value);
1769 if !self.does_value_as_date_apply() {
1770 return Err(Error::InvalidState(None));
1771 }
1772 if value.is_null() {
1773 return self.SetValue(DOMString::from(""), can_gc);
1774 }
1775 let mut msecs: f64 = 0.0;
1776 unsafe {
1780 let mut isDate = false;
1781 if !ObjectIsDate(*cx, Handle::from(value.handle()), &mut isDate) {
1782 return Err(Error::JSFailed);
1783 }
1784 if !isDate {
1785 return Err(Error::Type("Value was not a date".to_string()));
1786 }
1787 if !DateGetMsecSinceEpoch(*cx, Handle::from(value.handle()), &mut msecs) {
1788 return Err(Error::JSFailed);
1789 }
1790 if !msecs.is_finite() {
1791 return self.SetValue(DOMString::from(""), can_gc);
1792 }
1793 }
1794
1795 let Ok(date_time) = OffsetDateTime::from_unix_timestamp_nanos((msecs * 1e6) as i128) else {
1796 return self.SetValue(DOMString::from(""), can_gc);
1797 };
1798 self.SetValue(self.convert_datetime_to_dom_string(date_time), can_gc)
1799 }
1800
1801 fn ValueAsNumber(&self) -> f64 {
1803 self.convert_string_to_number(&self.Value())
1804 .unwrap_or(f64::NAN)
1805 }
1806
1807 fn SetValueAsNumber(&self, value: f64, can_gc: CanGc) -> ErrorResult {
1809 if value.is_infinite() {
1810 Err(Error::Type("value is not finite".to_string()))
1811 } else if !self.does_value_as_number_apply() {
1812 Err(Error::InvalidState(None))
1813 } else if value.is_nan() {
1814 self.SetValue(DOMString::from(""), can_gc)
1815 } else if let Some(converted) = self.convert_number_to_string(value) {
1816 self.SetValue(converted, can_gc)
1817 } else {
1818 self.SetValue(DOMString::from(""), can_gc)
1823 }
1824 }
1825
1826 make_getter!(Name, "name");
1828
1829 make_atomic_setter!(SetName, "name");
1831
1832 make_getter!(Placeholder, "placeholder");
1834
1835 make_setter!(SetPlaceholder, "placeholder");
1837
1838 make_form_action_getter!(FormAction, "formaction");
1840
1841 make_setter!(SetFormAction, "formaction");
1843
1844 make_enumerated_getter!(
1846 FormEnctype,
1847 "formenctype",
1848 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
1849 missing => "",
1850 invalid => "application/x-www-form-urlencoded"
1851 );
1852
1853 make_setter!(SetFormEnctype, "formenctype");
1855
1856 make_enumerated_getter!(
1858 FormMethod,
1859 "formmethod",
1860 "get" | "post" | "dialog",
1861 missing => "get",
1862 invalid => "get"
1863 );
1864
1865 make_setter!(SetFormMethod, "formmethod");
1867
1868 make_getter!(FormTarget, "formtarget");
1870
1871 make_setter!(SetFormTarget, "formtarget");
1873
1874 make_bool_getter!(FormNoValidate, "formnovalidate");
1876
1877 make_bool_setter!(SetFormNoValidate, "formnovalidate");
1879
1880 make_getter!(Max, "max");
1882
1883 make_setter!(SetMax, "max");
1885
1886 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1888
1889 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1891
1892 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
1894
1895 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
1897
1898 make_bool_getter!(Multiple, "multiple");
1900
1901 make_bool_setter!(SetMultiple, "multiple");
1903
1904 make_getter!(Pattern, "pattern");
1906
1907 make_setter!(SetPattern, "pattern");
1909
1910 make_bool_getter!(Required, "required");
1912
1913 make_bool_setter!(SetRequired, "required");
1915
1916 make_url_getter!(Src, "src");
1918
1919 make_url_setter!(SetSrc, "src");
1921
1922 make_getter!(Step, "step");
1924
1925 make_setter!(SetStep, "step");
1927
1928 fn Indeterminate(&self) -> bool {
1930 self.upcast::<Element>()
1931 .state()
1932 .contains(ElementState::INDETERMINATE)
1933 }
1934
1935 fn SetIndeterminate(&self, val: bool) {
1937 self.upcast::<Element>()
1938 .set_state(ElementState::INDETERMINATE, val)
1939 }
1940
1941 fn GetLabels(&self, can_gc: CanGc) -> Option<DomRoot<NodeList>> {
1945 if self.input_type() == InputType::Hidden {
1946 None
1947 } else {
1948 Some(self.labels_node_list.or_init(|| {
1949 NodeList::new_labels_list(
1950 self.upcast::<Node>().owner_doc().window(),
1951 self.upcast::<HTMLElement>(),
1952 can_gc,
1953 )
1954 }))
1955 }
1956 }
1957
1958 fn Select(&self) {
1960 self.selection().dom_select();
1961 }
1962
1963 fn GetSelectionStart(&self) -> Option<u32> {
1965 self.selection().dom_start()
1966 }
1967
1968 fn SetSelectionStart(&self, start: Option<u32>) -> ErrorResult {
1970 self.selection().set_dom_start(start)
1971 }
1972
1973 fn GetSelectionEnd(&self) -> Option<u32> {
1975 self.selection().dom_end()
1976 }
1977
1978 fn SetSelectionEnd(&self, end: Option<u32>) -> ErrorResult {
1980 self.selection().set_dom_end(end)
1981 }
1982
1983 fn GetSelectionDirection(&self) -> Option<DOMString> {
1985 self.selection().dom_direction()
1986 }
1987
1988 fn SetSelectionDirection(&self, direction: Option<DOMString>) -> ErrorResult {
1990 self.selection().set_dom_direction(direction)
1991 }
1992
1993 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
1995 self.selection().set_dom_range(start, end, direction)
1996 }
1997
1998 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
2000 self.selection()
2001 .set_dom_range_text(replacement, None, None, Default::default())
2002 }
2003
2004 fn SetRangeText_(
2006 &self,
2007 replacement: DOMString,
2008 start: u32,
2009 end: u32,
2010 selection_mode: SelectionMode,
2011 ) -> ErrorResult {
2012 self.selection()
2013 .set_dom_range_text(replacement, Some(start), Some(end), selection_mode)
2014 }
2015
2016 fn SelectFiles(&self, paths: Vec<DOMString>, can_gc: CanGc) {
2021 if self.input_type() == InputType::File {
2022 let _ = self.select_files(Some(paths), can_gc);
2023 }
2024 }
2025
2026 fn StepUp(&self, n: i32, can_gc: CanGc) -> ErrorResult {
2028 self.step_up_or_down(n, StepDirection::Up, can_gc)
2029 }
2030
2031 fn StepDown(&self, n: i32, can_gc: CanGc) -> ErrorResult {
2033 self.step_up_or_down(n, StepDirection::Down, can_gc)
2034 }
2035
2036 fn WillValidate(&self) -> bool {
2038 self.is_instance_validatable()
2039 }
2040
2041 fn Validity(&self) -> DomRoot<ValidityState> {
2043 self.validity_state()
2044 }
2045
2046 fn CheckValidity(&self, can_gc: CanGc) -> bool {
2048 self.check_validity(can_gc)
2049 }
2050
2051 fn ReportValidity(&self, can_gc: CanGc) -> bool {
2053 self.report_validity(can_gc)
2054 }
2055
2056 fn ValidationMessage(&self) -> DOMString {
2058 self.validation_message()
2059 }
2060
2061 fn SetCustomValidity(&self, error: DOMString) {
2063 self.validity_state().set_custom_error_message(error);
2064 }
2065}
2066
2067fn radio_group_iter<'a>(
2068 elem: &'a HTMLInputElement,
2069 group: Option<&'a Atom>,
2070 form: Option<&'a HTMLFormElement>,
2071 root: &'a Node,
2072) -> impl Iterator<Item = DomRoot<HTMLInputElement>> + 'a {
2073 root.traverse_preorder(ShadowIncluding::No)
2074 .filter_map(DomRoot::downcast::<HTMLInputElement>)
2075 .filter(move |r| &**r == elem || in_same_group(r, form, group, Some(root)))
2076}
2077
2078fn broadcast_radio_checked(broadcaster: &HTMLInputElement, group: Option<&Atom>) {
2079 let root = broadcaster
2080 .upcast::<Node>()
2081 .GetRootNode(&GetRootNodeOptions::empty());
2082 let form = broadcaster.form_owner();
2083 for r in radio_group_iter(broadcaster, group, form.as_deref(), &root) {
2084 if broadcaster != &*r && r.Checked() {
2085 r.SetChecked(false);
2086 }
2087 }
2088}
2089
2090fn perform_radio_group_validation(elem: &HTMLInputElement, group: Option<&Atom>, can_gc: CanGc) {
2091 let root = elem
2092 .upcast::<Node>()
2093 .GetRootNode(&GetRootNodeOptions::empty());
2094 let form = elem.form_owner();
2095 for r in radio_group_iter(elem, group, form.as_deref(), &root) {
2096 r.validity_state()
2097 .perform_validation_and_update(ValidationFlags::all(), can_gc);
2098 }
2099}
2100
2101fn in_same_group(
2103 other: &HTMLInputElement,
2104 owner: Option<&HTMLFormElement>,
2105 group: Option<&Atom>,
2106 tree_root: Option<&Node>,
2107) -> bool {
2108 if group.is_none() {
2109 return false;
2111 }
2112
2113 if other.input_type() != InputType::Radio ||
2114 other.form_owner().as_deref() != owner ||
2115 other.radio_group_name().as_ref() != group
2116 {
2117 return false;
2118 }
2119
2120 match tree_root {
2121 Some(tree_root) => {
2122 let other_root = other
2123 .upcast::<Node>()
2124 .GetRootNode(&GetRootNodeOptions::empty());
2125 tree_root == &*other_root
2126 },
2127 None => {
2128 true
2130 },
2131 }
2132}
2133
2134impl HTMLInputElement {
2135 fn radio_group_updated(&self, group: Option<&Atom>) {
2136 if self.Checked() {
2137 broadcast_radio_checked(self, group);
2138 }
2139 }
2140
2141 pub(crate) fn form_datums(
2144 &self,
2145 submitter: Option<FormSubmitterElement>,
2146 encoding: Option<&'static Encoding>,
2147 ) -> Vec<FormDatum> {
2148 let ty = self.Type();
2152
2153 let name = self.Name();
2155 let is_submitter = match submitter {
2156 Some(FormSubmitterElement::Input(s)) => self == s,
2157 _ => false,
2158 };
2159
2160 match self.input_type() {
2161 InputType::Submit | InputType::Button | InputType::Reset if !is_submitter => {
2163 return vec![];
2164 },
2165
2166 InputType::Radio | InputType::Checkbox => {
2168 if !self.Checked() || name.is_empty() {
2169 return vec![];
2170 }
2171 },
2172
2173 InputType::File => {
2174 let mut datums = vec![];
2175
2176 let name = self.Name();
2178
2179 match self.GetFiles() {
2180 Some(fl) => {
2181 for f in fl.iter_files() {
2182 datums.push(FormDatum {
2183 ty: ty.clone(),
2184 name: name.clone(),
2185 value: FormDatumValue::File(DomRoot::from_ref(f)),
2186 });
2187 }
2188 },
2189 None => {
2190 datums.push(FormDatum {
2191 ty: ty.clone(),
2194 name: name.clone(),
2195 value: FormDatumValue::String(DOMString::from("")),
2196 })
2197 },
2198 }
2199
2200 return datums;
2201 },
2202
2203 InputType::Image => return vec![], InputType::Hidden => {
2207 if name.to_ascii_lowercase() == "_charset_" {
2208 return vec![FormDatum {
2209 ty: ty.clone(),
2210 name,
2211 value: FormDatumValue::String(match encoding {
2212 None => DOMString::from("UTF-8"),
2213 Some(enc) => DOMString::from(enc.name()),
2214 }),
2215 }];
2216 }
2217 },
2218
2219 _ => {
2221 if name.is_empty() {
2222 return vec![];
2223 }
2224 },
2225 }
2226
2227 vec![FormDatum {
2229 ty: ty.clone(),
2230 name,
2231 value: FormDatumValue::String(self.Value()),
2232 }]
2233 }
2234
2235 fn radio_group_name(&self) -> Option<Atom> {
2237 self.upcast::<Element>()
2238 .get_name()
2239 .and_then(|name| if name == atom!("") { None } else { Some(name) })
2240 }
2241
2242 fn update_checked_state(&self, checked: bool, dirty: bool) {
2243 self.upcast::<Element>()
2244 .set_state(ElementState::CHECKED, checked);
2245
2246 if dirty {
2247 self.checked_changed.set(true);
2248 }
2249
2250 if self.input_type() == InputType::Radio && checked {
2251 broadcast_radio_checked(self, self.radio_group_name().as_ref());
2252 }
2253
2254 self.upcast::<Node>().dirty(NodeDamage::Other);
2255 }
2256
2257 pub(crate) fn is_mutable(&self) -> bool {
2259 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
2262 }
2263
2264 pub(crate) fn reset(&self, can_gc: CanGc) {
2266 match self.input_type() {
2267 InputType::Radio | InputType::Checkbox => {
2268 self.update_checked_state(self.DefaultChecked(), false);
2269 self.checked_changed.set(false);
2270 self.value_changed(can_gc);
2271 },
2272 InputType::Image => (),
2273 _ => (),
2274 }
2275 self.textinput.borrow_mut().set_content(self.DefaultValue());
2276 self.value_dirty.set(false);
2277 self.upcast::<Node>().dirty(NodeDamage::Other);
2278 }
2279
2280 pub(crate) fn clear(&self, can_gc: CanGc) {
2283 self.value_dirty.set(false);
2285 self.checked_changed.set(false);
2286 self.textinput.borrow_mut().set_content(DOMString::from(""));
2288 self.update_checked_state(self.DefaultChecked(), false);
2290 self.value_changed(can_gc);
2291 if self.filelist.get().is_some() {
2293 let window = self.owner_window();
2294 let filelist = FileList::new(&window, vec![], can_gc);
2295 self.filelist.set(Some(&filelist));
2296 }
2297 self.enable_sanitization();
2301 self.upcast::<Node>().dirty(NodeDamage::Other);
2302 }
2303
2304 fn update_placeholder_shown_state(&self) {
2305 if !self.input_type().is_textual_or_password() {
2306 return;
2307 }
2308
2309 let has_placeholder = !self.placeholder.borrow().is_empty();
2310 let has_value = !self.textinput.borrow().is_empty();
2311 let el = self.upcast::<Element>();
2312
2313 el.set_placeholder_shown_state(has_placeholder && !has_value);
2314 }
2315
2316 fn update_text_shadow_tree_placeholder(&self, can_gc: CanGc) {
2319 if !self.is_textual_widget() {
2320 return;
2321 }
2322
2323 let text_shadow_tree = self.text_shadow_tree(can_gc);
2324 text_shadow_tree.init_placeholder_container_if_necessary(self, can_gc);
2325
2326 let Some(ref placeholder_container) = *text_shadow_tree.placeholder_container.borrow()
2327 else {
2328 return;
2330 };
2331 let placeholder_text = self.placeholder.borrow().clone();
2332
2333 placeholder_container
2335 .upcast::<Node>()
2336 .GetFirstChild()
2337 .expect("UA widget text container without child")
2338 .downcast::<CharacterData>()
2339 .expect("First child is not a CharacterData node")
2340 .SetData(placeholder_text);
2341 }
2342
2343 pub(crate) fn select_files(
2346 &self,
2347 opt_test_paths: Option<Vec<DOMString>>,
2348 can_gc: CanGc,
2349 ) -> FileManagerResult<()> {
2350 let window = self.owner_window();
2351 let origin = get_blob_origin(&window.get_url());
2352 let resource_threads = window.as_global_scope().resource_threads();
2353
2354 let mut files: Vec<DomRoot<File>> = vec![];
2355
2356 let webview_id = window.webview_id();
2357 let filter = filter_from_accept(&self.Accept());
2358 let target = self.upcast::<EventTarget>();
2359
2360 if self.Multiple() {
2361 if pref!(dom_testing_html_input_element_select_files_enabled) {
2364 let filelist = self.filelist.get();
2365 if let Some(filelist) = filelist {
2366 for i in 0..filelist.Length() {
2367 files.push(
2368 filelist
2369 .Item(i)
2370 .expect("We should have iterate within filelist length"),
2371 );
2372 }
2373 }
2374 }
2375
2376 let opt_test_paths = opt_test_paths.map(|paths| {
2377 paths
2378 .iter()
2379 .filter_map(|p| PathBuf::from_str(&p.str()).ok())
2380 .collect()
2381 });
2382
2383 let (chan, recv) =
2384 profile_traits::ipc::channel(self.global().time_profiler_chan().clone())
2385 .expect("Error initializing channel");
2386 let msg =
2387 FileManagerThreadMsg::SelectFiles(webview_id, filter, chan, origin, opt_test_paths);
2388 resource_threads
2389 .send(CoreResourceMsg::ToFileManager(msg))
2390 .unwrap();
2391
2392 match recv.recv().expect("IpcSender side error") {
2393 Ok(selected_files) => {
2394 for selected in selected_files {
2395 files.push(File::new_from_selected(&window, selected, can_gc));
2396 }
2397 },
2398 Err(err) => {
2399 debug!("Input multiple file select error: {:?}", err);
2400 return Err(err);
2401 },
2402 };
2403 } else {
2404 let opt_test_path = match opt_test_paths {
2405 Some(paths) => {
2406 if paths.is_empty() {
2407 return Ok(());
2408 } else {
2409 Some(PathBuf::from(paths[0].to_string())) }
2411 },
2412 None => None,
2413 };
2414
2415 let (chan, recv) =
2416 profile_traits::ipc::channel(self.global().time_profiler_chan().clone())
2417 .expect("Error initializing channel");
2418 let msg =
2419 FileManagerThreadMsg::SelectFile(webview_id, filter, chan, origin, opt_test_path);
2420 resource_threads
2421 .send(CoreResourceMsg::ToFileManager(msg))
2422 .unwrap();
2423
2424 match recv.recv().expect("IpcSender side error") {
2425 Ok(selected) => {
2426 files.push(File::new_from_selected(&window, selected, can_gc));
2427 },
2428 Err(err) => {
2429 debug!("Input file select error: {:?}", err);
2430 return Err(err);
2431 },
2432 };
2433 }
2434
2435 let filelist = FileList::new(&window, files, can_gc);
2436 self.filelist.set(Some(&filelist));
2437
2438 target.fire_bubbling_event(atom!("input"), can_gc);
2439 target.fire_bubbling_event(atom!("change"), can_gc);
2440
2441 Ok(())
2442 }
2443
2444 fn sanitize_value(&self, value: &mut DOMString) {
2446 if !self.sanitization_flag.get() {
2451 return;
2452 }
2453 match self.input_type() {
2454 InputType::Text | InputType::Search | InputType::Tel | InputType::Password => {
2455 value.strip_newlines();
2456 },
2457 InputType::Url => {
2458 value.strip_newlines();
2459 value.strip_leading_and_trailing_ascii_whitespace();
2460 },
2461 InputType::Date => {
2462 if !value.str().is_valid_date_string() {
2463 value.clear();
2464 }
2465 },
2466 InputType::Month => {
2467 if !value.str().is_valid_month_string() {
2468 value.clear();
2469 }
2470 },
2471 InputType::Week => {
2472 if !value.str().is_valid_week_string() {
2473 value.clear();
2474 }
2475 },
2476 InputType::Color => {
2477 if value.str().is_valid_simple_color_string() {
2478 value.make_ascii_lowercase();
2479 } else {
2480 *value = "#000000".into();
2481 }
2482 },
2483 InputType::Time => {
2484 if !value.str().is_valid_time_string() {
2485 value.clear();
2486 }
2487 },
2488 InputType::DatetimeLocal => {
2489 let time = value
2490 .str()
2491 .parse_local_date_time_string()
2492 .map(|date_time| date_time.to_local_date_time_string());
2493 match time {
2494 Some(normalized_string) => *value = DOMString::from_string(normalized_string),
2495 None => value.clear(),
2496 }
2497 },
2498 InputType::Number => {
2499 if !value.is_valid_floating_point_number_string() {
2500 value.clear();
2501 }
2502 },
2509 InputType::Range => {
2511 if !value.is_valid_floating_point_number_string() {
2512 *value = DOMString::from(self.default_range_value().to_string());
2513 }
2514 if let Ok(fval) = &value.parse::<f64>() {
2515 let mut fval = *fval;
2516 if let Some(max) = self.maximum() {
2519 if fval > max {
2520 fval = max;
2521 }
2522 }
2523 if let Some(min) = self.minimum() {
2524 if fval < min {
2525 fval = min;
2526 }
2527 }
2528 if let Some(allowed_value_step) = self.allowed_value_step() {
2533 let step_base = self.step_base();
2534 let steps_from_base = (fval - step_base) / allowed_value_step;
2535 if steps_from_base.fract() != 0.0 {
2536 let int_steps = round_halves_positive(steps_from_base);
2539 fval = int_steps * allowed_value_step + step_base;
2541
2542 if let Some(stepped_maximum) = self.stepped_maximum() {
2546 if fval > stepped_maximum {
2547 fval = stepped_maximum;
2548 }
2549 }
2550 if let Some(stepped_minimum) = self.stepped_minimum() {
2551 if fval < stepped_minimum {
2552 fval = stepped_minimum;
2553 }
2554 }
2555 }
2556 }
2557 *value = DOMString::from(fval.to_string());
2558 };
2559 },
2560 InputType::Email => {
2561 if !self.Multiple() {
2562 value.strip_newlines();
2563 value.strip_leading_and_trailing_ascii_whitespace();
2564 } else {
2565 let sanitized = split_commas(&value.str())
2566 .map(|token| {
2567 let mut token = DOMString::from(token.to_string());
2568 token.strip_newlines();
2569 token.strip_leading_and_trailing_ascii_whitespace();
2570 token
2571 })
2572 .join(",");
2573 value.clear();
2574 value.push_str(sanitized.as_str());
2575 }
2576 },
2577 InputType::Button |
2580 InputType::Checkbox |
2581 InputType::File |
2582 InputType::Hidden |
2583 InputType::Image |
2584 InputType::Radio |
2585 InputType::Reset |
2586 InputType::Submit => (),
2587 }
2588 }
2589
2590 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
2591 fn selection(&self) -> TextControlSelection<'_, Self> {
2592 TextControlSelection::new(self, &self.textinput)
2593 }
2594
2595 #[allow(unsafe_code)]
2597 fn implicit_submission(&self, can_gc: CanGc) {
2598 let doc = self.owner_document();
2599 let node = doc.upcast::<Node>();
2600 let owner = self.form_owner();
2601 let form = match owner {
2602 None => return,
2603 Some(ref f) => f,
2604 };
2605
2606 if self.upcast::<Element>().click_in_progress() {
2607 return;
2608 }
2609 let submit_button = node
2610 .query_selector_iter(DOMString::from("input[type=submit]"))
2611 .unwrap()
2612 .filter_map(DomRoot::downcast::<HTMLInputElement>)
2613 .find(|r| r.form_owner() == owner);
2614 match submit_button {
2615 Some(ref button) => {
2616 if button.is_instance_activatable() {
2617 button
2620 .upcast::<Node>()
2621 .fire_synthetic_pointer_event_not_trusted(DOMString::from("click"), can_gc);
2622 }
2623 },
2624 None => {
2625 let mut inputs = node
2626 .query_selector_iter(DOMString::from("input"))
2627 .unwrap()
2628 .filter_map(DomRoot::downcast::<HTMLInputElement>)
2629 .filter(|input| {
2630 input.form_owner() == owner &&
2631 matches!(
2632 input.input_type(),
2633 InputType::Text |
2634 InputType::Search |
2635 InputType::Url |
2636 InputType::Tel |
2637 InputType::Email |
2638 InputType::Password |
2639 InputType::Date |
2640 InputType::Month |
2641 InputType::Week |
2642 InputType::Time |
2643 InputType::DatetimeLocal |
2644 InputType::Number
2645 )
2646 });
2647
2648 if inputs.nth(1).is_some() {
2649 return;
2651 }
2652 form.submit(
2653 SubmittedFrom::NotFromForm,
2654 FormSubmitterElement::Form(form),
2655 can_gc,
2656 );
2657 },
2658 }
2659 }
2660
2661 fn convert_string_to_number(&self, value: &DOMString) -> Option<f64> {
2663 match self.input_type() {
2664 InputType::Date => value.str().parse_date_string().map(|date_time| {
2671 (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64
2672 }),
2673 InputType::Month => value.str().parse_month_string().map(|date_time| {
2682 ((date_time.year() - 1970) * 12) as f64 + (date_time.month() as u8 - 1) as f64
2683 }),
2684 InputType::Week => value.str().parse_week_string().map(|date_time| {
2691 (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64
2692 }),
2693 InputType::Time => value
2698 .str()
2699 .parse_time_string()
2700 .map(|date_time| (date_time.time() - Time::MIDNIGHT).whole_milliseconds() as f64),
2701 InputType::DatetimeLocal => {
2708 value.str().parse_local_date_time_string().map(|date_time| {
2709 (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64
2710 })
2711 },
2712 InputType::Number | InputType::Range => value.parse_floating_point_number(),
2713 _ => None,
2716 }
2717 }
2718
2719 fn convert_number_to_string(&self, value: f64) -> Option<DOMString> {
2721 match self.input_type() {
2722 InputType::Date | InputType::Week | InputType::Time | InputType::DatetimeLocal => {
2723 OffsetDateTime::from_unix_timestamp_nanos((value * 1e6) as i128)
2724 .ok()
2725 .map(|value| self.convert_datetime_to_dom_string(value))
2726 },
2727 InputType::Month => {
2728 let date = OffsetDateTime::UNIX_EPOCH;
2732 let years = (value / 12.) as i32;
2733 let year = date.year() + years;
2734
2735 let months = value as i32 - (years * 12);
2736 let months = match months.cmp(&0) {
2737 Ordering::Less => (12 - months) as u8,
2738 Ordering::Equal | Ordering::Greater => months as u8,
2739 } + 1;
2740
2741 let date = date
2742 .replace_year(year)
2743 .ok()?
2744 .replace_month(Month::try_from(months).ok()?)
2745 .ok()?;
2746 Some(self.convert_datetime_to_dom_string(date))
2747 },
2748 InputType::Number | InputType::Range => {
2749 let mut value = DOMString::from(value.to_string());
2750 value.set_best_representation_of_the_floating_point_number();
2751 Some(value)
2752 },
2753 _ => unreachable!("Should not have called convert_number_to_string for non-Date types"),
2754 }
2755 }
2756
2757 fn convert_string_to_naive_datetime(&self, value: DOMString) -> Option<OffsetDateTime> {
2761 match self.input_type() {
2762 InputType::Date => value.str().parse_date_string(),
2763 InputType::Time => value.str().parse_time_string(),
2764 InputType::Week => value.str().parse_week_string(),
2765 InputType::Month => value.str().parse_month_string(),
2766 InputType::DatetimeLocal => value.str().parse_local_date_time_string(),
2767 _ => None,
2769 }
2770 }
2771
2772 fn convert_datetime_to_dom_string(&self, value: OffsetDateTime) -> DOMString {
2776 DOMString::from_string(match self.input_type() {
2777 InputType::Date => value.to_date_string(),
2778 InputType::Month => value.to_month_string(),
2779 InputType::Week => value.to_week_string(),
2780 InputType::Time => value.to_time_string(),
2781 InputType::DatetimeLocal => value.to_local_date_time_string(),
2782 _ => {
2783 unreachable!("Should not have called convert_datetime_to_string for non-Date types")
2784 },
2785 })
2786 }
2787
2788 fn update_related_validity_states(&self, can_gc: CanGc) {
2789 match self.input_type() {
2790 InputType::Radio => {
2791 perform_radio_group_validation(self, self.radio_group_name().as_ref(), can_gc)
2792 },
2793 _ => {
2794 self.validity_state()
2795 .perform_validation_and_update(ValidationFlags::all(), can_gc);
2796 },
2797 }
2798 }
2799
2800 fn value_changed(&self, can_gc: CanGc) {
2801 self.update_related_validity_states(can_gc);
2802 self.update_shadow_tree(can_gc);
2803 }
2804
2805 fn show_the_picker_if_applicable(&self) {
2807 if !self.is_mutable() {
2811 return;
2812 }
2813
2814 if self.input_type() == InputType::Color {
2817 let document = self.owner_document();
2818 let rect = self.upcast::<Node>().border_box().unwrap_or_default();
2819 let rect = Rect::new(
2820 Point2D::new(rect.origin.x.to_px(), rect.origin.y.to_px()),
2821 Size2D::new(rect.size.width.to_px(), rect.size.height.to_px()),
2822 );
2823 let current_value = self.Value();
2824 let current_color = RgbColor {
2825 red: u8::from_str_radix(¤t_value.str()[1..3], 16).unwrap(),
2826 green: u8::from_str_radix(¤t_value.str()[3..5], 16).unwrap(),
2827 blue: u8::from_str_radix(¤t_value.str()[5..7], 16).unwrap(),
2828 };
2829 document.embedder_controls().show_form_control(
2830 ControlElement::ColorInput(DomRoot::from_ref(self)),
2831 DeviceIntRect::from_untyped(&rect.to_box2d()),
2832 EmbedderFormControl::ColorPicker(current_color),
2833 );
2834 }
2835 }
2836
2837 pub(crate) fn handle_color_picker_response(&self, response: Option<RgbColor>, can_gc: CanGc) {
2838 let Some(selected_color) = response else {
2839 return;
2840 };
2841 let formatted_color = format!(
2842 "#{:0>2x}{:0>2x}{:0>2x}",
2843 selected_color.red, selected_color.green, selected_color.blue
2844 );
2845 let _ = self.SetValue(formatted_color.into(), can_gc);
2846 }
2847}
2848
2849impl VirtualMethods for HTMLInputElement {
2850 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2851 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2852 }
2853
2854 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
2855 self.super_type()
2856 .unwrap()
2857 .attribute_mutated(attr, mutation, can_gc);
2858
2859 match *attr.local_name() {
2860 local_name!("disabled") => {
2861 let disabled_state = match mutation {
2862 AttributeMutation::Set(None) => true,
2863 AttributeMutation::Set(Some(_)) => {
2864 return;
2866 },
2867 AttributeMutation::Removed => false,
2868 };
2869 let el = self.upcast::<Element>();
2870 el.set_disabled_state(disabled_state);
2871 el.set_enabled_state(!disabled_state);
2872 el.check_ancestors_disabled_state_for_form_control();
2873
2874 if self.input_type().is_textual() {
2875 let read_write = !(self.ReadOnly() || el.disabled_state());
2876 el.set_read_write_state(read_write);
2877 }
2878
2879 el.update_sequentially_focusable_status(can_gc);
2880 },
2881 local_name!("checked") if !self.checked_changed.get() => {
2882 let checked_state = match mutation {
2883 AttributeMutation::Set(None) => true,
2884 AttributeMutation::Set(Some(_)) => {
2885 return;
2887 },
2888 AttributeMutation::Removed => false,
2889 };
2890 self.update_checked_state(checked_state, false);
2891 },
2892 local_name!("size") => {
2893 let size = mutation.new_value(attr).map(|value| value.as_uint());
2894 self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
2895 },
2896 local_name!("type") => {
2897 let el = self.upcast::<Element>();
2898 match mutation {
2899 AttributeMutation::Set(_) => {
2900 let new_type = InputType::from(attr.value().as_atom());
2901
2902 let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
2904 let previously_selectable = self.selection_api_applies();
2905
2906 self.input_type.set(new_type);
2907
2908 if new_type.is_textual() {
2909 let read_write = !(self.ReadOnly() || el.disabled_state());
2910 el.set_read_write_state(read_write);
2911 } else {
2912 el.set_read_write_state(false);
2913 }
2914
2915 if new_type == InputType::File {
2916 let window = self.owner_window();
2917 let filelist = FileList::new(&window, vec![], can_gc);
2918 self.filelist.set(Some(&filelist));
2919 }
2920
2921 let new_value_mode = self.value_mode();
2922 match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
2923 (&ValueMode::Value, false, ValueMode::Default) |
2925 (&ValueMode::Value, false, ValueMode::DefaultOn) => {
2926 self.SetValue(old_idl_value, can_gc)
2927 .expect("Failed to set input value on type change to a default ValueMode.");
2928 },
2929
2930 (_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
2932 self.SetValue(
2933 self.upcast::<Element>()
2934 .get_attribute(&ns!(), &local_name!("value"))
2935 .map_or(DOMString::from(""), |a| {
2936 DOMString::from(a.summarize().value)
2937 }),
2938 can_gc,
2939 )
2940 .expect(
2941 "Failed to set input value on type change to ValueMode::Value.",
2942 );
2943 self.value_dirty.set(false);
2944 },
2945
2946 (_, _, ValueMode::Filename)
2948 if old_value_mode != ValueMode::Filename =>
2949 {
2950 self.SetValue(DOMString::from(""), can_gc)
2951 .expect("Failed to set input value on type change to ValueMode::Filename.");
2952 },
2953 _ => {},
2954 }
2955
2956 if new_type == InputType::Radio {
2958 self.radio_group_updated(self.radio_group_name().as_ref());
2959 }
2960
2961 let mut textinput = self.textinput.borrow_mut();
2963 let mut value = textinput.single_line_content().clone();
2964 self.sanitize_value(&mut value);
2965 textinput.set_content(value);
2966 self.upcast::<Node>().dirty(NodeDamage::Other);
2967
2968 if !previously_selectable && self.selection_api_applies() {
2970 textinput.clear_selection_to_limit(Direction::Backward);
2971 }
2972 },
2973 AttributeMutation::Removed => {
2974 if self.input_type() == InputType::Radio {
2975 broadcast_radio_checked(self, self.radio_group_name().as_ref());
2976 }
2977 self.input_type.set(InputType::default());
2978 let el = self.upcast::<Element>();
2979
2980 let read_write = !(self.ReadOnly() || el.disabled_state());
2981 el.set_read_write_state(read_write);
2982 },
2983 }
2984
2985 self.update_placeholder_shown_state();
2986 self.update_text_shadow_tree_placeholder(can_gc);
2987 },
2988 local_name!("value") if !self.value_dirty.get() => {
2991 let value = mutation.new_value(attr).map(|value| (**value).to_owned());
2992 let mut value = value.map_or(DOMString::new(), DOMString::from);
2993
2994 self.sanitize_value(&mut value);
2995 self.textinput.borrow_mut().set_content(value);
2996 self.update_placeholder_shown_state();
2997
2998 self.upcast::<Node>().dirty(NodeDamage::Other);
2999 },
3000 local_name!("name") if self.input_type() == InputType::Radio => {
3001 self.radio_group_updated(
3002 mutation.new_value(attr).as_ref().map(|name| name.as_atom()),
3003 );
3004 },
3005 local_name!("maxlength") => match *attr.value() {
3006 AttrValue::Int(_, value) => {
3007 let mut textinput = self.textinput.borrow_mut();
3008
3009 if value < 0 {
3010 textinput.set_max_length(None);
3011 } else {
3012 textinput.set_max_length(Some(UTF16CodeUnits(value as usize)))
3013 }
3014 },
3015 _ => panic!("Expected an AttrValue::Int"),
3016 },
3017 local_name!("minlength") => match *attr.value() {
3018 AttrValue::Int(_, value) => {
3019 let mut textinput = self.textinput.borrow_mut();
3020
3021 if value < 0 {
3022 textinput.set_min_length(None);
3023 } else {
3024 textinput.set_min_length(Some(UTF16CodeUnits(value as usize)))
3025 }
3026 },
3027 _ => panic!("Expected an AttrValue::Int"),
3028 },
3029 local_name!("placeholder") => {
3030 {
3031 let mut placeholder = self.placeholder.borrow_mut();
3032 placeholder.clear();
3033 if let AttributeMutation::Set(_) = mutation {
3034 placeholder
3035 .extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
3036 }
3037 }
3038 self.update_placeholder_shown_state();
3039 self.update_text_shadow_tree_placeholder(can_gc);
3040 },
3041 local_name!("readonly") => {
3042 if self.input_type().is_textual() {
3043 let el = self.upcast::<Element>();
3044 match mutation {
3045 AttributeMutation::Set(_) => {
3046 el.set_read_write_state(false);
3047 },
3048 AttributeMutation::Removed => {
3049 el.set_read_write_state(!el.disabled_state());
3050 },
3051 }
3052 }
3053 },
3054 local_name!("form") => {
3055 self.form_attribute_mutated(mutation, can_gc);
3056 },
3057 _ => {},
3058 }
3059
3060 self.value_changed(can_gc);
3061 }
3062
3063 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
3064 match *name {
3065 local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
3066 local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
3067 local_name!("type") => AttrValue::from_atomic(value.into()),
3068 local_name!("maxlength") => {
3069 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
3070 },
3071 local_name!("minlength") => {
3072 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
3073 },
3074 _ => self
3075 .super_type()
3076 .unwrap()
3077 .parse_plain_attribute(name, value),
3078 }
3079 }
3080
3081 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
3082 if let Some(s) = self.super_type() {
3083 s.bind_to_tree(context, can_gc);
3084 }
3085 self.upcast::<Element>()
3086 .check_ancestors_disabled_state_for_form_control();
3087
3088 if self.input_type() == InputType::Radio {
3089 self.radio_group_updated(self.radio_group_name().as_ref());
3090 }
3091
3092 self.value_changed(can_gc);
3093 }
3094
3095 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
3096 let form_owner = self.form_owner();
3097
3098 self.super_type().unwrap().unbind_from_tree(context, can_gc);
3099
3100 let node = self.upcast::<Node>();
3101 let el = self.upcast::<Element>();
3102 if node
3103 .ancestors()
3104 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
3105 {
3106 el.check_ancestors_disabled_state_for_form_control();
3107 } else {
3108 el.check_disabled_attribute();
3109 }
3110
3111 if self.input_type() == InputType::Radio {
3112 let root = context.parent.GetRootNode(&GetRootNodeOptions::empty());
3113 for r in radio_group_iter(
3114 self,
3115 self.radio_group_name().as_ref(),
3116 form_owner.as_deref(),
3117 &root,
3118 ) {
3119 r.validity_state()
3120 .perform_validation_and_update(ValidationFlags::all(), can_gc);
3121 }
3122 }
3123
3124 self.validity_state()
3125 .perform_validation_and_update(ValidationFlags::all(), can_gc);
3126 }
3127
3128 fn handle_event(&self, event: &Event, can_gc: CanGc) {
3134 if let Some(s) = self.super_type() {
3135 s.handle_event(event, can_gc);
3136 }
3137
3138 if event.type_() == atom!("click") && !event.DefaultPrevented() {
3139 if self.input_type().is_textual_or_password() &&
3145 !self.textinput.borrow().is_empty()
3147 {
3148 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
3149 if let Some(point_in_target) = mouse_event.point_in_target() {
3153 let window = self.owner_window();
3154 let index = window
3155 .text_index_query(self.upcast::<Node>(), point_in_target.to_untyped());
3156 let edit_point_index = match index {
3159 Some(i) => i,
3160 None => self.textinput.borrow().char_count(),
3161 };
3162 self.textinput
3163 .borrow_mut()
3164 .set_edit_point_index(edit_point_index);
3165 self.upcast::<Node>().dirty(NodeDamage::Other);
3167 event.PreventDefault();
3168 }
3169 }
3170 }
3171 } else if event.type_() == atom!("keydown") &&
3172 !event.DefaultPrevented() &&
3173 self.input_type().is_textual_or_password()
3174 {
3175 if let Some(keyevent) = event.downcast::<KeyboardEvent>() {
3176 let action = self.textinput.borrow_mut().handle_keydown(keyevent);
3179 match action {
3180 TriggerDefaultAction => {
3181 self.implicit_submission(can_gc);
3182 },
3183 DispatchInput => {
3184 if event.IsTrusted() {
3185 self.owner_global()
3186 .task_manager()
3187 .user_interaction_task_source()
3188 .queue_event(
3189 self.upcast(),
3190 atom!("input"),
3191 EventBubbles::Bubbles,
3192 EventCancelable::NotCancelable,
3193 );
3194 }
3195 self.value_dirty.set(true);
3196 self.update_placeholder_shown_state();
3197 self.upcast::<Node>().dirty(NodeDamage::Other);
3198 event.mark_as_handled();
3199 },
3200 RedrawSelection => {
3201 self.upcast::<Node>().dirty(NodeDamage::Other);
3202 event.mark_as_handled();
3203 },
3204 Nothing => (),
3205 }
3206 }
3207 } else if event.type_() == atom!("keypress") &&
3208 !event.DefaultPrevented() &&
3209 self.input_type().is_textual_or_password()
3210 {
3211 } else if (event.type_() == atom!("compositionstart") ||
3215 event.type_() == atom!("compositionupdate") ||
3216 event.type_() == atom!("compositionend")) &&
3217 self.input_type().is_textual_or_password()
3218 {
3219 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
3220 if event.type_() == atom!("compositionend") {
3221 let _ = self
3222 .textinput
3223 .borrow_mut()
3224 .handle_compositionend(compositionevent);
3225 self.upcast::<Node>().dirty(NodeDamage::Other);
3226 } else if event.type_() == atom!("compositionupdate") {
3227 let _ = self
3228 .textinput
3229 .borrow_mut()
3230 .handle_compositionupdate(compositionevent);
3231 self.upcast::<Node>().dirty(NodeDamage::Other);
3232 }
3233 event.mark_as_handled();
3234 }
3235 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
3236 let reaction = self
3237 .textinput
3238 .borrow_mut()
3239 .handle_clipboard_event(clipboard_event);
3240 if reaction.contains(ClipboardEventReaction::FireClipboardChangedEvent) {
3241 self.owner_document()
3242 .event_handler()
3243 .fire_clipboardchange_event(can_gc);
3244 }
3245 if reaction.contains(ClipboardEventReaction::QueueInputEvent) {
3246 self.owner_global()
3247 .task_manager()
3248 .user_interaction_task_source()
3249 .queue_event(
3250 self.upcast(),
3251 atom!("input"),
3252 EventBubbles::Bubbles,
3253 EventCancelable::NotCancelable,
3254 );
3255 }
3256 if !reaction.is_empty() {
3257 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
3258 }
3259 }
3260
3261 self.value_changed(can_gc);
3262 }
3263
3264 fn cloning_steps(
3266 &self,
3267 copy: &Node,
3268 maybe_doc: Option<&Document>,
3269 clone_children: CloneChildrenFlag,
3270 can_gc: CanGc,
3271 ) {
3272 if let Some(s) = self.super_type() {
3273 s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
3274 }
3275 let elem = copy.downcast::<HTMLInputElement>().unwrap();
3276 elem.value_dirty.set(self.value_dirty.get());
3277 elem.checked_changed.set(self.checked_changed.get());
3278 elem.upcast::<Element>()
3279 .set_state(ElementState::CHECKED, self.Checked());
3280 elem.textinput
3281 .borrow_mut()
3282 .set_content(self.textinput.borrow().get_content());
3283 self.value_changed(can_gc);
3284 }
3285}
3286
3287impl FormControl for HTMLInputElement {
3288 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
3289 self.form_owner.get()
3290 }
3291
3292 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
3293 self.form_owner.set(form);
3294 }
3295
3296 fn to_element(&self) -> &Element {
3297 self.upcast::<Element>()
3298 }
3299}
3300
3301impl Validatable for HTMLInputElement {
3302 fn as_element(&self) -> &Element {
3303 self.upcast()
3304 }
3305
3306 fn validity_state(&self) -> DomRoot<ValidityState> {
3307 self.validity_state
3308 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), CanGc::note()))
3309 }
3310
3311 fn is_instance_validatable(&self) -> bool {
3312 match self.input_type() {
3319 InputType::Hidden | InputType::Button | InputType::Reset => false,
3320 _ => {
3321 !(self.upcast::<Element>().disabled_state() ||
3322 self.ReadOnly() ||
3323 is_barred_by_datalist_ancestor(self.upcast()))
3324 },
3325 }
3326 }
3327
3328 fn perform_validation(
3329 &self,
3330 validate_flags: ValidationFlags,
3331 _can_gc: CanGc,
3332 ) -> ValidationFlags {
3333 let mut failed_flags = ValidationFlags::empty();
3334 let value = self.Value();
3335
3336 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
3337 self.suffers_from_being_missing(&value)
3338 {
3339 failed_flags.insert(ValidationFlags::VALUE_MISSING);
3340 }
3341
3342 if validate_flags.contains(ValidationFlags::TYPE_MISMATCH) &&
3343 self.suffers_from_type_mismatch(&value)
3344 {
3345 failed_flags.insert(ValidationFlags::TYPE_MISMATCH);
3346 }
3347
3348 if validate_flags.contains(ValidationFlags::PATTERN_MISMATCH) &&
3349 self.suffers_from_pattern_mismatch(&value)
3350 {
3351 failed_flags.insert(ValidationFlags::PATTERN_MISMATCH);
3352 }
3353
3354 if validate_flags.contains(ValidationFlags::BAD_INPUT) &&
3355 self.suffers_from_bad_input(&value)
3356 {
3357 failed_flags.insert(ValidationFlags::BAD_INPUT);
3358 }
3359
3360 if validate_flags.intersects(ValidationFlags::TOO_LONG | ValidationFlags::TOO_SHORT) {
3361 failed_flags |= self.suffers_from_length_issues(&value);
3362 }
3363
3364 if validate_flags.intersects(
3365 ValidationFlags::RANGE_UNDERFLOW |
3366 ValidationFlags::RANGE_OVERFLOW |
3367 ValidationFlags::STEP_MISMATCH,
3368 ) {
3369 failed_flags |= self.suffers_from_range_issues(&value);
3370 }
3371
3372 failed_flags & validate_flags
3373 }
3374}
3375
3376impl Activatable for HTMLInputElement {
3377 fn as_element(&self) -> &Element {
3378 self.upcast()
3379 }
3380
3381 fn is_instance_activatable(&self) -> bool {
3382 match self.input_type() {
3383 InputType::Submit | InputType::Reset | InputType::File | InputType::Image => {
3388 self.is_mutable()
3389 },
3390 InputType::Checkbox | InputType::Radio | InputType::Color => true,
3394 _ => false,
3395 }
3396 }
3397
3398 fn legacy_pre_activation_behavior(&self, can_gc: CanGc) -> Option<InputActivationState> {
3400 let ty = self.input_type();
3401 let activation_state = match ty {
3402 InputType::Checkbox => {
3403 let was_checked = self.Checked();
3404 let was_indeterminate = self.Indeterminate();
3405 self.SetIndeterminate(false);
3406 self.SetChecked(!was_checked);
3407 Some(InputActivationState {
3408 checked: was_checked,
3409 indeterminate: was_indeterminate,
3410 checked_radio: None,
3411 old_type: InputType::Checkbox,
3412 })
3413 },
3414 InputType::Radio => {
3415 let root = self
3416 .upcast::<Node>()
3417 .GetRootNode(&GetRootNodeOptions::empty());
3418 let form_owner = self.form_owner();
3419 let checked_member = radio_group_iter(
3420 self,
3421 self.radio_group_name().as_ref(),
3422 form_owner.as_deref(),
3423 &root,
3424 )
3425 .find(|r| r.Checked());
3426 let was_checked = self.Checked();
3427 self.SetChecked(true);
3428 Some(InputActivationState {
3429 checked: was_checked,
3430 indeterminate: false,
3431 checked_radio: checked_member.as_deref().map(DomRoot::from_ref),
3432 old_type: InputType::Radio,
3433 })
3434 },
3435 _ => None,
3436 };
3437
3438 if activation_state.is_some() {
3439 self.value_changed(can_gc);
3440 }
3441
3442 activation_state
3443 }
3444
3445 fn legacy_canceled_activation_behavior(
3447 &self,
3448 cache: Option<InputActivationState>,
3449 can_gc: CanGc,
3450 ) {
3451 let ty = self.input_type();
3453 let cache = match cache {
3454 Some(cache) => {
3455 if cache.old_type != ty {
3456 return;
3459 }
3460 cache
3461 },
3462 None => {
3463 return;
3464 },
3465 };
3466
3467 match ty {
3468 InputType::Checkbox => {
3470 self.SetIndeterminate(cache.indeterminate);
3471 self.SetChecked(cache.checked);
3472 },
3473 InputType::Radio => {
3475 if let Some(ref o) = cache.checked_radio {
3476 let tree_root = self
3477 .upcast::<Node>()
3478 .GetRootNode(&GetRootNodeOptions::empty());
3479 if in_same_group(
3482 o,
3483 self.form_owner().as_deref(),
3484 self.radio_group_name().as_ref(),
3485 Some(&*tree_root),
3486 ) {
3487 o.SetChecked(true);
3488 } else {
3489 self.SetChecked(false);
3490 }
3491 } else {
3492 self.SetChecked(false);
3493 }
3494 },
3495 _ => (),
3496 }
3497
3498 self.value_changed(can_gc);
3499 }
3500
3501 fn activation_behavior(&self, _event: &Event, _target: &EventTarget, can_gc: CanGc) {
3503 match self.input_type() {
3504 InputType::Submit | InputType::Image => {
3507 if let Some(form_owner) = self.form_owner() {
3509 let document = self.owner_document();
3511
3512 if !document.is_fully_active() {
3513 return;
3514 }
3515
3516 form_owner.submit(
3519 SubmittedFrom::NotFromForm,
3520 FormSubmitterElement::Input(self),
3521 can_gc,
3522 )
3523 }
3524 },
3525 InputType::Reset => {
3526 if let Some(form_owner) = self.form_owner() {
3529 let document = self.owner_document();
3530
3531 if !document.is_fully_active() {
3533 return;
3534 }
3535
3536 form_owner.reset(ResetFrom::NotFromForm, can_gc);
3538 }
3539 },
3540 InputType::Checkbox | InputType::Radio => {
3543 if !self.upcast::<Node>().is_connected() {
3545 return;
3546 }
3547
3548 let target = self.upcast::<EventTarget>();
3549
3550 target.fire_bubbling_event(atom!("input"), can_gc);
3553
3554 target.fire_bubbling_event(atom!("change"), can_gc);
3557 },
3558 InputType::File => {
3560 let _ = self.select_files(None, can_gc);
3561 },
3562 InputType::Color => {
3564 self.show_the_picker_if_applicable();
3565 },
3566 _ => (),
3567 }
3568 }
3569}
3570
3571fn filter_from_accept(s: &DOMString) -> Vec<FilterPattern> {
3573 let mut filter = vec![];
3574 for p in split_commas(&s.str()) {
3575 let p = p.trim();
3576 if let Some('.') = p.chars().next() {
3577 filter.push(FilterPattern(p[1..].to_string()));
3578 } else if let Some(exts) = mime_guess::get_mime_extensions_str(p) {
3579 for ext in exts {
3580 filter.push(FilterPattern(ext.to_string()));
3581 }
3582 }
3583 }
3584
3585 filter
3586}
3587
3588fn round_halves_positive(n: f64) -> f64 {
3589 if n.fract() == -0.5 {
3593 n.ceil()
3594 } else {
3595 n.round()
3596 }
3597}
3598
3599fn compile_pattern(cx: SafeJSContext, pattern_str: &str, out_regex: MutableHandleObject) -> bool {
3603 if check_js_regex_syntax(cx, pattern_str) {
3605 let pattern_str = format!("^(?:{})$", pattern_str);
3607 let flags = RegExpFlags {
3608 flags_: RegExpFlag_UnicodeSets,
3609 };
3610 new_js_regex(cx, &pattern_str, flags, out_regex)
3611 } else {
3612 false
3613 }
3614}
3615
3616#[allow(unsafe_code)]
3617fn check_js_regex_syntax(cx: SafeJSContext, pattern: &str) -> bool {
3620 let pattern: Vec<u16> = pattern.encode_utf16().collect();
3621 unsafe {
3622 rooted!(in(*cx) let mut exception = UndefinedValue());
3623
3624 let valid = CheckRegExpSyntax(
3625 *cx,
3626 pattern.as_ptr(),
3627 pattern.len(),
3628 RegExpFlags {
3629 flags_: RegExpFlag_UnicodeSets,
3630 },
3631 exception.handle_mut(),
3632 );
3633
3634 if !valid {
3635 JS_ClearPendingException(*cx);
3636 return false;
3637 }
3638
3639 exception.is_undefined()
3642 }
3643}
3644
3645#[allow(unsafe_code)]
3648pub(crate) fn new_js_regex(
3649 cx: SafeJSContext,
3650 pattern: &str,
3651 flags: RegExpFlags,
3652 mut out_regex: MutableHandleObject,
3653) -> bool {
3654 let pattern: Vec<u16> = pattern.encode_utf16().collect();
3655 unsafe {
3656 out_regex.set(NewUCRegExpObject(
3657 *cx,
3658 pattern.as_ptr(),
3659 pattern.len(),
3660 flags,
3661 ));
3662 if out_regex.is_null() {
3663 JS_ClearPendingException(*cx);
3664 return false;
3665 }
3666 }
3667 true
3668}
3669
3670#[allow(unsafe_code)]
3671fn matches_js_regex(cx: SafeJSContext, regex_obj: HandleObject, value: &str) -> Result<bool, ()> {
3672 let mut value: Vec<u16> = value.encode_utf16().collect();
3673
3674 unsafe {
3675 let mut is_regex = false;
3676 assert!(ObjectIsRegExp(*cx, regex_obj, &mut is_regex));
3677 assert!(is_regex);
3678
3679 rooted!(in(*cx) let mut rval = UndefinedValue());
3680 let mut index = 0;
3681
3682 let ok = ExecuteRegExpNoStatics(
3683 *cx,
3684 regex_obj,
3685 value.as_mut_ptr(),
3686 value.len(),
3687 &mut index,
3688 true,
3689 rval.handle_mut(),
3690 );
3691
3692 if ok {
3693 Ok(!rval.is_null())
3694 } else {
3695 JS_ClearPendingException(*cx);
3696 Err(())
3697 }
3698 }
3699}