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