1use std::cell::{Cell, RefCell};
6use std::ptr;
7
8use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
9use dom_struct::dom_struct;
10use embedder_traits::{EmbedderControlRequest, InputMethodRequest, RgbColor, SelectedFile};
11use encoding_rs::Encoding;
12use html5ever::{LocalName, Prefix, local_name};
13use js::context::{JSContext, NoGC};
14use js::jsapi::{ClippedTime, JSObject, RegExpFlag_UnicodeSets, RegExpFlags};
15use js::jsval::UndefinedValue;
16use js::rust::wrappers2::{
17 CheckRegExpSyntax, DateGetMsecSinceEpoch, ExecuteRegExpNoStatics, JS_ClearPendingException,
18 NewDateObject, NewUCRegExpObject, ObjectIsDate, ObjectIsRegExp,
19};
20use js::rust::{HandleObject, MutableHandleObject};
21use num_traits::ToPrimitive;
22use script_bindings::cell::{DomRefCell, Ref};
23use script_bindings::codegen::GenericBindings::SelectionBinding::SelectionMethods;
24use script_bindings::domstring::{TracedDOMString, parse_floating_point_number};
25use servo_base::generic_channel::GenericSender;
26use servo_base::text::{RangeAny, Utf16CodeUnits, Utf32CodeUnits};
27use style::attr::{AttrValue, LengthOrPercentageOrAuto};
28use style::str::split_commas;
29use stylo_atoms::Atom;
30use stylo_dom::ElementState;
31use time::OffsetDateTime;
32use unicode_bidi::{BidiClass, bidi_class};
33use webdriver::error::ErrorStatus;
34use xml5ever::ns;
35
36use crate::dom::MoveContext;
37use crate::dom::activation::Activatable;
38use crate::dom::bindings::cell::AtomicSafeBorrowMut;
39use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
40use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
41use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
42use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
43use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
44use crate::dom::bindings::error::{Error, ErrorResult};
45use crate::dom::bindings::inheritance::Castable;
46use crate::dom::bindings::refcounted::Trusted;
47use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
48use crate::dom::bindings::str::{DOMString, USVString};
49use crate::dom::document::Document;
50use crate::dom::document_embedder_controls::ControlElement;
51use crate::dom::element::attributes::storage::AttrRef;
52use crate::dom::element::{AttributeMutation, Element};
53use crate::dom::event::Event;
54use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
55use crate::dom::eventtarget::EventTarget;
56use crate::dom::filelist::FileList;
57use crate::dom::html::form_controls::input_type::radio_input_type::perform_radio_group_validation;
58use crate::dom::html::form_controls::input_type::{
59 InputActivationType, InputType, ValueChangeEvents,
60};
61use crate::dom::html::form_controls::text_control::TextControlElement;
62use crate::dom::html::form_controls::text_input::{KeyReaction, Lines, TextInput};
63use crate::dom::html::htmldatalistelement::HTMLDataListElement;
64use crate::dom::html::htmlelement::HTMLElement;
65use crate::dom::html::htmlformelement::{
66 FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, SubmittedFrom,
67};
68use crate::dom::input_type::radio_input_type::radio_group_iter;
69use crate::dom::iterators::ShadowIncluding;
70use crate::dom::node::virtualmethods::VirtualMethods;
71use crate::dom::node::{
72 BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
73};
74use crate::dom::nodelist::NodeList;
75use crate::dom::text_input::EmbedderClipboardProvider;
76use crate::dom::types::FocusEvent;
77use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
78use crate::dom::validitystate::{ValidationFlags, ValidityState};
79use crate::realms::enter_auto_realm;
80
81#[derive(Debug, PartialEq)]
82pub(crate) enum ValueMode {
83 Value,
85
86 Default,
88
89 DefaultOn,
91
92 Filename,
94}
95
96#[derive(Debug, PartialEq)]
97enum StepDirection {
98 Up,
99 Down,
100}
101
102#[dom_struct]
103pub(crate) struct HTMLInputElement {
104 htmlelement: HTMLElement,
105 input_type: DomRefCell<InputType>,
106
107 is_textual_or_password: Cell<bool>,
110
111 placeholder: DomRefCell<TracedDOMString>,
112 size: Cell<u32>,
113 maxlength: Cell<i32>,
114 minlength: Cell<i32>,
115 checkedness: Cell<bool>,
119 checked_changed: Cell<bool>,
121 indeterminateness: Cell<bool>,
127 #[no_trace]
128 text_input: AtomicRefCell<TextInput<EmbedderClipboardProvider>>,
129 form_owner: MutNullableDom<HTMLFormElement>,
130 labels_node_list: MutNullableDom<NodeList>,
131 validity_state: MutNullableDom<ValidityState>,
132 #[no_trace]
133 pending_webdriver_response: RefCell<Option<Box<PendingWebDriverResponse>>>,
134 value_dirty: Cell<bool>,
136
137 has_scheduled_selectionchange_event: Cell<bool>,
139}
140
141#[derive(JSTraceable)]
142pub(crate) struct InputActivationState {
143 pub(crate) indeterminate: bool,
144 pub(crate) checked: bool,
145 pub(crate) checked_radio: Option<DomRoot<HTMLInputElement>>,
146 pub(crate) was_radio: bool,
147 pub(crate) was_checkbox: bool,
148 }
150
151static DEFAULT_INPUT_SIZE: u32 = 20;
152static DEFAULT_MAX_LENGTH: i32 = -1;
153static DEFAULT_MIN_LENGTH: i32 = -1;
154
155#[expect(non_snake_case)]
156impl HTMLInputElement {
157 fn new_inherited(
158 local_name: LocalName,
159 prefix: Option<Prefix>,
160 document: &Document,
161 ) -> HTMLInputElement {
162 let embedder_sender = document
163 .window()
164 .as_global_scope()
165 .script_to_embedder_chan()
166 .clone();
167 HTMLInputElement {
168 htmlelement: HTMLElement::new_inherited_with_state(
169 ElementState::ENABLED | ElementState::READWRITE | ElementState::OPTIONAL_,
170 local_name,
171 prefix,
172 document,
173 ),
174 input_type: DomRefCell::new(InputType::new_text()),
175 is_textual_or_password: Cell::new(true),
176 placeholder: DomRefCell::new(DOMString::new().traced()),
177 checkedness: Cell::new(false),
178 checked_changed: Cell::new(false),
179 indeterminateness: Cell::new(false),
180 maxlength: Cell::new(DEFAULT_MAX_LENGTH),
181 minlength: Cell::new(DEFAULT_MIN_LENGTH),
182 size: Cell::new(DEFAULT_INPUT_SIZE),
183 text_input: AtomicRefCell::new(TextInput::new(
184 Lines::Single,
185 DOMString::new(),
186 EmbedderClipboardProvider {
187 embedder_sender,
188 webview_id: document.webview_id(),
189 },
190 )),
191 value_dirty: Cell::new(false),
192 form_owner: Default::default(),
193 labels_node_list: MutNullableDom::new(None),
194 validity_state: Default::default(),
195 pending_webdriver_response: Default::default(),
196 has_scheduled_selectionchange_event: Default::default(),
197 }
198 }
199
200 pub(crate) fn new(
201 cx: &mut JSContext,
202 local_name: LocalName,
203 prefix: Option<Prefix>,
204 document: &Document,
205 proto: Option<HandleObject>,
206 ) -> DomRoot<HTMLInputElement> {
207 Node::reflect_node_with_proto(
208 cx,
209 Box::new(HTMLInputElement::new_inherited(
210 local_name, prefix, document,
211 )),
212 document,
213 proto,
214 )
215 }
216
217 pub(crate) fn auto_directionality(&self) -> Option<String> {
218 match *self.input_type() {
219 InputType::Text(_) | InputType::Search(_) | InputType::Url(_) | InputType::Email(_) => {
220 let value: String = String::from(self.Value());
221 Some(HTMLInputElement::directionality_from_value(&value))
222 },
223 _ => None,
224 }
225 }
226
227 pub(crate) fn directionality_from_value(value: &str) -> String {
228 if HTMLInputElement::is_first_strong_character_rtl(value) {
229 "rtl".to_owned()
230 } else {
231 "ltr".to_owned()
232 }
233 }
234
235 fn is_first_strong_character_rtl(value: &str) -> bool {
236 for ch in value.chars() {
237 return match bidi_class(ch) {
238 BidiClass::L => false,
239 BidiClass::AL => true,
240 BidiClass::R => true,
241 _ => continue,
242 };
243 }
244 false
245 }
246
247 pub(crate) fn value_mode(&self) -> ValueMode {
250 match *self.input_type() {
251 InputType::Submit(_) |
252 InputType::Reset(_) |
253 InputType::Button(_) |
254 InputType::Image(_) |
255 InputType::Hidden(_) => ValueMode::Default,
256
257 InputType::Checkbox(_) | InputType::Radio(_) => ValueMode::DefaultOn,
258
259 InputType::Color(_) |
260 InputType::Date(_) |
261 InputType::DatetimeLocal(_) |
262 InputType::Email(_) |
263 InputType::Month(_) |
264 InputType::Number(_) |
265 InputType::Password(_) |
266 InputType::Range(_) |
267 InputType::Search(_) |
268 InputType::Tel(_) |
269 InputType::Text(_) |
270 InputType::Time(_) |
271 InputType::Url(_) |
272 InputType::Week(_) => ValueMode::Value,
273
274 InputType::File(_) => ValueMode::Filename,
275 }
276 }
277
278 #[inline]
279 pub(crate) fn input_type(&self) -> Ref<'_, InputType> {
280 self.input_type.borrow()
281 }
282
283 pub(crate) fn is_nontypeable(&self) -> bool {
285 matches!(
286 *self.input_type(),
287 InputType::Button(_) |
288 InputType::Checkbox(_) |
289 InputType::Color(_) |
290 InputType::File(_) |
291 InputType::Hidden(_) |
292 InputType::Image(_) |
293 InputType::Radio(_) |
294 InputType::Range(_) |
295 InputType::Reset(_) |
296 InputType::Submit(_)
297 )
298 }
299
300 #[inline]
301 pub(crate) fn is_submit_button(&self) -> bool {
302 matches!(
303 *self.input_type(),
304 InputType::Submit(_) | InputType::Image(_)
305 )
306 }
307
308 pub(crate) fn is_auto_directionality_form_associated_element(&self) -> bool {
310 matches!(
311 *self.input_type(),
312 InputType::Hidden(_) |
313 InputType::Text(_) |
314 InputType::Search(_) |
315 InputType::Tel(_) |
316 InputType::Url(_) |
317 InputType::Email(_) |
318 InputType::Password(_) |
319 InputType::Submit(_) |
320 InputType::Reset(_) |
321 InputType::Button(_)
322 )
323 }
324
325 fn does_minmaxlength_apply(&self) -> bool {
326 matches!(
327 *self.input_type(),
328 InputType::Text(_) |
329 InputType::Search(_) |
330 InputType::Url(_) |
331 InputType::Tel(_) |
332 InputType::Email(_) |
333 InputType::Password(_)
334 )
335 }
336
337 fn does_pattern_apply(&self) -> bool {
338 matches!(
339 *self.input_type(),
340 InputType::Text(_) |
341 InputType::Search(_) |
342 InputType::Url(_) |
343 InputType::Tel(_) |
344 InputType::Email(_) |
345 InputType::Password(_)
346 )
347 }
348
349 fn does_multiple_apply(&self) -> bool {
350 matches!(*self.input_type(), InputType::Email(_))
351 }
352
353 fn does_required_apply(&self) -> bool {
355 matches!(
356 *self.input_type(),
357 InputType::Text(_) |
358 InputType::Search(_) |
359 InputType::Tel(_) |
360 InputType::Url(_) |
361 InputType::Email(_) |
362 InputType::Password(_) |
363 InputType::Date(_) |
364 InputType::Month(_) |
365 InputType::Week(_) |
366 InputType::Time(_) |
367 InputType::DatetimeLocal(_) |
368 InputType::Number(_) |
369 InputType::Checkbox(_) |
370 InputType::Radio(_) |
371 InputType::File(_)
372 )
373 }
374
375 fn update_required_and_optional_state(&self, no_gc: &NoGC) {
376 let element = self.upcast::<Element>();
377 let required_applies = self.does_required_apply();
378 let required = element.has_attribute(&local_name!("required"));
379
380 element.set_state(no_gc, ElementState::REQUIRED, required_applies && required);
384
385 element.set_state(
389 no_gc,
390 ElementState::OPTIONAL_,
391 required_applies && !required,
392 );
393 }
394
395 fn does_value_as_number_apply(&self) -> bool {
398 matches!(
399 *self.input_type(),
400 InputType::Date(_) |
401 InputType::Month(_) |
402 InputType::Week(_) |
403 InputType::Time(_) |
404 InputType::DatetimeLocal(_) |
405 InputType::Number(_) |
406 InputType::Range(_)
407 )
408 }
409
410 fn does_value_as_date_apply(&self) -> bool {
411 matches!(
412 *self.input_type(),
413 InputType::Date(_) | InputType::Month(_) | InputType::Week(_) | InputType::Time(_)
414 )
415 }
416
417 pub(crate) fn allowed_value_step(&self) -> Option<f64> {
419 let default_step = self.default_step()?;
422
423 let Some(step_value) = self
426 .upcast::<Element>()
427 .get_attribute_string_value(&local_name!("step"))
428 else {
429 return Some(default_step * self.step_scale_factor());
430 };
431
432 if step_value.eq_ignore_ascii_case("any") {
435 return None;
436 }
437
438 let Some(parsed_value) =
442 parse_floating_point_number(&step_value).filter(|value| *value > 0.0)
443 else {
444 return Some(default_step * self.step_scale_factor());
445 };
446
447 Some(parsed_value * self.step_scale_factor())
451 }
452
453 pub(crate) fn minimum(&self) -> Option<f64> {
455 self.upcast::<Element>()
456 .get_attribute_string_value(&local_name!("min"))
457 .and_then(|value| self.convert_string_to_number(&value))
458 .or_else(|| self.default_minimum())
459 }
460
461 pub(crate) fn maximum(&self) -> Option<f64> {
463 self.upcast::<Element>()
464 .get_attribute_string_value(&local_name!("max"))
465 .and_then(|value| self.convert_string_to_number(&value))
466 .or_else(|| self.default_maximum())
467 }
468
469 pub(crate) fn stepped_minimum(&self) -> Option<f64> {
472 match (self.minimum(), self.allowed_value_step()) {
473 (Some(min), Some(allowed_step)) => {
474 let step_base = self.step_base();
475 let nsteps = (min - step_base) / allowed_step;
477 Some(step_base + (allowed_step * nsteps.ceil()))
479 },
480 (_, _) => None,
481 }
482 }
483
484 pub(crate) fn stepped_maximum(&self) -> Option<f64> {
487 match (self.maximum(), self.allowed_value_step()) {
488 (Some(max), Some(allowed_step)) => {
489 let step_base = self.step_base();
490 let nsteps = (max - step_base) / allowed_step;
492 Some(step_base + (allowed_step * nsteps.floor()))
494 },
495 (_, _) => None,
496 }
497 }
498
499 fn default_minimum(&self) -> Option<f64> {
501 match *self.input_type() {
502 InputType::Range(_) => Some(0.0),
503 _ => None,
504 }
505 }
506
507 fn default_maximum(&self) -> Option<f64> {
509 match *self.input_type() {
510 InputType::Range(_) => Some(100.0),
511 _ => None,
512 }
513 }
514
515 pub(crate) fn default_range_value(&self) -> f64 {
517 let min = self.minimum().unwrap_or(0.0);
518 let max = self.maximum().unwrap_or(100.0);
519 if max < min {
520 min
521 } else {
522 min + (max - min) * 0.5
523 }
524 }
525
526 fn default_step(&self) -> Option<f64> {
528 match *self.input_type() {
529 InputType::Date(_) => Some(1.0),
530 InputType::Month(_) => Some(1.0),
531 InputType::Week(_) => Some(1.0),
532 InputType::Time(_) => Some(60.0),
533 InputType::DatetimeLocal(_) => Some(60.0),
534 InputType::Number(_) => Some(1.0),
535 InputType::Range(_) => Some(1.0),
536 _ => None,
537 }
538 }
539
540 fn step_scale_factor(&self) -> f64 {
542 match *self.input_type() {
543 InputType::Date(_) => 86400000.0,
544 InputType::Month(_) => 1.0,
545 InputType::Week(_) => 604800000.0,
546 InputType::Time(_) => 1000.0,
547 InputType::DatetimeLocal(_) => 1000.0,
548 InputType::Number(_) => 1.0,
549 InputType::Range(_) => 1.0,
550 _ => unreachable!(),
551 }
552 }
553
554 pub(crate) fn step_base(&self) -> f64 {
556 if let Some(minimum) = self
560 .upcast::<Element>()
561 .get_attribute_string_value(&local_name!("min"))
562 .and_then(|value| self.convert_string_to_number(&value))
563 {
564 return minimum;
565 }
566
567 if let Some(value) = self
571 .upcast::<Element>()
572 .get_attribute_string_value(&local_name!("value"))
573 .and_then(|value| self.convert_string_to_number(&value))
574 {
575 return value;
576 }
577
578 if let Some(default_step_base) = self.default_step_base() {
580 return default_step_base;
581 }
582
583 0.0
585 }
586
587 fn default_step_base(&self) -> Option<f64> {
589 match *self.input_type() {
590 InputType::Week(_) => Some(-259200000.0),
591 _ => None,
592 }
593 }
594
595 fn step_up_or_down(&self, cx: &mut JSContext, n: i32, dir: StepDirection) -> ErrorResult {
599 if !self.does_value_as_number_apply() {
602 return Err(Error::InvalidState(Some(
603 "Input element does not implement `stepDown()` or `stepUp()`".into(),
604 )));
605 }
606 let step_base = self.step_base();
607
608 let Some(allowed_value_step) = self.allowed_value_step() else {
610 return Err(Error::InvalidState(Some(
611 "Input element does not have a value step".into(),
612 )));
613 };
614
615 let minimum = self.minimum();
618 let maximum = self.maximum();
619 if let (Some(min), Some(max)) = (minimum, maximum) {
620 if min > max {
621 return Ok(());
622 }
623
624 if let Some(stepped_minimum) = self.stepped_minimum() &&
628 stepped_minimum > max
629 {
630 return Ok(());
631 }
632 }
633
634 let mut value: f64 = self
638 .convert_string_to_number(&self.Value().str())
639 .unwrap_or(0.0);
640
641 let valueBeforeStepping = value;
643
644 if (value - step_base) % allowed_value_step != 0.0 {
649 value = match dir {
650 StepDirection::Down =>
651 {
653 let intervals_from_base = ((value - step_base) / allowed_value_step).floor();
654 intervals_from_base * allowed_value_step + step_base
655 },
656 StepDirection::Up =>
657 {
659 let intervals_from_base = ((value - step_base) / allowed_value_step).ceil();
660 intervals_from_base * allowed_value_step + step_base
661 },
662 };
663 }
664 else {
666 value += match dir {
671 StepDirection::Down => -f64::from(n) * allowed_value_step,
672 StepDirection::Up => f64::from(n) * allowed_value_step,
673 };
674 }
675
676 if let Some(min) = minimum &&
680 value < min
681 {
682 value = self.stepped_minimum().unwrap_or(value);
683 }
684
685 if let Some(max) = maximum &&
689 value > max
690 {
691 value = self.stepped_maximum().unwrap_or(value);
692 }
693
694 match dir {
698 StepDirection::Down => {
699 if value > valueBeforeStepping {
700 return Ok(());
701 }
702 },
703 StepDirection::Up => {
704 if value < valueBeforeStepping {
705 return Ok(());
706 }
707 },
708 }
709
710 self.SetValueAsNumber(cx, value)
714 }
715
716 fn suggestions_source_element(&self) -> Option<DomRoot<HTMLDataListElement>> {
718 let list_string = self
719 .upcast::<Element>()
720 .get_string_attribute(&local_name!("list"));
721 if list_string.is_empty() {
722 return None;
723 }
724 let ancestor = self
725 .upcast::<Node>()
726 .GetRootNode(&GetRootNodeOptions::empty());
727 let first_with_id = &ancestor
728 .traverse_preorder(ShadowIncluding::No)
729 .find(|node| {
730 node.downcast::<Element>()
731 .is_some_and(|e| e.Id() == list_string)
732 });
733 first_with_id
734 .as_ref()
735 .and_then(|el| el.downcast::<HTMLDataListElement>())
736 .map(DomRoot::from_ref)
737 }
738
739 fn suffers_from_being_missing(&self, value: &DOMString) -> bool {
741 self.input_type()
742 .as_specific()
743 .suffers_from_being_missing(self, value)
744 }
745
746 fn suffers_from_type_mismatch(&self, value: &DOMString) -> bool {
748 if value.is_empty() {
749 return false;
750 }
751
752 self.input_type()
753 .as_specific()
754 .suffers_from_type_mismatch(self, value)
755 }
756
757 fn suffers_from_pattern_mismatch(&self, cx: &mut JSContext, value: &DOMString) -> bool {
759 let pattern_str = self.Pattern();
762 if value.is_empty() || pattern_str.is_empty() || !self.does_pattern_apply() {
763 return false;
764 }
765
766 let mut realm = enter_auto_realm(cx, self);
767 let cx = &mut realm;
768
769 rooted!(&in(cx) let mut pattern = ptr::null_mut::<JSObject>());
771 if compile_pattern(cx, &pattern_str.str(), pattern.handle_mut()) {
772 if self.Multiple() && self.does_multiple_apply() {
773 !split_commas(&value.str())
774 .all(|s| matches_js_regex(cx, pattern.handle(), s).unwrap_or(true))
775 } else {
776 !matches_js_regex(cx, pattern.handle(), &value.str()).unwrap_or(true)
777 }
778 } else {
779 false
781 }
782 }
783
784 fn suffers_from_bad_input(&self, value: &DOMString) -> bool {
786 if value.is_empty() {
787 return false;
788 }
789
790 self.input_type()
791 .as_specific()
792 .suffers_from_bad_input(value)
793 }
794
795 fn suffers_from_length_issues(&self, value: &DOMString) -> ValidationFlags {
798 let value_dirty = self.value_dirty.get();
801 let text_input = self.text_input.borrow();
802 let edit_by_user = !text_input.was_last_change_by_set_content();
803
804 if value.is_empty() || !value_dirty || !edit_by_user || !self.does_minmaxlength_apply() {
805 return ValidationFlags::empty();
806 }
807
808 let mut failed_flags = ValidationFlags::empty();
809 let Utf16CodeUnits(value_len) = text_input.len_utf16();
810 let min_length = self.MinLength();
811 let max_length = self.MaxLength();
812
813 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as u32) {
814 failed_flags.insert(ValidationFlags::TOO_SHORT);
815 }
816
817 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as u32) {
818 failed_flags.insert(ValidationFlags::TOO_LONG);
819 }
820
821 failed_flags
822 }
823
824 fn suffers_from_range_issues(&self, value: &DOMString) -> ValidationFlags {
828 if value.is_empty() || !self.does_value_as_number_apply() {
829 return ValidationFlags::empty();
830 }
831
832 let Some(value_as_number) = self.convert_string_to_number(&value.str()) else {
833 return ValidationFlags::empty();
834 };
835
836 let mut failed_flags = ValidationFlags::empty();
837 let min_value = self.minimum();
838 let max_value = self.maximum();
839
840 let has_reversed_range = match (min_value, max_value) {
842 (Some(min), Some(max)) => self.input_type().has_periodic_domain() && min > max,
843 _ => false,
844 };
845
846 if has_reversed_range {
847 if value_as_number > max_value.unwrap() && value_as_number < min_value.unwrap() {
849 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
850 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
851 }
852 } else {
853 if let Some(min_value) = min_value &&
855 value_as_number < min_value
856 {
857 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
858 }
859 if let Some(max_value) = max_value &&
861 value_as_number > max_value
862 {
863 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
864 }
865 }
866
867 if let Some(step) = self.allowed_value_step() {
869 let diff = (self.step_base() - value_as_number) % step / value_as_number;
873 if diff.abs() > 1e-12 {
874 failed_flags.insert(ValidationFlags::STEP_MISMATCH);
875 }
876 }
877
878 failed_flags
879 }
880
881 pub(crate) fn is_textual_or_password(&self) -> bool {
883 self.is_textual_or_password.get()
884 }
885
886 fn may_have_embedder_control(&self) -> bool {
887 let el = self.upcast::<Element>();
888 matches!(*self.input_type(), InputType::Color(_)) && !el.disabled_state()
889 }
890
891 pub(crate) fn value_for_shadow_dom(&self) -> DOMString {
893 let input_type = &*self.input_type();
894 match input_type {
895 InputType::Checkbox(_) |
896 InputType::Radio(_) |
897 InputType::Image(_) |
898 InputType::Hidden(_) |
899 InputType::Range(_) => input_type.as_specific().value_for_shadow_dom(self),
900 _ => {
901 if let Some(attribute_value) = self
902 .upcast::<Element>()
903 .get_attribute_string_value(&local_name!("value"))
904 {
905 return attribute_value.into();
906 }
907 input_type.as_specific().value_for_shadow_dom(self)
908 },
909 }
910 }
911
912 fn schedule_a_selection_change_event(&self) {
914 if self.has_scheduled_selectionchange_event.get() {
916 return;
917 }
918 self.has_scheduled_selectionchange_event.set(true);
920 let this = Trusted::new(self);
922 self.owner_global()
923 .task_manager()
924 .user_interaction_task_source()
925 .queue(
926 task!(selectionchange_task_steps: move |cx| {
928 let this = this.root();
929 this.has_scheduled_selectionchange_event.set(false);
931 this.upcast::<EventTarget>().fire_event_with_params(
933 cx,
934 atom!("selectionchange"),
935 EventBubbles::Bubbles,
936 EventCancelable::NotCancelable,
937 EventComposed::Composed,
938 );
939 }),
944 );
945 }
946
947 fn dimensions(&self, cx: &mut JSContext) -> (u32, u32) {
949 let InputType::Image(ref image_input_type) = *self.input_type() else {
952 return Default::default();
953 };
954 image_input_type.dimensions(cx, self)
956 }
957}
958
959impl<'dom> LayoutDom<'dom, HTMLInputElement> {
960 pub(crate) fn size_for_layout(self) -> u32 {
970 self.unsafe_get().size.get()
971 }
972
973 pub(crate) fn selection_for_layout(self) -> Option<RangeAny<Utf32CodeUnits>> {
974 let element = self.unsafe_get();
975 if !element.is_textual_or_password.get() {
976 return None;
977 }
978 let text_input = element.text_input.borrow_mut();
979 text_input.selection_for_layout
980 }
981
982 pub(crate) fn width(self) -> LengthOrPercentageOrAuto {
983 self.image_button_dimension(&local_name!("width"))
984 }
985
986 pub(crate) fn height(self) -> LengthOrPercentageOrAuto {
987 self.image_button_dimension(&local_name!("height"))
988 }
989
990 pub(crate) fn margin_right_left(self) -> LengthOrPercentageOrAuto {
991 self.image_button_dimension(&local_name!("hspace"))
992 }
993
994 pub(crate) fn margin_top_bottom(self) -> LengthOrPercentageOrAuto {
995 self.image_button_dimension(&local_name!("vspace"))
996 }
997
998 fn image_button_dimension(self, name: &LocalName) -> LengthOrPercentageOrAuto {
999 let element = self.upcast::<Element>();
1000 let is_image_button = element
1001 .get_attr_val_for_layout(&ns!(), &local_name!("type"))
1002 .is_some_and(|value| value.eq_ignore_ascii_case("image"));
1003
1004 if !is_image_button {
1005 return LengthOrPercentageOrAuto::Auto;
1006 }
1007
1008 element
1009 .get_attr_for_layout(&ns!(), name)
1010 .map(AttrValue::as_dimension)
1011 .cloned()
1012 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1013 }
1014}
1015
1016impl TextControlElement for HTMLInputElement {
1017 fn as_element(&self) -> &Element {
1018 self.upcast()
1019 }
1020
1021 fn text_input(&self) -> AtomicRef<'_, TextInput<EmbedderClipboardProvider>> {
1022 self.text_input.borrow()
1023 }
1024
1025 fn text_input_mut(&self) -> AtomicRefMut<'_, TextInput<EmbedderClipboardProvider>> {
1026 self.text_input.borrow_mut()
1027 }
1028
1029 fn selection_api_applies(&self) -> bool {
1031 matches!(
1032 *self.input_type(),
1033 InputType::Text(_) |
1034 InputType::Search(_) |
1035 InputType::Url(_) |
1036 InputType::Tel(_) |
1037 InputType::Password(_)
1038 )
1039 }
1040
1041 fn has_selectable_text(&self) -> bool {
1049 self.is_textual_or_password() && !self.text_input.borrow().get_content().is_empty()
1050 }
1051
1052 fn has_uncollapsed_selection(&self) -> bool {
1053 self.text_input.borrow().has_uncollapsed_selection()
1054 }
1055
1056 fn set_dirty_value_flag(&self, value: bool) {
1057 self.value_dirty.set(value)
1058 }
1059
1060 fn select_all(&self) {
1061 self.text_input.borrow_mut().select_all();
1062 self.maybe_update_shared_selection();
1063 }
1064
1065 fn maybe_update_shared_selection(&self) {
1066 let selection = {
1067 let mut text_input = self.text_input.borrow_mut();
1068 let selection_range = text_input.selection_start()..text_input.selection_end();
1069 let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
1070
1071 let range_remained_equal = selection_range == text_input.previous_selection_range;
1072 if range_remained_equal && enabled == text_input.selection_for_layout.is_some() {
1073 return;
1074 }
1075
1076 if !range_remained_equal {
1077 self.schedule_a_selection_change_event();
1082 }
1083
1084 let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range());
1085 text_input.previous_selection_range = selection_range;
1086 text_input.selection_for_layout = selection;
1087 selection
1088 };
1089
1090 if let Some(text_input_widget) = self.input_type.borrow().as_specific().text_input_widget() &&
1095 text_input_widget.borrow().set_text_run_selection(selection)
1096 {
1097 self.owner_window().layout().set_needs_new_display_list();
1098 }
1099 }
1100
1101 fn is_password_field(&self) -> bool {
1102 matches!(*self.input_type(), InputType::Password(_))
1103 }
1104
1105 fn placeholder_text<'a>(&'a self) -> Ref<'a, TracedDOMString> {
1106 self.placeholder.borrow()
1107 }
1108
1109 fn value_text(&self) -> DOMString {
1110 self.Value()
1111 }
1112
1113 fn read_only_or_disabled(&self) -> bool {
1114 self.ReadOnly() || self.Disabled()
1115 }
1116
1117 fn handle_text_content_changed(&self, cx: &mut JSContext) {
1118 self.update_placeholder_shown_state(cx.no_gc());
1119 self.upcast::<Node>()
1120 .dirty(cx.no_gc(), NodeDamage::ContentOrHeritage);
1121 }
1122
1123 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction) {
1124 match action {
1125 KeyReaction::TriggerDefaultAction => {
1126 self.implicit_submission(cx);
1127 },
1128 KeyReaction::DispatchInput(text, input_type) => {
1129 self.queue_input_event(text, input_type);
1130 self.value_dirty.set(true);
1131 self.update_placeholder_shown_state(cx.no_gc());
1132 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1133 self.maybe_update_shared_selection();
1134 },
1135 KeyReaction::RedrawSelection => {
1136 self.maybe_update_shared_selection();
1137 },
1138 KeyReaction::Nothing => (),
1139 }
1140 }
1141}
1142
1143impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
1144 make_getter!(Accept, "accept");
1146
1147 make_setter!(SetAccept, "accept");
1149
1150 make_bool_getter!(Alpha, "alpha");
1152
1153 make_bool_setter!(SetAlpha, "alpha");
1155
1156 make_getter!(Alt, "alt");
1158
1159 make_setter!(SetAlt, "alt");
1161
1162 make_getter!(DirName, "dirname");
1164
1165 make_setter!(SetDirName, "dirname");
1167
1168 make_bool_getter!(Disabled, "disabled");
1170
1171 make_bool_setter!(SetDisabled, "disabled");
1173
1174 fn Width(&self, cx: &mut JSContext) -> u32 {
1176 self.dimensions(cx).0
1179 }
1180
1181 make_dimension_uint_setter!(SetWidth, "width");
1183
1184 fn Height(&self, cx: &mut JSContext) -> u32 {
1186 self.dimensions(cx).1
1189 }
1190 make_dimension_uint_setter!(SetHeight, "height");
1192
1193 make_dimension_uint_getter!(Hspace, "hspace");
1195 make_dimension_uint_setter!(SetHspace, "hspace");
1197
1198 make_dimension_uint_getter!(Vspace, "vspace");
1200 make_dimension_uint_setter!(SetVspace, "vspace");
1202
1203 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
1205 self.form_owner()
1206 }
1207
1208 fn GetFiles(&self) -> Option<DomRoot<FileList>> {
1210 self.input_type()
1211 .as_specific()
1212 .get_files()
1213 .as_ref()
1214 .cloned()
1215 }
1216
1217 fn SetFiles(&self, _cx: &mut JSContext, files: Option<&FileList>) {
1219 if let Some(files) = files {
1220 self.input_type().as_specific().set_files(files)
1221 }
1222 }
1223
1224 make_bool_getter!(DefaultChecked, "checked");
1226
1227 make_bool_setter!(SetDefaultChecked, "checked");
1229
1230 fn Checked(&self) -> bool {
1232 self.checkedness.get()
1233 }
1234
1235 fn SetChecked(&self, cx: &mut JSContext, checked: bool) {
1237 self.update_checkedness(cx, checked, true);
1238 self.value_changed(cx);
1239 }
1240
1241 make_enumerated_getter!(
1243 ColorSpace,
1244 "colorspace",
1245 "limited-srgb" | "display-p3",
1246 missing => "limited-srgb",
1247 invalid => "limited-srgb"
1248 );
1249
1250 make_setter!(SetColorSpace, "colorspace");
1252
1253 make_bool_getter!(ReadOnly, "readonly");
1255
1256 make_bool_setter!(SetReadOnly, "readonly");
1258
1259 make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE);
1261
1262 make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
1264
1265 fn Type(&self) -> DOMString {
1267 DOMString::from(self.input_type().as_str())
1268 }
1269
1270 make_atomic_setter!(SetType, "type");
1272
1273 fn Value(&self) -> DOMString {
1275 match self.value_mode() {
1276 ValueMode::Value => self.text_input.borrow().get_content(),
1277 ValueMode::Default => self
1278 .upcast::<Element>()
1279 .get_attribute_string_value(&local_name!("value"))
1280 .map(|value| value.into())
1281 .unwrap_or_default(),
1282 ValueMode::DefaultOn => self
1283 .upcast::<Element>()
1284 .get_attribute_string_value(&local_name!("value"))
1285 .map(|value| value.into())
1286 .unwrap_or(DOMString::from_static("on")),
1287 ValueMode::Filename => {
1288 let mut path = DOMString::new();
1289 match self.input_type().as_specific().get_files() {
1290 Some(ref fl) => match fl.Item(0) {
1291 Some(ref f) => {
1292 path.push_str("C:\\fakepath\\");
1293 path.push_str(&f.name().str());
1294 path
1295 },
1296 None => path,
1297 },
1298 None => path,
1299 }
1300 },
1301 }
1302 }
1303
1304 fn SetValue(&self, cx: &mut JSContext, mut value: DOMString) -> ErrorResult {
1306 match self.value_mode() {
1307 ValueMode::Value => {
1308 {
1309 self.value_dirty.set(true);
1311
1312 self.sanitize_value(&mut value);
1315
1316 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
1317
1318 if text_input.get_content() != value {
1323 text_input.set_content(value);
1325
1326 text_input.clear_selection_to_end();
1327 }
1328 }
1329
1330 self.update_placeholder_shown_state(cx.no_gc());
1334 self.maybe_update_shared_selection();
1335 },
1336 ValueMode::Default | ValueMode::DefaultOn => {
1337 self.upcast::<Element>()
1338 .set_string_attribute(cx, &local_name!("value"), value);
1339 },
1340 ValueMode::Filename => {
1341 if value.is_empty() {
1342 let window = self.owner_window();
1343 let fl = FileList::new(cx, &window, vec![]);
1344 self.input_type().as_specific().set_files(&fl)
1345 } else {
1346 return Err(Error::InvalidState(Some(
1347 "Non-empty value provided for filename".into(),
1348 )));
1349 }
1350 },
1351 }
1352
1353 self.value_changed(cx);
1354 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1355 Ok(())
1356 }
1357
1358 make_getter!(DefaultValue, "value");
1360
1361 make_setter!(SetDefaultValue, "value");
1363
1364 make_getter!(Min, "min");
1366
1367 make_setter!(SetMin, "min");
1369
1370 fn GetList(&self) -> Option<DomRoot<HTMLDataListElement>> {
1372 self.suggestions_source_element()
1373 }
1374
1375 #[expect(unsafe_code)]
1377 fn GetValueAsDate(&self, cx: &mut JSContext, mut return_value: MutableHandleObject) {
1378 if let Some(date_time) = self
1379 .input_type()
1380 .as_specific()
1381 .convert_string_to_naive_datetime(self.Value())
1382 {
1383 let time = ClippedTime {
1384 t: (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64,
1385 };
1386 return_value.set(unsafe { NewDateObject(cx, time) });
1387 }
1388 }
1389
1390 #[expect(unsafe_code)]
1392 fn SetValueAsDate(&self, cx: &mut JSContext, value: *mut JSObject) -> ErrorResult {
1393 rooted!(&in(cx) let value = value);
1394 if !self.does_value_as_date_apply() {
1395 return Err(Error::InvalidState(Some(
1396 "Input element cannot be treated as a date".into(),
1397 )));
1398 }
1399 if value.is_null() {
1400 return self.SetValue(cx, DOMString::new());
1401 }
1402 let mut msecs: f64 = 0.0;
1403 unsafe {
1407 let mut is_date = false;
1408 if !ObjectIsDate(cx, value.handle(), &mut is_date) {
1409 return Err(Error::JSFailed);
1410 }
1411 if !is_date {
1412 return Err(Error::Type(c"Value was not a date".to_owned()));
1413 }
1414 if !DateGetMsecSinceEpoch(cx, value.handle(), &mut msecs) {
1415 return Err(Error::JSFailed);
1416 }
1417 if !msecs.is_finite() {
1418 return self.SetValue(cx, DOMString::new());
1419 }
1420 }
1421
1422 let Ok(date_time) = OffsetDateTime::from_unix_timestamp_nanos((msecs * 1e6) as i128) else {
1423 return self.SetValue(cx, DOMString::new());
1424 };
1425 self.SetValue(
1426 cx,
1427 self.input_type()
1428 .as_specific()
1429 .convert_datetime_to_dom_string(date_time),
1430 )
1431 }
1432
1433 fn ValueAsNumber(&self) -> f64 {
1435 self.convert_string_to_number(&self.Value().str())
1436 .unwrap_or(f64::NAN)
1437 }
1438
1439 fn SetValueAsNumber(&self, cx: &mut JSContext, value: f64) -> ErrorResult {
1441 if value.is_infinite() {
1442 Err(Error::Type(c"value is not finite".to_owned()))
1443 } else if !self.does_value_as_number_apply() {
1444 Err(Error::InvalidState(Some(
1445 "Input element value cannot be treated as a number".into(),
1446 )))
1447 } else if value.is_nan() {
1448 self.SetValue(cx, DOMString::new())
1449 } else if let Some(converted) = self.convert_number_to_string(value) {
1450 self.SetValue(cx, converted)
1451 } else {
1452 self.SetValue(cx, DOMString::new())
1457 }
1458 }
1459
1460 make_getter!(Name, "name");
1462
1463 make_atomic_setter!(SetName, "name");
1465
1466 make_getter!(Placeholder, "placeholder");
1468
1469 make_setter!(SetPlaceholder, "placeholder");
1471
1472 make_form_action_getter!(FormAction, "formaction");
1474
1475 make_setter!(SetFormAction, "formaction");
1477
1478 make_enumerated_getter!(
1480 FormEnctype,
1481 "formenctype",
1482 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
1483 invalid => "application/x-www-form-urlencoded"
1484 );
1485
1486 make_setter!(SetFormEnctype, "formenctype");
1488
1489 make_enumerated_getter!(
1491 FormMethod,
1492 "formmethod",
1493 "get" | "post" | "dialog",
1494 invalid => "get"
1495 );
1496
1497 make_setter!(SetFormMethod, "formmethod");
1499
1500 make_getter!(FormTarget, "formtarget");
1502
1503 make_setter!(SetFormTarget, "formtarget");
1505
1506 make_bool_getter!(FormNoValidate, "formnovalidate");
1508
1509 make_bool_setter!(SetFormNoValidate, "formnovalidate");
1511
1512 make_getter!(Max, "max");
1514
1515 make_setter!(SetMax, "max");
1517
1518 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1520
1521 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1523
1524 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
1526
1527 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
1529
1530 make_bool_getter!(Multiple, "multiple");
1532
1533 make_bool_setter!(SetMultiple, "multiple");
1535
1536 make_getter!(Pattern, "pattern");
1538
1539 make_setter!(SetPattern, "pattern");
1541
1542 make_bool_getter!(Required, "required");
1544
1545 make_bool_setter!(SetRequired, "required");
1547
1548 make_url_getter!(Src, "src");
1550
1551 make_url_setter!(SetSrc, "src");
1553
1554 make_getter!(Step, "step");
1556
1557 make_setter!(SetStep, "step");
1559
1560 make_getter!(UseMap, "usemap");
1562
1563 make_setter!(SetUseMap, "usemap");
1565
1566 fn Indeterminate(&self) -> bool {
1568 self.indeterminateness.get()
1569 }
1570
1571 fn SetIndeterminate(&self, cx: &mut JSContext, val: bool) {
1573 self.indeterminateness.set(val);
1574 self.update_indeterminate_state(cx.no_gc());
1575 }
1576
1577 fn GetLabels(&self, cx: &mut JSContext) -> Option<DomRoot<NodeList>> {
1581 if matches!(*self.input_type(), InputType::Hidden(_)) {
1582 None
1583 } else {
1584 Some(self.labels_node_list.or_init(|| {
1585 NodeList::new_labels_list(
1586 cx,
1587 self.upcast::<Node>().owner_doc().window(),
1588 self.upcast::<HTMLElement>(),
1589 )
1590 }))
1591 }
1592 }
1593
1594 fn Select(&self, cx: &mut JSContext) {
1596 self.dom_select(cx);
1597 }
1598
1599 fn GetSelectionStart(&self) -> Option<u32> {
1601 self.dom_start().map(|start| start.0)
1602 }
1603
1604 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
1606 self.set_dom_start(start.map(Utf16CodeUnits::from))
1607 }
1608
1609 fn GetSelectionEnd(&self) -> Option<u32> {
1611 self.dom_end().map(|end| end.0)
1612 }
1613
1614 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
1616 self.set_dom_end(end.map(Utf16CodeUnits::from))
1617 }
1618
1619 fn GetSelectionDirection(&self) -> Option<DOMString> {
1621 self.dom_direction()
1622 }
1623
1624 fn SetSelectionDirection(
1626 &self,
1627 _cx: &mut JSContext,
1628 direction: Option<DOMString>,
1629 ) -> ErrorResult {
1630 self.set_dom_direction(direction)
1631 }
1632
1633 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
1635 self.set_dom_range(
1636 Utf16CodeUnits::from(start),
1637 Utf16CodeUnits::from(end),
1638 direction,
1639 )
1640 }
1641
1642 fn SetRangeText(&self, cx: &mut JSContext, replacement: DOMString) -> ErrorResult {
1644 self.set_dom_range_text(cx, replacement, None, None, Default::default())?;
1645 self.value_changed(cx);
1646 Ok(())
1647 }
1648
1649 fn SetRangeText_(
1651 &self,
1652 cx: &mut JSContext,
1653 replacement: DOMString,
1654 start: u32,
1655 end: u32,
1656 selection_mode: SelectionMode,
1657 ) -> ErrorResult {
1658 self.set_dom_range_text(
1659 cx,
1660 replacement,
1661 Some(Utf16CodeUnits::from(start)),
1662 Some(Utf16CodeUnits::from(end)),
1663 selection_mode,
1664 )?;
1665 self.value_changed(cx);
1666 Ok(())
1667 }
1668
1669 fn SelectFiles(&self, cx: &mut JSContext, paths: Vec<DOMString>) {
1672 self.input_type()
1673 .as_specific()
1674 .select_files(cx, self, Some(paths));
1675 }
1676
1677 fn StepUp(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1679 self.step_up_or_down(cx, n, StepDirection::Up)
1680 }
1681
1682 fn StepDown(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1684 self.step_up_or_down(cx, n, StepDirection::Down)
1685 }
1686
1687 fn WillValidate(&self, no_gc: &NoGC) -> bool {
1689 self.is_instance_validatable(no_gc)
1690 }
1691
1692 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
1694 self.validity_state(cx)
1695 }
1696
1697 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
1699 self.check_validity(cx)
1700 }
1701
1702 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
1704 self.report_validity(cx)
1705 }
1706
1707 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
1709 self.validation_message(cx)
1710 }
1711
1712 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
1714 self.validity_state(cx).set_custom_error_message(cx, error);
1715 }
1716}
1717
1718impl HTMLInputElement {
1719 pub(crate) fn form_datums(
1722 &self,
1723 submitter: Option<FormSubmitterElement>,
1724 encoding: Option<&'static Encoding>,
1725 ) -> (Vec<FormDatum>, bool) {
1726 let ty = self.Type();
1727 let name = self.Name();
1728 let is_submitter = match submitter {
1729 Some(FormSubmitterElement::Input(s)) => self == s,
1730 _ => false,
1731 };
1732
1733 match *self.input_type() {
1735 InputType::Submit(_) | InputType::Button(_) | InputType::Reset(_) if !is_submitter => {
1737 return (vec![], true);
1738 },
1739
1740 InputType::Radio(_) | InputType::Checkbox(_) if !self.Checked() => {
1742 return (vec![], true);
1743 },
1744 InputType::Image(_) => {
1745 if !is_submitter {
1747 return (vec![], true);
1748 }
1749 },
1750 _ => {
1752 if name.is_empty() {
1753 return (vec![], true);
1754 }
1755 },
1756 }
1757
1758 let datums = match *self.input_type() {
1759 InputType::Image(ref image_input_type) => {
1761 let prefix = if name.is_empty() {
1766 String::new()
1767 } else {
1768 format!("{name}.")
1769 };
1770
1771 let name_x: DOMString = format!("{prefix}x").into();
1774 let name_y: DOMString = format!("{prefix}y").into();
1775
1776 let coordinate = image_input_type.selected_coordinate();
1780
1781 vec![
1782 FormDatum {
1783 name: name_x,
1784 ty: ty.clone(),
1785 value: FormDatumValue::String(coordinate.x.to_string().into()),
1786 },
1787 FormDatum {
1788 name: name_y,
1789 ty,
1790 value: FormDatumValue::String(coordinate.y.to_string().into()),
1791 },
1792 ]
1793 },
1794 InputType::Checkbox(_) | InputType::Radio(_) => {
1796 let field_value = self.Value();
1798 let value = if field_value.is_empty() {
1799 DOMString::from_static("on")
1800 } else {
1801 field_value
1802 };
1803 vec![FormDatum {
1805 ty,
1806 name,
1807 value: FormDatumValue::String(value),
1808 }]
1809 },
1810
1811 InputType::File(_) => {
1813 let mut datums = vec![];
1814
1815 let name = self.Name();
1817
1818 match self.GetFiles() {
1819 None => {
1821 datums.push(FormDatum {
1822 ty,
1825 name,
1826 value: FormDatumValue::String(DOMString::new()),
1827 })
1828 },
1829 Some(fl) => {
1831 for f in fl.iter_files() {
1832 datums.push(FormDatum {
1833 ty: ty.clone(),
1834 name: name.clone(),
1835 value: FormDatumValue::File(DomRoot::from_ref(f)),
1836 });
1837 }
1838 },
1839 }
1840
1841 datums
1842 },
1843
1844 InputType::Hidden(_) if name.eq_ignore_ascii_case("_charset_") => {
1846 let charset = match encoding {
1848 None => DOMString::from_static("UTF-8"),
1849 Some(enc) => DOMString::from(enc.name()),
1850 };
1851 vec![FormDatum {
1853 ty,
1854 name,
1855 value: FormDatumValue::String(charset),
1856 }]
1857 },
1858
1859 _ => vec![FormDatum {
1861 ty,
1862 name,
1863 value: FormDatumValue::String(self.Value()),
1864 }],
1865 };
1866 (datums, false)
1867 }
1868
1869 pub(crate) fn radio_group_name(&self) -> Option<Atom> {
1871 self.upcast::<Element>()
1872 .get_name()
1873 .filter(|name| !name.is_empty())
1874 }
1875
1876 fn update_checkedness(&self, cx: &mut JSContext, checked: bool, dirty: bool) {
1877 let previously_checked = self.checkedness.replace(checked);
1878 self.update_checked_state(cx.no_gc());
1879
1880 if dirty {
1881 self.checked_changed.set(true);
1882 }
1883
1884 self.input_type()
1885 .as_specific()
1886 .update_checkedness(cx, self, checked, previously_checked);
1887
1888 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1889 }
1890
1891 fn update_checked_state(&self, no_gc: &NoGC) {
1893 let should_checked_state_apply = matches!(
1896 *self.input_type(),
1897 InputType::Checkbox(_) | InputType::Radio(_)
1898 ) && self.Checked();
1899 self.upcast::<Element>().set_state(
1900 no_gc,
1901 ElementState::CHECKED,
1902 should_checked_state_apply,
1903 );
1904 }
1905
1906 pub(crate) fn update_default_state(&self, is_default_button: bool, no_gc: &NoGC) {
1908 let is_default = self.is_submit_button() && is_default_button;
1912 let checked_attribute = matches!(
1913 *self.input_type(),
1914 InputType::Checkbox(_) | InputType::Radio(_)
1915 ) && self
1916 .upcast::<Element>()
1917 .has_attribute(&local_name!("checked"));
1918 self.upcast::<Element>().set_state(
1919 no_gc,
1920 ElementState::DEFAULT,
1921 is_default || checked_attribute,
1922 );
1923 }
1924
1925 fn update_default_state_indirectly(&self, no_gc: &NoGC) {
1927 match self.form_owner() {
1928 Some(form) => form.invalidate_default_button(no_gc),
1929 None => self.update_default_state(false, no_gc),
1930 }
1931 }
1932
1933 pub(crate) fn update_indeterminate_state(&self, no_gc: &NoGC) {
1935 let should_indeterminate_state_apply = match *self.input_type() {
1940 InputType::Checkbox(_) => self.Indeterminate(),
1941 InputType::Radio(_) => {
1942 let group = self.radio_group_name();
1943 let form = self.form_owner();
1944 let root = self
1945 .upcast::<Node>()
1946 .GetRootNode(&GetRootNodeOptions::empty());
1947 radio_group_iter(self, group.as_ref(), form.as_deref(), &root)
1948 .all(|elem| !elem.Checked())
1949 },
1950 _ => false,
1951 };
1952 self.upcast::<Element>().set_state(
1953 no_gc,
1954 ElementState::INDETERMINATE,
1955 should_indeterminate_state_apply,
1956 );
1957 }
1958
1959 pub(crate) fn is_mutable(&self) -> bool {
1961 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
1964 }
1965
1966 pub(crate) fn reset(&self, cx: &mut JSContext) {
1976 self.value_dirty.set(false);
1977
1978 let mut value = self.DefaultValue();
1980 self.sanitize_value(&mut value);
1981 self.text_input
1982 .safe_borrow_mut(cx.no_gc())
1983 .set_content(value);
1984
1985 let input_type = &*self.input_type();
1986 if matches!(input_type, InputType::Radio(_) | InputType::Checkbox(_)) {
1987 self.update_checkedness(cx, self.DefaultChecked(), false);
1988 self.checked_changed.set(false);
1989 }
1990
1991 if matches!(input_type, InputType::File(_)) {
1992 input_type
1993 .as_specific()
1994 .set_files(&FileList::new(cx, &self.owner_window(), vec![]));
1995 }
1996
1997 self.value_changed(cx);
1998 }
1999
2000 pub(crate) fn clear(&self, cx: &mut JSContext) {
2003 self.value_dirty.set(false);
2005 self.checked_changed.set(false);
2006 self.text_input
2008 .safe_borrow_mut(cx.no_gc())
2009 .set_content(DOMString::new());
2010 self.update_checkedness(cx, self.DefaultChecked(), false);
2012 if self.input_type().as_specific().get_files().is_some() {
2014 let window = self.owner_window();
2015 let filelist = FileList::new(cx, &window, vec![]);
2016 self.input_type().as_specific().set_files(&filelist);
2017 }
2018
2019 {
2022 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
2023 let mut value = text_input.get_content();
2024 self.sanitize_value(&mut value);
2025 text_input.set_content(value);
2026 }
2027
2028 self.value_changed(cx);
2029 }
2030
2031 fn update_placeholder_shown_state(&self, no_gc: &NoGC) {
2032 if !self.input_type().is_textual_or_password() {
2033 self.upcast::<Element>()
2034 .set_placeholder_shown_state(no_gc, false);
2035 } else {
2036 let has_placeholder = !self.placeholder.borrow().is_empty();
2037 let has_value = !self.text_input.borrow().is_empty();
2038 self.upcast::<Element>()
2039 .set_placeholder_shown_state(no_gc, has_placeholder && !has_value);
2040 }
2041 }
2042
2043 pub(crate) fn select_files_for_webdriver(
2044 &self,
2045 cx: &mut JSContext,
2046 test_paths: Vec<DOMString>,
2047 response_sender: GenericSender<Result<bool, ErrorStatus>>,
2048 ) {
2049 let mut stored_sender = self.pending_webdriver_response.borrow_mut();
2050 assert!(stored_sender.is_none());
2051
2052 *stored_sender = Some(Box::new(PendingWebDriverResponse {
2053 response_sender,
2054 expected_file_count: test_paths.len(),
2055 }));
2056
2057 self.input_type()
2058 .as_specific()
2059 .select_files(cx, self, Some(test_paths));
2060 }
2061
2062 pub(crate) fn take_pending_webdriver_response(&self) -> Option<Box<PendingWebDriverResponse>> {
2063 self.pending_webdriver_response.borrow_mut().take()
2064 }
2065
2066 fn sanitize_value(&self, value: &mut DOMString) {
2068 self.input_type().as_specific().sanitize_value(self, value);
2069 }
2070
2071 fn implicit_submission(&self, cx: &mut JSContext) {
2073 let doc = self.owner_document();
2074 let node = doc.upcast::<Node>();
2075 let owner = self.form_owner();
2076 let form = match owner {
2077 None => return,
2078 Some(ref f) => f,
2079 };
2080
2081 if self.upcast::<Element>().click_in_progress() {
2082 return;
2083 }
2084 match form.default_button() {
2085 Some(ref button) => {
2086 if button
2087 .as_maybe_activatable()
2088 .is_some_and(|button| button.is_instance_activatable())
2089 {
2090 button
2093 .upcast::<Node>()
2094 .fire_synthetic_pointer_event_not_trusted(cx, atom!("click"));
2095 }
2096 },
2097 None => {
2098 let mut inputs = node
2099 .traverse_preorder(ShadowIncluding::No)
2100 .filter_map(DomRoot::downcast::<HTMLInputElement>)
2101 .filter(|input| {
2102 input.form_owner() == owner &&
2103 matches!(
2104 *input.input_type(),
2105 InputType::Text(_) |
2106 InputType::Search(_) |
2107 InputType::Url(_) |
2108 InputType::Tel(_) |
2109 InputType::Email(_) |
2110 InputType::Password(_) |
2111 InputType::Date(_) |
2112 InputType::Month(_) |
2113 InputType::Week(_) |
2114 InputType::Time(_) |
2115 InputType::DatetimeLocal(_) |
2116 InputType::Number(_)
2117 )
2118 });
2119
2120 if inputs.nth(1).is_some() {
2121 return;
2123 }
2124
2125 form.submit(
2126 cx,
2127 SubmittedFrom::NotFromForm,
2128 FormSubmitterElement::Form(form),
2129 );
2130 },
2131 }
2132 }
2133
2134 pub(crate) fn convert_string_to_number(&self, value: &str) -> Option<f64> {
2136 self.input_type()
2137 .as_specific()
2138 .convert_string_to_number(value)
2139 }
2140
2141 fn convert_number_to_string(&self, value: f64) -> Option<DOMString> {
2143 self.input_type()
2144 .as_specific()
2145 .convert_number_to_string(value)
2146 }
2147
2148 fn update_related_validity_states(&self, cx: &mut JSContext) {
2149 match *self.input_type() {
2150 InputType::Radio(_) => {
2151 perform_radio_group_validation(cx, self, self.radio_group_name().as_ref())
2152 },
2153 _ => {
2154 self.validity_state(cx)
2155 .perform_validation_and_update(cx, ValidationFlags::all());
2156 },
2157 }
2158 }
2159
2160 fn value_changed(&self, cx: &mut JSContext) {
2161 self.maybe_update_shared_selection();
2162 self.update_related_validity_states(cx);
2163 if self.upcast::<Node>().is_connected() {
2164 self.input_type().as_specific().update_shadow_tree(cx, self);
2165 }
2166 }
2167
2168 pub(crate) fn fire_value_change_events(&self, cx: &mut JSContext, events: ValueChangeEvents) {
2173 let target = self.upcast::<EventTarget>();
2174
2175 if events.contains(ValueChangeEvents::Input) {
2178 target.fire_event_with_params(
2179 cx,
2180 atom!("input"),
2181 EventBubbles::Bubbles,
2182 EventCancelable::NotCancelable,
2183 EventComposed::Composed,
2184 );
2185 }
2186
2187 if events.contains(ValueChangeEvents::Change) {
2190 target.fire_bubbling_event(cx, atom!("change"));
2191 }
2192 }
2193
2194 pub(crate) fn show_the_picker_if_applicable(&self, cx: &mut JSContext) {
2196 if !self.is_mutable() {
2200 return;
2201 }
2202
2203 self.input_type()
2206 .as_specific()
2207 .show_the_picker_if_applicable(cx, self);
2208 }
2209
2210 pub(crate) fn handle_color_picker_response(
2211 &self,
2212 cx: &mut JSContext,
2213 response: Option<RgbColor>,
2214 ) {
2215 if let InputType::Color(ref color_input_type) = *self.input_type() {
2216 color_input_type.handle_color_picker_response(cx, self, response)
2217 }
2218 }
2219
2220 pub(crate) fn handle_file_picker_response(
2221 &self,
2222 cx: &mut JSContext,
2223 response: Option<Vec<SelectedFile>>,
2224 ) {
2225 if let InputType::File(ref file_input_type) = *self.input_type() {
2226 file_input_type.handle_file_picker_response(cx, self, response)
2227 }
2228 }
2229
2230 fn handle_focus_event(&self, cx: &mut JSContext, event: &FocusEvent) {
2231 let event_type = event.upcast::<Event>().type_();
2232 let document = self.owner_document();
2233
2234 if *event_type == *"blur" && !matches!(&*self.input_type(), InputType::File(..)) {
2238 document
2239 .embedder_controls()
2240 .hide_embedder_control(self.upcast());
2241 } else if *event_type == *"focus" {
2242 let input_type = &*self.input_type();
2243 let Ok(input_method_type) = input_type.try_into() else {
2244 return;
2245 };
2246
2247 if self.is_textual_or_password() &&
2252 let Some(selection) = document.selection()
2253 {
2254 let _ = selection.Collapse(cx, None, 0);
2255 }
2256
2257 document.embedder_controls().show_embedder_control(
2258 cx,
2259 ControlElement::Ime(Dom::from_ref(self.upcast())),
2260 EmbedderControlRequest::InputMethod(InputMethodRequest {
2261 input_method_type,
2262 text: String::from(self.Value()),
2263 insertion_point: self.GetSelectionEnd(),
2264 multiline: false,
2265 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
2267 }),
2268 None,
2269 );
2270 }
2271 }
2272}
2273
2274impl VirtualMethods for HTMLInputElement {
2275 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2276 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2277 }
2278
2279 fn attribute_mutated(
2280 &self,
2281 cx: &mut JSContext,
2282 attr: AttrRef<'_>,
2283 mutation: AttributeMutation,
2284 ) {
2285 let could_have_had_embedder_control = self.may_have_embedder_control();
2286
2287 self.super_type()
2288 .unwrap()
2289 .attribute_mutated(cx, attr, mutation);
2290
2291 match *attr.local_name() {
2292 local_name!("disabled") => {
2293 let element = self.upcast::<Element>();
2294 if self.input_type().is_textual() {
2295 let read_write = !(self.ReadOnly() || element.disabled_state());
2296 element.set_read_write_state(cx.no_gc(), read_write);
2297 }
2298 },
2299 local_name!("checked") => {
2300 if !self.checked_changed.get() {
2301 let checked_state = match mutation {
2302 AttributeMutation::Set(None) => true,
2303 AttributeMutation::Set(Some(_)) => {
2304 return;
2306 },
2307 AttributeMutation::Removed => false,
2308 };
2309 self.update_checkedness(cx, checked_state, false);
2310 }
2311 self.update_default_state_indirectly(cx.no_gc());
2312 },
2313 local_name!("min") | local_name!("max") => {
2314 self.validity_state(cx)
2315 .perform_validation_and_update(cx, ValidationFlags::all());
2316 },
2317 local_name!("size") => {
2318 let size = mutation.new_value(attr).map(|value| value.as_uint());
2319 self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
2320 },
2321 local_name!("type") => {
2322 let element = self.upcast::<Element>();
2323
2324 match mutation {
2325 AttributeMutation::Set(previous_value) => {
2326 if previous_value
2330 .is_some_and(|previous_value| **previous_value == **attr.value())
2331 {
2332 return;
2333 }
2334
2335 let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
2336 let previously_selectable = self.selection_api_applies();
2337
2338 *self.input_type.safe_borrow_mut(cx.no_gc()) =
2339 InputType::new_from_atom(attr.value().as_atom());
2340 self.is_textual_or_password
2341 .set(self.input_type().is_textual_or_password());
2342
2343 if self.input_type().is_textual() {
2344 let read_write = !(self.ReadOnly() || element.disabled_state());
2345 element.set_read_write_state(cx.no_gc(), read_write);
2346 } else {
2347 element.set_read_write_state(cx.no_gc(), false);
2348 }
2349
2350 let new_value_mode = self.value_mode();
2351 match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
2352 (&ValueMode::Value, false, ValueMode::Default) |
2354 (&ValueMode::Value, false, ValueMode::DefaultOn) => {
2355 self.SetValue(cx, old_idl_value)
2356 .expect("Failed to set input value on type change to a default ValueMode.");
2357 },
2358
2359 (_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
2361 self.SetValue(
2362 cx,
2363 self.upcast::<Element>()
2364 .get_attribute_string_value(&local_name!("value"))
2365 .unwrap_or_default()
2366 .into(),
2367 )
2368 .expect(
2369 "Failed to set input value on type change to ValueMode::Value.",
2370 );
2371 self.value_dirty.set(false);
2372 },
2373
2374 (_, _, ValueMode::Filename)
2376 if old_value_mode != ValueMode::Filename =>
2377 {
2378 self.SetValue(cx, DOMString::new())
2379 .expect("Failed to set input value on type change to ValueMode::Filename.");
2380 },
2381 _ => {},
2382 }
2383
2384 self.input_type().as_specific().signal_type_change(cx, self);
2386
2387 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
2389 let mut value = text_input.get_content();
2390 self.sanitize_value(&mut value);
2391 text_input.set_content(value);
2392 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
2393
2394 if self.does_minmaxlength_apply() {
2396 text_input
2397 .set_min_length(self.MinLength().to_u32().map(Utf16CodeUnits));
2398 text_input
2399 .set_max_length(self.MaxLength().to_u32().map(Utf16CodeUnits));
2400 } else {
2401 text_input.set_min_length(None);
2402 text_input.set_max_length(None);
2403 }
2404
2405 if !previously_selectable && self.selection_api_applies() {
2407 text_input.clear_selection_to_start();
2408 }
2409 },
2410 AttributeMutation::Removed => {
2411 self.input_type().as_specific().signal_type_change(cx, self);
2412 *self.input_type.safe_borrow_mut(cx.no_gc()) = InputType::new_text();
2413 self.is_textual_or_password
2414 .set(self.input_type().is_textual_or_password());
2415
2416 let element = self.upcast::<Element>();
2417 let read_write = !(self.ReadOnly() || element.disabled_state());
2418 element.set_read_write_state(cx.no_gc(), read_write);
2419 },
2420 }
2421
2422 self.update_placeholder_shown_state(cx.no_gc());
2423 self.input_type()
2424 .as_specific()
2425 .update_placeholder_contents(cx, self);
2426
2427 self.validity_state(cx)
2428 .perform_validation_and_update(cx, ValidationFlags::all());
2429 self.update_checked_state(cx.no_gc());
2430 self.update_indeterminate_state(cx.no_gc());
2431 self.update_default_state_indirectly(cx.no_gc());
2432 if element.focus_state() {
2433 element.set_focusring_state(cx.no_gc(), self.is_textual_or_password());
2434 }
2435 self.update_required_and_optional_state(cx.no_gc());
2436 },
2437 local_name!("value") if !self.value_dirty.get() => {
2438 let value = mutation.new_value(attr).map(|value| (**value).to_owned());
2442 let mut value = value.map_or(DOMString::new(), DOMString::from);
2443
2444 self.sanitize_value(&mut value);
2445 self.text_input
2446 .safe_borrow_mut(cx.no_gc())
2447 .set_content(value);
2448 self.update_placeholder_shown_state(cx.no_gc());
2449 },
2450 local_name!("maxlength") if self.does_minmaxlength_apply() => match *attr.value() {
2451 AttrValue::Int(_, value) => {
2452 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
2453
2454 if value < 0 {
2455 text_input.set_max_length(None);
2456 } else {
2457 text_input.set_max_length(Some(Utf16CodeUnits(value as u32)))
2458 }
2459 },
2460 _ => panic!("Expected an AttrValue::Int"),
2461 },
2462 local_name!("minlength") if self.does_minmaxlength_apply() => match *attr.value() {
2463 AttrValue::Int(_, value) => {
2464 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
2465
2466 if value < 0 {
2467 text_input.set_min_length(None);
2468 } else {
2469 text_input.set_min_length(Some(Utf16CodeUnits(value as u32)))
2470 }
2471 },
2472 _ => panic!("Expected an AttrValue::Int"),
2473 },
2474 local_name!("placeholder") => {
2475 {
2476 let mut placeholder = self.placeholder.safe_borrow_mut(cx.no_gc());
2477 placeholder.clear();
2478 if let AttributeMutation::Set(_) = mutation {
2479 placeholder
2480 .extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
2481 }
2482 }
2483 self.update_placeholder_shown_state(cx.no_gc());
2484 self.input_type()
2485 .as_specific()
2486 .update_placeholder_contents(cx, self);
2487 },
2488 local_name!("readonly") => {
2489 if self.input_type().is_textual() {
2490 let el = self.upcast::<Element>();
2491 match mutation {
2492 AttributeMutation::Set(_) => {
2493 el.set_read_write_state(cx.no_gc(), false);
2494 },
2495 AttributeMutation::Removed => {
2496 el.set_read_write_state(cx.no_gc(), !el.disabled_state());
2497 },
2498 }
2499 }
2500 },
2501 local_name!("form") => {
2502 self.form_attribute_mutated(cx, mutation);
2503 },
2504 local_name!("required") => {
2505 self.update_required_and_optional_state(cx.no_gc());
2506 },
2507 _ => {
2508 self.input_type()
2509 .as_specific()
2510 .attribute_mutated(cx, self, attr, mutation);
2511 },
2512 }
2513
2514 self.value_changed(cx);
2515
2516 if could_have_had_embedder_control && !self.may_have_embedder_control() {
2517 self.owner_document()
2518 .embedder_controls()
2519 .hide_embedder_control(self.upcast());
2520 }
2521 }
2522
2523 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2524 match *name {
2525 local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
2526 local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
2527 local_name!("type") => AttrValue::from_atomic(value.into()),
2528 local_name!("maxlength") => {
2529 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
2530 },
2531 local_name!("minlength") => {
2532 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
2533 },
2534 local_name!("width") |
2535 local_name!("height") |
2536 local_name!("hspace") |
2537 local_name!("vspace") => AttrValue::from_dimension(value.into()),
2538 _ => self
2539 .super_type()
2540 .unwrap()
2541 .parse_plain_attribute(name, value),
2542 }
2543 }
2544
2545 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2546 if let Some(super_type) = self.super_type() {
2547 super_type.bind_to_tree(cx, context);
2548 }
2549 self.input_type()
2550 .as_specific()
2551 .run_shared_bind_to_tree_or_moving_steps(cx, self);
2552 self.value_changed(cx);
2553 }
2554
2555 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2556 if let Some(super_type) = self.super_type() {
2557 super_type.moving_steps(cx, context);
2558 }
2559 self.input_type()
2560 .as_specific()
2561 .run_shared_bind_to_tree_or_moving_steps(cx, self);
2562 }
2563
2564 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
2565 self.owner_document_unrooted(cx.no_gc())
2567 .embedder_controls()
2568 .hide_embedder_control(self.upcast());
2569
2570 let form_owner = self.form_owner();
2571 self.super_type().unwrap().unbind_from_tree(cx, context);
2572
2573 self.input_type()
2574 .as_specific()
2575 .unbind_from_tree(cx, self, form_owner, context);
2576
2577 self.validity_state(cx)
2578 .perform_validation_and_update(cx, ValidationFlags::all());
2579 }
2580
2581 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
2587 if let Some(event) = event.downcast::<FocusEvent>() {
2588 self.handle_focus_event(cx, event)
2589 }
2590
2591 let value_change_events = self
2592 .input_type()
2593 .as_specific()
2594 .handle_event(cx, self, event);
2595
2596 self.value_changed(cx);
2597
2598 self.fire_value_change_events(cx, value_change_events);
2599
2600 if let Some(super_type) = self.super_type() {
2601 super_type.handle_event(cx, event);
2602 }
2603 }
2604
2605 fn cloning_steps(
2607 &self,
2608 cx: &mut JSContext,
2609 copy: &Node,
2610 maybe_doc: Option<&Document>,
2611 clone_children: CloneChildrenFlag,
2612 ) {
2613 if let Some(s) = self.super_type() {
2614 s.cloning_steps(cx, copy, maybe_doc, clone_children);
2615 }
2616 let elem = copy.downcast::<HTMLInputElement>().unwrap();
2617 elem.value_dirty.set(self.value_dirty.get());
2618 elem.update_checkedness(cx, self.Checked(), false);
2619 elem.checked_changed.set(self.checked_changed.get());
2620 elem.SetIndeterminate(cx, self.Indeterminate());
2623 elem.text_input
2624 .safe_borrow_mut(cx.no_gc())
2625 .clone_content_from(&self.text_input.borrow());
2626 elem.value_changed(cx);
2627 }
2628
2629 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2630 match attr.local_name() {
2631 &local_name!("width") |
2632 &local_name!("height") |
2633 &local_name!("hspace") |
2634 &local_name!("vspace") => {
2635 matches!(*self.input_type(), InputType::Image(_))
2636 },
2637 _ => self
2638 .super_type()
2639 .unwrap()
2640 .attribute_affects_presentational_hints(attr),
2641 }
2642 }
2643}
2644
2645impl FormControl for HTMLInputElement {
2646 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2647 self.form_owner.get()
2648 }
2649
2650 fn set_form_owner(&self, cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2651 self.form_owner.set(form);
2652 self.input_type().as_specific().form_owner_changed(cx, self);
2653 }
2654
2655 fn to_html_element(&self) -> &HTMLElement {
2656 self.upcast::<HTMLElement>()
2657 }
2658}
2659
2660impl Validatable for HTMLInputElement {
2661 fn as_element(&self) -> &Element {
2662 self.upcast()
2663 }
2664
2665 fn has_range_limitations(&self) -> bool {
2667 self.does_value_as_number_apply() && (self.minimum().is_some() || self.maximum().is_some())
2668 }
2669
2670 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
2671 self.validity_state
2672 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
2673 }
2674
2675 fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
2676 match *self.input_type() {
2683 InputType::Hidden(_) | InputType::Button(_) | InputType::Reset(_) => false,
2684 _ => {
2685 !(self.upcast::<Element>().disabled_state() ||
2686 self.ReadOnly() ||
2687 is_barred_by_datalist_ancestor(no_gc, self.upcast()))
2688 },
2689 }
2690 }
2691
2692 fn perform_validation(
2693 &self,
2694 cx: &mut JSContext,
2695 validate_flags: ValidationFlags,
2696 ) -> ValidationFlags {
2697 let mut failed_flags = ValidationFlags::empty();
2698 let value = self.Value();
2699
2700 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
2701 self.suffers_from_being_missing(&value)
2702 {
2703 failed_flags.insert(ValidationFlags::VALUE_MISSING);
2704 }
2705
2706 if validate_flags.contains(ValidationFlags::TYPE_MISMATCH) &&
2707 self.suffers_from_type_mismatch(&value)
2708 {
2709 failed_flags.insert(ValidationFlags::TYPE_MISMATCH);
2710 }
2711
2712 if validate_flags.contains(ValidationFlags::PATTERN_MISMATCH) &&
2713 self.suffers_from_pattern_mismatch(cx, &value)
2714 {
2715 failed_flags.insert(ValidationFlags::PATTERN_MISMATCH);
2716 }
2717
2718 if validate_flags.contains(ValidationFlags::BAD_INPUT) &&
2719 self.suffers_from_bad_input(&value)
2720 {
2721 failed_flags.insert(ValidationFlags::BAD_INPUT);
2722 }
2723
2724 if validate_flags.intersects(ValidationFlags::TOO_LONG | ValidationFlags::TOO_SHORT) {
2725 failed_flags |= self.suffers_from_length_issues(&value);
2726 }
2727
2728 if validate_flags.intersects(
2729 ValidationFlags::RANGE_UNDERFLOW |
2730 ValidationFlags::RANGE_OVERFLOW |
2731 ValidationFlags::STEP_MISMATCH,
2732 ) {
2733 failed_flags |= self.suffers_from_range_issues(&value);
2734 }
2735
2736 failed_flags & validate_flags
2737 }
2738}
2739
2740impl Activatable for HTMLInputElement {
2741 fn as_element(&self) -> &Element {
2742 self.upcast()
2743 }
2744
2745 fn is_instance_activatable(&self) -> bool {
2747 match *self.input_type() {
2750 InputType::Checkbox(_) | InputType::Radio(_) => true,
2751 InputType::Hidden(_) => false,
2755 _ => self.is_mutable(),
2756 }
2757 }
2758
2759 fn legacy_pre_activation_behavior(&self, cx: &mut JSContext) -> Option<InputActivationState> {
2761 let activation_state = self
2762 .input_type()
2763 .as_specific()
2764 .legacy_pre_activation_behavior(cx, self);
2765
2766 if activation_state.is_some() {
2767 self.value_changed(cx);
2768 }
2769
2770 activation_state
2771 }
2772
2773 fn legacy_canceled_activation_behavior(
2775 &self,
2776 cx: &mut JSContext,
2777 cache: Option<InputActivationState>,
2778 ) {
2779 let ty = self.input_type();
2781 let cache = match cache {
2782 Some(cache) => {
2783 if (cache.was_radio && !matches!(*ty, InputType::Radio(_))) ||
2784 (cache.was_checkbox && !matches!(*ty, InputType::Checkbox(_)))
2785 {
2786 return;
2789 }
2790 cache
2791 },
2792 None => {
2793 return;
2794 },
2795 };
2796
2797 ty.as_specific()
2799 .legacy_canceled_activation_behavior(cx, self, cache);
2800
2801 self.value_changed(cx);
2802 }
2803
2804 fn activation_behavior(&self, cx: &mut JSContext, event: &Event, target: &EventTarget) {
2806 if !self.is_instance_activatable() {
2809 return;
2810 }
2811
2812 let input_activation_type = {
2814 let input_type = self.input_type();
2815 InputActivationType::new_from_input_type(&input_type)
2816 };
2817
2818 if let Some(input_activation_type) = input_activation_type {
2819 input_activation_type
2820 .as_specific()
2821 .activation_behavior(cx, self, event, target);
2822 }
2823
2824 }
2832}
2833
2834fn compile_pattern(cx: &mut JSContext, pattern_str: &str, out_regex: MutableHandleObject) -> bool {
2838 if check_js_regex_syntax(cx, pattern_str) {
2840 let pattern_str = format!("^(?:{})$", pattern_str);
2842 let flags = RegExpFlags {
2843 flags_: RegExpFlag_UnicodeSets,
2844 };
2845 new_js_regex(cx, &pattern_str, flags, out_regex)
2846 } else {
2847 false
2848 }
2849}
2850
2851#[expect(unsafe_code)]
2852fn check_js_regex_syntax(cx: &mut JSContext, pattern: &str) -> bool {
2855 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2856 rooted!(&in(cx) let mut exception = UndefinedValue());
2857
2858 let valid = unsafe {
2859 CheckRegExpSyntax(
2860 cx,
2861 pattern.as_ptr(),
2862 pattern.len(),
2863 RegExpFlags {
2864 flags_: RegExpFlag_UnicodeSets,
2865 },
2866 exception.handle_mut(),
2867 )
2868 };
2869
2870 if !valid {
2871 unsafe { JS_ClearPendingException(cx) };
2872 return false;
2873 }
2874
2875 exception.is_undefined()
2878}
2879
2880#[expect(unsafe_code)]
2881fn new_js_regex(
2882 cx: &mut JSContext,
2883 pattern: &str,
2884 flags: RegExpFlags,
2885 mut out_regex: MutableHandleObject,
2886) -> bool {
2887 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2888 out_regex.set(unsafe { NewUCRegExpObject(cx, pattern.as_ptr(), pattern.len(), flags) });
2889
2890 if out_regex.is_null() {
2891 unsafe { JS_ClearPendingException(cx) };
2892 return false;
2893 }
2894 true
2895}
2896
2897#[expect(unsafe_code)]
2898fn matches_js_regex(cx: &mut JSContext, regex_obj: HandleObject, value: &str) -> Result<bool, ()> {
2899 let mut value: Vec<u16> = value.encode_utf16().collect();
2900
2901 let mut is_regex = false;
2902 assert!(unsafe { ObjectIsRegExp(cx, regex_obj, &mut is_regex) });
2903 assert!(is_regex);
2904
2905 rooted!(&in(cx) let mut rval = UndefinedValue());
2906 let mut index = 0;
2907
2908 let ok = unsafe {
2909 ExecuteRegExpNoStatics(
2910 cx,
2911 regex_obj,
2912 value.as_mut_ptr(),
2913 value.len(),
2914 &mut index,
2915 true,
2916 rval.handle_mut(),
2917 )
2918 };
2919
2920 if ok {
2921 Ok(!rval.is_null())
2922 } else {
2923 unsafe { JS_ClearPendingException(cx) };
2924 Err(())
2925 }
2926}
2927
2928#[derive(MallocSizeOf)]
2932pub(crate) struct PendingWebDriverResponse {
2933 response_sender: GenericSender<Result<bool, ErrorStatus>>,
2935 expected_file_count: usize,
2937}
2938
2939impl PendingWebDriverResponse {
2940 pub(crate) fn finish(self, number_files_selected: usize) {
2941 if number_files_selected == self.expected_file_count {
2942 let _ = self.response_sender.send(Ok(false));
2943 } else {
2944 let _ = self.response_sender.send(Err(ErrorStatus::InvalidArgument));
2947 }
2948 }
2949}