1use std::cell::{Cell, RefCell, RefMut};
6use std::{f64, ptr};
7
8use dom_struct::dom_struct;
9use embedder_traits::{EmbedderControlRequest, InputMethodRequest, RgbColor, SelectedFile};
10use encoding_rs::Encoding;
11use html5ever::{LocalName, Prefix, local_name};
12use js::context::{JSContext, NoGC};
13use js::jsapi::{ClippedTime, JSObject, RegExpFlag_UnicodeSets, RegExpFlags};
14use js::jsval::UndefinedValue;
15use js::rust::wrappers2::{
16 CheckRegExpSyntax, DateGetMsecSinceEpoch, ExecuteRegExpNoStatics, JS_ClearPendingException,
17 NewDateObject, NewUCRegExpObject, ObjectIsDate, ObjectIsRegExp,
18};
19use js::rust::{HandleObject, MutableHandleObject};
20use num_traits::ToPrimitive;
21use script_bindings::cell::{DomRefCell, Ref};
22use script_bindings::codegen::GenericBindings::SelectionBinding::SelectionMethods;
23use script_bindings::domstring::parse_floating_point_number;
24use servo_base::generic_channel::GenericSender;
25use servo_base::text::{RangeAny, Utf16CodeUnits, Utf32CodeUnits};
26use style::attr::{AttrValue, LengthOrPercentageOrAuto};
27use style::str::split_commas;
28use stylo_atoms::Atom;
29use stylo_dom::ElementState;
30use time::OffsetDateTime;
31use unicode_bidi::{BidiClass, bidi_class};
32use webdriver::error::ErrorStatus;
33use xml5ever::ns;
34
35use crate::dom::activation::Activatable;
36use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
37use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
38use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
39use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
40use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
41use crate::dom::bindings::error::{Error, ErrorResult};
42use crate::dom::bindings::inheritance::Castable;
43use crate::dom::bindings::refcounted::Trusted;
44use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
45use crate::dom::bindings::str::{DOMString, USVString};
46use crate::dom::document::Document;
47use crate::dom::document_embedder_controls::ControlElement;
48use crate::dom::element::attributes::storage::AttrRef;
49use crate::dom::element::{AttributeMutation, Element};
50use crate::dom::event::Event;
51use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
52use crate::dom::eventtarget::EventTarget;
53use crate::dom::filelist::FileList;
54use crate::dom::html::form_controls::input_type::radio_input_type::perform_radio_group_validation;
55use crate::dom::html::form_controls::input_type::{
56 InputActivationType, InputType, ValueChangeEvents,
57};
58use crate::dom::html::form_controls::text_control::TextControlElement;
59use crate::dom::html::form_controls::text_input::{KeyReaction, Lines, TextInput};
60use crate::dom::html::htmldatalistelement::HTMLDataListElement;
61use crate::dom::html::htmlelement::HTMLElement;
62use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
63use crate::dom::html::htmlformelement::{
64 FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, SubmittedFrom,
65};
66use crate::dom::input_type::radio_input_type::radio_group_iter;
67use crate::dom::iterators::ShadowIncluding;
68use crate::dom::node::virtualmethods::VirtualMethods;
69use crate::dom::node::{
70 BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
71};
72use crate::dom::nodelist::NodeList;
73use crate::dom::text_input::EmbedderClipboardProvider;
74use crate::dom::types::FocusEvent;
75use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
76use crate::dom::validitystate::{ValidationFlags, ValidityState};
77use crate::realms::enter_auto_realm;
78
79#[derive(Debug, PartialEq)]
80pub(crate) enum ValueMode {
81 Value,
83
84 Default,
86
87 DefaultOn,
89
90 Filename,
92}
93
94#[derive(Debug, PartialEq)]
95enum StepDirection {
96 Up,
97 Down,
98}
99
100#[dom_struct]
101pub(crate) struct HTMLInputElement {
102 htmlelement: HTMLElement,
103 input_type: DomRefCell<InputType>,
104
105 is_textual_or_password: Cell<bool>,
108
109 placeholder: DomRefCell<DOMString>,
110 size: Cell<u32>,
111 maxlength: Cell<i32>,
112 minlength: Cell<i32>,
113 checkedness: Cell<bool>,
117 checked_changed: Cell<bool>,
119 indeterminateness: Cell<bool>,
125 #[no_trace]
126 text_input: DomRefCell<TextInput<EmbedderClipboardProvider>>,
127 form_owner: MutNullableDom<HTMLFormElement>,
128 labels_node_list: MutNullableDom<NodeList>,
129 validity_state: MutNullableDom<ValidityState>,
130 #[no_trace]
131 pending_webdriver_response: RefCell<Option<PendingWebDriverResponse>>,
132 value_dirty: Cell<bool>,
134
135 has_scheduled_selectionchange_event: Cell<bool>,
137}
138
139#[derive(JSTraceable)]
140pub(crate) struct InputActivationState {
141 pub(crate) indeterminate: bool,
142 pub(crate) checked: bool,
143 pub(crate) checked_radio: Option<DomRoot<HTMLInputElement>>,
144 pub(crate) was_radio: bool,
145 pub(crate) was_checkbox: bool,
146 }
148
149static DEFAULT_INPUT_SIZE: u32 = 20;
150static DEFAULT_MAX_LENGTH: i32 = -1;
151static DEFAULT_MIN_LENGTH: i32 = -1;
152
153#[expect(non_snake_case)]
154impl HTMLInputElement {
155 fn new_inherited(
156 local_name: LocalName,
157 prefix: Option<Prefix>,
158 document: &Document,
159 ) -> HTMLInputElement {
160 let embedder_sender = document
161 .window()
162 .as_global_scope()
163 .script_to_embedder_chan()
164 .clone();
165 HTMLInputElement {
166 htmlelement: HTMLElement::new_inherited_with_state(
167 ElementState::ENABLED | ElementState::READWRITE,
168 local_name,
169 prefix,
170 document,
171 ),
172 input_type: DomRefCell::new(InputType::new_text()),
173 is_textual_or_password: Cell::new(true),
174 placeholder: DomRefCell::new(DOMString::new()),
175 checkedness: Cell::new(false),
176 checked_changed: Cell::new(false),
177 indeterminateness: Cell::new(false),
178 maxlength: Cell::new(DEFAULT_MAX_LENGTH),
179 minlength: Cell::new(DEFAULT_MIN_LENGTH),
180 size: Cell::new(DEFAULT_INPUT_SIZE),
181 text_input: DomRefCell::new(TextInput::new(
182 Lines::Single,
183 DOMString::new(),
184 EmbedderClipboardProvider {
185 embedder_sender,
186 webview_id: document.webview_id(),
187 },
188 )),
189 value_dirty: Cell::new(false),
190 form_owner: Default::default(),
191 labels_node_list: MutNullableDom::new(None),
192 validity_state: Default::default(),
193 pending_webdriver_response: Default::default(),
194 has_scheduled_selectionchange_event: Default::default(),
195 }
196 }
197
198 pub(crate) fn new(
199 cx: &mut JSContext,
200 local_name: LocalName,
201 prefix: Option<Prefix>,
202 document: &Document,
203 proto: Option<HandleObject>,
204 ) -> DomRoot<HTMLInputElement> {
205 Node::reflect_node_with_proto(
206 cx,
207 Box::new(HTMLInputElement::new_inherited(
208 local_name, prefix, document,
209 )),
210 document,
211 proto,
212 )
213 }
214
215 pub(crate) fn auto_directionality(&self) -> Option<String> {
216 match *self.input_type() {
217 InputType::Text(_) | InputType::Search(_) | InputType::Url(_) | InputType::Email(_) => {
218 let value: String = String::from(self.Value());
219 Some(HTMLInputElement::directionality_from_value(&value))
220 },
221 _ => None,
222 }
223 }
224
225 pub(crate) fn directionality_from_value(value: &str) -> String {
226 if HTMLInputElement::is_first_strong_character_rtl(value) {
227 "rtl".to_owned()
228 } else {
229 "ltr".to_owned()
230 }
231 }
232
233 fn is_first_strong_character_rtl(value: &str) -> bool {
234 for ch in value.chars() {
235 return match bidi_class(ch) {
236 BidiClass::L => false,
237 BidiClass::AL => true,
238 BidiClass::R => true,
239 _ => continue,
240 };
241 }
242 false
243 }
244
245 pub(crate) fn value_mode(&self) -> ValueMode {
248 match *self.input_type() {
249 InputType::Submit(_) |
250 InputType::Reset(_) |
251 InputType::Button(_) |
252 InputType::Image(_) |
253 InputType::Hidden(_) => ValueMode::Default,
254
255 InputType::Checkbox(_) | InputType::Radio(_) => ValueMode::DefaultOn,
256
257 InputType::Color(_) |
258 InputType::Date(_) |
259 InputType::DatetimeLocal(_) |
260 InputType::Email(_) |
261 InputType::Month(_) |
262 InputType::Number(_) |
263 InputType::Password(_) |
264 InputType::Range(_) |
265 InputType::Search(_) |
266 InputType::Tel(_) |
267 InputType::Text(_) |
268 InputType::Time(_) |
269 InputType::Url(_) |
270 InputType::Week(_) => ValueMode::Value,
271
272 InputType::File(_) => ValueMode::Filename,
273 }
274 }
275
276 #[inline]
277 pub(crate) fn input_type(&self) -> Ref<'_, InputType> {
278 self.input_type.borrow()
279 }
280
281 pub(crate) fn is_nontypeable(&self) -> bool {
283 matches!(
284 *self.input_type(),
285 InputType::Button(_) |
286 InputType::Checkbox(_) |
287 InputType::Color(_) |
288 InputType::File(_) |
289 InputType::Hidden(_) |
290 InputType::Image(_) |
291 InputType::Radio(_) |
292 InputType::Range(_) |
293 InputType::Reset(_) |
294 InputType::Submit(_)
295 )
296 }
297
298 #[inline]
299 pub(crate) fn is_submit_button(&self) -> bool {
300 matches!(
301 *self.input_type(),
302 InputType::Submit(_) | InputType::Image(_)
303 )
304 }
305
306 pub(crate) fn is_auto_directionality_form_associated_element(&self) -> bool {
308 matches!(
309 *self.input_type(),
310 InputType::Hidden(_) |
311 InputType::Text(_) |
312 InputType::Search(_) |
313 InputType::Tel(_) |
314 InputType::Url(_) |
315 InputType::Email(_) |
316 InputType::Password(_) |
317 InputType::Submit(_) |
318 InputType::Reset(_) |
319 InputType::Button(_)
320 )
321 }
322
323 fn does_minmaxlength_apply(&self) -> bool {
324 matches!(
325 *self.input_type(),
326 InputType::Text(_) |
327 InputType::Search(_) |
328 InputType::Url(_) |
329 InputType::Tel(_) |
330 InputType::Email(_) |
331 InputType::Password(_)
332 )
333 }
334
335 fn does_pattern_apply(&self) -> bool {
336 matches!(
337 *self.input_type(),
338 InputType::Text(_) |
339 InputType::Search(_) |
340 InputType::Url(_) |
341 InputType::Tel(_) |
342 InputType::Email(_) |
343 InputType::Password(_)
344 )
345 }
346
347 fn does_multiple_apply(&self) -> bool {
348 matches!(*self.input_type(), InputType::Email(_))
349 }
350
351 fn does_value_as_number_apply(&self) -> bool {
354 matches!(
355 *self.input_type(),
356 InputType::Date(_) |
357 InputType::Month(_) |
358 InputType::Week(_) |
359 InputType::Time(_) |
360 InputType::DatetimeLocal(_) |
361 InputType::Number(_) |
362 InputType::Range(_)
363 )
364 }
365
366 fn does_value_as_date_apply(&self) -> bool {
367 matches!(
368 *self.input_type(),
369 InputType::Date(_) | InputType::Month(_) | InputType::Week(_) | InputType::Time(_)
370 )
371 }
372
373 pub(crate) fn allowed_value_step(&self) -> Option<f64> {
375 let default_step = self.default_step()?;
378
379 let Some(step_value) = self
382 .upcast::<Element>()
383 .get_attribute_string_value(&local_name!("step"))
384 else {
385 return Some(default_step * self.step_scale_factor());
386 };
387
388 if step_value.eq_ignore_ascii_case("any") {
391 return None;
392 }
393
394 let Some(parsed_value) =
398 parse_floating_point_number(&step_value).filter(|value| *value > 0.0)
399 else {
400 return Some(default_step * self.step_scale_factor());
401 };
402
403 Some(parsed_value * self.step_scale_factor())
407 }
408
409 pub(crate) fn minimum(&self) -> Option<f64> {
411 self.upcast::<Element>()
412 .get_attribute_string_value(&local_name!("min"))
413 .and_then(|value| self.convert_string_to_number(&value))
414 .or_else(|| self.default_minimum())
415 }
416
417 pub(crate) fn maximum(&self) -> Option<f64> {
419 self.upcast::<Element>()
420 .get_attribute_string_value(&local_name!("max"))
421 .and_then(|value| self.convert_string_to_number(&value))
422 .or_else(|| self.default_maximum())
423 }
424
425 pub(crate) fn stepped_minimum(&self) -> Option<f64> {
428 match (self.minimum(), self.allowed_value_step()) {
429 (Some(min), Some(allowed_step)) => {
430 let step_base = self.step_base();
431 let nsteps = (min - step_base) / allowed_step;
433 Some(step_base + (allowed_step * nsteps.ceil()))
435 },
436 (_, _) => None,
437 }
438 }
439
440 pub(crate) fn stepped_maximum(&self) -> Option<f64> {
443 match (self.maximum(), self.allowed_value_step()) {
444 (Some(max), Some(allowed_step)) => {
445 let step_base = self.step_base();
446 let nsteps = (max - step_base) / allowed_step;
448 Some(step_base + (allowed_step * nsteps.floor()))
450 },
451 (_, _) => None,
452 }
453 }
454
455 fn default_minimum(&self) -> Option<f64> {
457 match *self.input_type() {
458 InputType::Range(_) => Some(0.0),
459 _ => None,
460 }
461 }
462
463 fn default_maximum(&self) -> Option<f64> {
465 match *self.input_type() {
466 InputType::Range(_) => Some(100.0),
467 _ => None,
468 }
469 }
470
471 pub(crate) fn default_range_value(&self) -> f64 {
473 let min = self.minimum().unwrap_or(0.0);
474 let max = self.maximum().unwrap_or(100.0);
475 if max < min {
476 min
477 } else {
478 min + (max - min) * 0.5
479 }
480 }
481
482 fn default_step(&self) -> Option<f64> {
484 match *self.input_type() {
485 InputType::Date(_) => Some(1.0),
486 InputType::Month(_) => Some(1.0),
487 InputType::Week(_) => Some(1.0),
488 InputType::Time(_) => Some(60.0),
489 InputType::DatetimeLocal(_) => Some(60.0),
490 InputType::Number(_) => Some(1.0),
491 InputType::Range(_) => Some(1.0),
492 _ => None,
493 }
494 }
495
496 fn step_scale_factor(&self) -> f64 {
498 match *self.input_type() {
499 InputType::Date(_) => 86400000.0,
500 InputType::Month(_) => 1.0,
501 InputType::Week(_) => 604800000.0,
502 InputType::Time(_) => 1000.0,
503 InputType::DatetimeLocal(_) => 1000.0,
504 InputType::Number(_) => 1.0,
505 InputType::Range(_) => 1.0,
506 _ => unreachable!(),
507 }
508 }
509
510 pub(crate) fn step_base(&self) -> f64 {
512 if let Some(minimum) = self
516 .upcast::<Element>()
517 .get_attribute_string_value(&local_name!("min"))
518 .and_then(|value| self.convert_string_to_number(&value))
519 {
520 return minimum;
521 }
522
523 if let Some(value) = self
527 .upcast::<Element>()
528 .get_attribute_string_value(&local_name!("value"))
529 .and_then(|value| self.convert_string_to_number(&value))
530 {
531 return value;
532 }
533
534 if let Some(default_step_base) = self.default_step_base() {
536 return default_step_base;
537 }
538
539 0.0
541 }
542
543 fn default_step_base(&self) -> Option<f64> {
545 match *self.input_type() {
546 InputType::Week(_) => Some(-259200000.0),
547 _ => None,
548 }
549 }
550
551 fn step_up_or_down(&self, cx: &mut JSContext, n: i32, dir: StepDirection) -> ErrorResult {
555 if !self.does_value_as_number_apply() {
558 return Err(Error::InvalidState(Some(
559 "Input element does not implement `stepDown()` or `stepUp()`".into(),
560 )));
561 }
562 let step_base = self.step_base();
563
564 let Some(allowed_value_step) = self.allowed_value_step() else {
566 return Err(Error::InvalidState(Some(
567 "Input element does not have a value step".into(),
568 )));
569 };
570
571 let minimum = self.minimum();
574 let maximum = self.maximum();
575 if let (Some(min), Some(max)) = (minimum, maximum) {
576 if min > max {
577 return Ok(());
578 }
579
580 if let Some(stepped_minimum) = self.stepped_minimum() &&
584 stepped_minimum > max
585 {
586 return Ok(());
587 }
588 }
589
590 let mut value: f64 = self
594 .convert_string_to_number(&self.Value().str())
595 .unwrap_or(0.0);
596
597 let valueBeforeStepping = value;
599
600 if (value - step_base) % allowed_value_step != 0.0 {
605 value = match dir {
606 StepDirection::Down =>
607 {
609 let intervals_from_base = ((value - step_base) / allowed_value_step).floor();
610 intervals_from_base * allowed_value_step + step_base
611 },
612 StepDirection::Up =>
613 {
615 let intervals_from_base = ((value - step_base) / allowed_value_step).ceil();
616 intervals_from_base * allowed_value_step + step_base
617 },
618 };
619 }
620 else {
622 value += match dir {
627 StepDirection::Down => -f64::from(n) * allowed_value_step,
628 StepDirection::Up => f64::from(n) * allowed_value_step,
629 };
630 }
631
632 if let Some(min) = minimum &&
636 value < min
637 {
638 value = self.stepped_minimum().unwrap_or(value);
639 }
640
641 if let Some(max) = maximum &&
645 value > max
646 {
647 value = self.stepped_maximum().unwrap_or(value);
648 }
649
650 match dir {
654 StepDirection::Down => {
655 if value > valueBeforeStepping {
656 return Ok(());
657 }
658 },
659 StepDirection::Up => {
660 if value < valueBeforeStepping {
661 return Ok(());
662 }
663 },
664 }
665
666 self.SetValueAsNumber(cx, value)
670 }
671
672 fn suggestions_source_element(&self) -> Option<DomRoot<HTMLDataListElement>> {
674 let list_string = self
675 .upcast::<Element>()
676 .get_string_attribute(&local_name!("list"));
677 if list_string.is_empty() {
678 return None;
679 }
680 let ancestor = self
681 .upcast::<Node>()
682 .GetRootNode(&GetRootNodeOptions::empty());
683 let first_with_id = &ancestor
684 .traverse_preorder(ShadowIncluding::No)
685 .find(|node| {
686 node.downcast::<Element>()
687 .is_some_and(|e| e.Id() == list_string)
688 });
689 first_with_id
690 .as_ref()
691 .and_then(|el| el.downcast::<HTMLDataListElement>())
692 .map(DomRoot::from_ref)
693 }
694
695 fn suffers_from_being_missing(&self, value: &DOMString) -> bool {
697 self.input_type()
698 .as_specific()
699 .suffers_from_being_missing(self, value)
700 }
701
702 fn suffers_from_type_mismatch(&self, value: &DOMString) -> bool {
704 if value.is_empty() {
705 return false;
706 }
707
708 self.input_type()
709 .as_specific()
710 .suffers_from_type_mismatch(self, value)
711 }
712
713 fn suffers_from_pattern_mismatch(&self, cx: &mut JSContext, value: &DOMString) -> bool {
715 let pattern_str = self.Pattern();
718 if value.is_empty() || pattern_str.is_empty() || !self.does_pattern_apply() {
719 return false;
720 }
721
722 let mut realm = enter_auto_realm(cx, self);
723 let cx = &mut realm;
724
725 rooted!(&in(cx) let mut pattern = ptr::null_mut::<JSObject>());
727 if compile_pattern(cx, &pattern_str.str(), pattern.handle_mut()) {
728 if self.Multiple() && self.does_multiple_apply() {
729 !split_commas(&value.str())
730 .all(|s| matches_js_regex(cx, pattern.handle(), s).unwrap_or(true))
731 } else {
732 !matches_js_regex(cx, pattern.handle(), &value.str()).unwrap_or(true)
733 }
734 } else {
735 false
737 }
738 }
739
740 fn suffers_from_bad_input(&self, value: &DOMString) -> bool {
742 if value.is_empty() {
743 return false;
744 }
745
746 self.input_type()
747 .as_specific()
748 .suffers_from_bad_input(value)
749 }
750
751 fn suffers_from_length_issues(&self, value: &DOMString) -> ValidationFlags {
754 let value_dirty = self.value_dirty.get();
757 let text_input = self.text_input.borrow();
758 let edit_by_user = !text_input.was_last_change_by_set_content();
759
760 if value.is_empty() || !value_dirty || !edit_by_user || !self.does_minmaxlength_apply() {
761 return ValidationFlags::empty();
762 }
763
764 let mut failed_flags = ValidationFlags::empty();
765 let Utf16CodeUnits(value_len) = text_input.len_utf16();
766 let min_length = self.MinLength();
767 let max_length = self.MaxLength();
768
769 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as u32) {
770 failed_flags.insert(ValidationFlags::TOO_SHORT);
771 }
772
773 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as u32) {
774 failed_flags.insert(ValidationFlags::TOO_LONG);
775 }
776
777 failed_flags
778 }
779
780 fn suffers_from_range_issues(&self, value: &DOMString) -> ValidationFlags {
784 if value.is_empty() || !self.does_value_as_number_apply() {
785 return ValidationFlags::empty();
786 }
787
788 let Some(value_as_number) = self.convert_string_to_number(&value.str()) else {
789 return ValidationFlags::empty();
790 };
791
792 let mut failed_flags = ValidationFlags::empty();
793 let min_value = self.minimum();
794 let max_value = self.maximum();
795
796 let has_reversed_range = match (min_value, max_value) {
798 (Some(min), Some(max)) => self.input_type().has_periodic_domain() && min > max,
799 _ => false,
800 };
801
802 if has_reversed_range {
803 if value_as_number > max_value.unwrap() && value_as_number < min_value.unwrap() {
805 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
806 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
807 }
808 } else {
809 if let Some(min_value) = min_value &&
811 value_as_number < min_value
812 {
813 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
814 }
815 if let Some(max_value) = max_value &&
817 value_as_number > max_value
818 {
819 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
820 }
821 }
822
823 if let Some(step) = self.allowed_value_step() {
825 let diff = (self.step_base() - value_as_number) % step / value_as_number;
829 if diff.abs() > 1e-12 {
830 failed_flags.insert(ValidationFlags::STEP_MISMATCH);
831 }
832 }
833
834 failed_flags
835 }
836
837 pub(crate) fn is_textual_or_password(&self) -> bool {
839 self.is_textual_or_password.get()
840 }
841
842 fn may_have_embedder_control(&self) -> bool {
843 let el = self.upcast::<Element>();
844 matches!(*self.input_type(), InputType::Color(_)) && !el.disabled_state()
845 }
846
847 pub(crate) fn value_for_shadow_dom(&self) -> DOMString {
849 let input_type = &*self.input_type();
850 match input_type {
851 InputType::Checkbox(_) |
852 InputType::Radio(_) |
853 InputType::Image(_) |
854 InputType::Hidden(_) |
855 InputType::Range(_) => input_type.as_specific().value_for_shadow_dom(self),
856 _ => {
857 if let Some(attribute_value) = self
858 .upcast::<Element>()
859 .get_attribute_string_value(&local_name!("value"))
860 {
861 return attribute_value.into();
862 }
863 input_type.as_specific().value_for_shadow_dom(self)
864 },
865 }
866 }
867
868 fn schedule_a_selection_change_event(&self) {
870 if self.has_scheduled_selectionchange_event.get() {
872 return;
873 }
874 self.has_scheduled_selectionchange_event.set(true);
876 let this = Trusted::new(self);
878 self.owner_global()
879 .task_manager()
880 .user_interaction_task_source()
881 .queue(
882 task!(selectionchange_task_steps: move |cx| {
884 let this = this.root();
885 this.has_scheduled_selectionchange_event.set(false);
887 this.upcast::<EventTarget>().fire_event_with_params(
889 cx,
890 atom!("selectionchange"),
891 EventBubbles::Bubbles,
892 EventCancelable::NotCancelable,
893 EventComposed::Composed,
894 );
895 }),
900 );
901 }
902}
903
904impl<'dom> LayoutDom<'dom, HTMLInputElement> {
905 pub(crate) fn size_for_layout(self) -> u32 {
915 self.unsafe_get().size.get()
916 }
917
918 pub(crate) fn selection_for_layout(self) -> Option<RangeAny<Utf32CodeUnits>> {
919 let element = self.unsafe_get();
920 if !element.is_textual_or_password.get() {
921 return None;
922 }
923 #[expect(unsafe_code)]
924 let text_input = unsafe { element.text_input.borrow_for_layout() };
925 text_input.selection_for_layout
926 }
927
928 pub(crate) fn width(self) -> LengthOrPercentageOrAuto {
929 self.image_button_dimension(&local_name!("width"))
930 }
931
932 pub(crate) fn height(self) -> LengthOrPercentageOrAuto {
933 self.image_button_dimension(&local_name!("height"))
934 }
935
936 fn image_button_dimension(self, name: &LocalName) -> LengthOrPercentageOrAuto {
937 let element = self.upcast::<Element>();
938 let is_image_button = element
939 .get_attr_val_for_layout(&ns!(), &local_name!("type"))
940 .is_some_and(|value| value.eq_ignore_ascii_case("image"));
941
942 if !is_image_button {
943 return LengthOrPercentageOrAuto::Auto;
944 }
945
946 element
947 .get_attr_for_layout(&ns!(), name)
948 .map(AttrValue::as_dimension)
949 .cloned()
950 .unwrap_or(LengthOrPercentageOrAuto::Auto)
951 }
952}
953
954impl TextControlElement for HTMLInputElement {
955 fn as_element(&self) -> &Element {
956 self.upcast()
957 }
958
959 fn text_input(&self) -> Ref<'_, TextInput<EmbedderClipboardProvider>> {
960 self.text_input.borrow()
961 }
962
963 fn text_input_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
964 self.text_input.borrow_mut()
965 }
966
967 fn selection_api_applies(&self) -> bool {
969 matches!(
970 *self.input_type(),
971 InputType::Text(_) |
972 InputType::Search(_) |
973 InputType::Url(_) |
974 InputType::Tel(_) |
975 InputType::Password(_)
976 )
977 }
978
979 fn has_selectable_text(&self) -> bool {
987 self.is_textual_or_password() && !self.text_input.borrow().get_content().is_empty()
988 }
989
990 fn has_uncollapsed_selection(&self) -> bool {
991 self.text_input.borrow().has_uncollapsed_selection()
992 }
993
994 fn set_dirty_value_flag(&self, value: bool) {
995 self.value_dirty.set(value)
996 }
997
998 fn select_all(&self) {
999 self.text_input.borrow_mut().select_all();
1000 self.maybe_update_shared_selection();
1001 }
1002
1003 fn maybe_update_shared_selection(&self) {
1004 let selection = {
1005 let mut text_input = self.text_input.borrow_mut();
1006 let selection_range = text_input.selection_start()..text_input.selection_end();
1007 let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
1008
1009 let range_remained_equal = selection_range == text_input.previous_selection_range;
1010 if range_remained_equal && enabled == text_input.selection_for_layout.is_some() {
1011 return;
1012 }
1013
1014 if !range_remained_equal {
1015 self.schedule_a_selection_change_event();
1020 }
1021
1022 let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range());
1023 text_input.previous_selection_range = selection_range;
1024 text_input.selection_for_layout = selection;
1025 selection
1026 };
1027
1028 if let Some(text_input_widget) = self.input_type.borrow().as_specific().text_input_widget() &&
1033 text_input_widget.borrow().set_text_run_selection(selection)
1034 {
1035 self.owner_window().layout().set_needs_new_display_list();
1036 }
1037 }
1038
1039 fn is_password_field(&self) -> bool {
1040 matches!(*self.input_type(), InputType::Password(_))
1041 }
1042
1043 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
1044 self.placeholder.borrow()
1045 }
1046
1047 fn value_text(&self) -> DOMString {
1048 self.Value()
1049 }
1050
1051 fn read_only_or_disabled(&self) -> bool {
1052 self.ReadOnly() || self.Disabled()
1053 }
1054
1055 fn handle_text_content_changed(&self, cx: &mut JSContext) {
1056 self.update_placeholder_shown_state();
1057 self.upcast::<Node>()
1058 .dirty(cx.no_gc(), NodeDamage::ContentOrHeritage);
1059 }
1060
1061 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction) {
1062 match action {
1063 KeyReaction::TriggerDefaultAction => {
1064 self.implicit_submission(cx);
1065 },
1066 KeyReaction::DispatchInput(text, is_composing, input_type) => {
1067 self.queue_input_event(text, is_composing, input_type);
1068 self.value_dirty.set(true);
1069 self.update_placeholder_shown_state();
1070 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1071 self.maybe_update_shared_selection();
1072 },
1073 KeyReaction::RedrawSelection => {
1074 self.maybe_update_shared_selection();
1075 },
1076 KeyReaction::Nothing => (),
1077 }
1078 }
1079}
1080
1081impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
1082 make_getter!(Accept, "accept");
1084
1085 make_setter!(SetAccept, "accept");
1087
1088 make_bool_getter!(Alpha, "alpha");
1090
1091 make_bool_setter!(SetAlpha, "alpha");
1093
1094 make_getter!(Alt, "alt");
1096
1097 make_setter!(SetAlt, "alt");
1099
1100 make_getter!(DirName, "dirname");
1102
1103 make_setter!(SetDirName, "dirname");
1105
1106 make_bool_getter!(Disabled, "disabled");
1108
1109 make_bool_setter!(SetDisabled, "disabled");
1111
1112 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
1114 self.form_owner()
1115 }
1116
1117 fn GetFiles(&self) -> Option<DomRoot<FileList>> {
1119 self.input_type()
1120 .as_specific()
1121 .get_files()
1122 .as_ref()
1123 .cloned()
1124 }
1125
1126 fn SetFiles(&self, _cx: &mut JSContext, files: Option<&FileList>) {
1128 if let Some(files) = files {
1129 self.input_type().as_specific().set_files(files)
1130 }
1131 }
1132
1133 make_bool_getter!(DefaultChecked, "checked");
1135
1136 make_bool_setter!(SetDefaultChecked, "checked");
1138
1139 fn Checked(&self) -> bool {
1141 self.checkedness.get()
1142 }
1143
1144 fn SetChecked(&self, cx: &mut JSContext, checked: bool) {
1146 self.update_checkedness(cx, checked, true);
1147 self.value_changed(cx);
1148 }
1149
1150 make_enumerated_getter!(
1152 ColorSpace,
1153 "colorspace",
1154 "limited-srgb" | "display-p3",
1155 missing => "limited-srgb",
1156 invalid => "limited-srgb"
1157 );
1158
1159 make_setter!(SetColorSpace, "colorspace");
1161
1162 make_bool_getter!(ReadOnly, "readonly");
1164
1165 make_bool_setter!(SetReadOnly, "readonly");
1167
1168 make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE);
1170
1171 make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
1173
1174 fn Type(&self) -> DOMString {
1176 DOMString::from(self.input_type().as_str())
1177 }
1178
1179 make_atomic_setter!(SetType, "type");
1181
1182 fn Value(&self) -> DOMString {
1184 match self.value_mode() {
1185 ValueMode::Value => self.text_input.borrow().get_content(),
1186 ValueMode::Default => self
1187 .upcast::<Element>()
1188 .get_attribute_string_value(&local_name!("value"))
1189 .map(|value| value.into())
1190 .unwrap_or_default(),
1191 ValueMode::DefaultOn => self
1192 .upcast::<Element>()
1193 .get_attribute_string_value(&local_name!("value"))
1194 .map(|value| value.into())
1195 .unwrap_or(DOMString::from_static("on")),
1196 ValueMode::Filename => {
1197 let mut path = DOMString::new();
1198 match self.input_type().as_specific().get_files() {
1199 Some(ref fl) => match fl.Item(0) {
1200 Some(ref f) => {
1201 path.push_str("C:\\fakepath\\");
1202 path.push_str(&f.name().str());
1203 path
1204 },
1205 None => path,
1206 },
1207 None => path,
1208 }
1209 },
1210 }
1211 }
1212
1213 fn SetValue(&self, cx: &mut JSContext, mut value: DOMString) -> ErrorResult {
1215 match self.value_mode() {
1216 ValueMode::Value => {
1217 {
1218 self.value_dirty.set(true);
1220
1221 self.sanitize_value(&mut value);
1224
1225 let mut text_input = self.text_input.borrow_mut();
1226
1227 if text_input.get_content() != value {
1232 text_input.set_content(value);
1234
1235 text_input.clear_selection_to_end();
1236 }
1237 }
1238
1239 self.update_placeholder_shown_state();
1243 self.maybe_update_shared_selection();
1244 },
1245 ValueMode::Default | ValueMode::DefaultOn => {
1246 self.upcast::<Element>()
1247 .set_string_attribute(cx, &local_name!("value"), value);
1248 },
1249 ValueMode::Filename => {
1250 if value.is_empty() {
1251 let window = self.owner_window();
1252 let fl = FileList::new(cx, &window, vec![]);
1253 self.input_type().as_specific().set_files(&fl)
1254 } else {
1255 return Err(Error::InvalidState(Some(
1256 "Non-empty value provided for filename".into(),
1257 )));
1258 }
1259 },
1260 }
1261
1262 self.value_changed(cx);
1263 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1264 Ok(())
1265 }
1266
1267 make_getter!(DefaultValue, "value");
1269
1270 make_setter!(SetDefaultValue, "value");
1272
1273 make_getter!(Min, "min");
1275
1276 make_setter!(SetMin, "min");
1278
1279 fn GetList(&self) -> Option<DomRoot<HTMLDataListElement>> {
1281 self.suggestions_source_element()
1282 }
1283
1284 #[expect(unsafe_code)]
1286 fn GetValueAsDate(&self, cx: &mut JSContext, mut return_value: MutableHandleObject) {
1287 if let Some(date_time) = self
1288 .input_type()
1289 .as_specific()
1290 .convert_string_to_naive_datetime(self.Value())
1291 {
1292 let time = ClippedTime {
1293 t: (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64,
1294 };
1295 return_value.set(unsafe { NewDateObject(cx, time) });
1296 }
1297 }
1298
1299 #[expect(unsafe_code)]
1301 fn SetValueAsDate(&self, cx: &mut JSContext, value: *mut JSObject) -> ErrorResult {
1302 rooted!(&in(cx) let value = value);
1303 if !self.does_value_as_date_apply() {
1304 return Err(Error::InvalidState(Some(
1305 "Input element cannot be treated as a date".into(),
1306 )));
1307 }
1308 if value.is_null() {
1309 return self.SetValue(cx, DOMString::new());
1310 }
1311 let mut msecs: f64 = 0.0;
1312 unsafe {
1316 let mut is_date = false;
1317 if !ObjectIsDate(cx, value.handle(), &mut is_date) {
1318 return Err(Error::JSFailed);
1319 }
1320 if !is_date {
1321 return Err(Error::Type(c"Value was not a date".to_owned()));
1322 }
1323 if !DateGetMsecSinceEpoch(cx, value.handle(), &mut msecs) {
1324 return Err(Error::JSFailed);
1325 }
1326 if !msecs.is_finite() {
1327 return self.SetValue(cx, DOMString::new());
1328 }
1329 }
1330
1331 let Ok(date_time) = OffsetDateTime::from_unix_timestamp_nanos((msecs * 1e6) as i128) else {
1332 return self.SetValue(cx, DOMString::new());
1333 };
1334 self.SetValue(
1335 cx,
1336 self.input_type()
1337 .as_specific()
1338 .convert_datetime_to_dom_string(date_time),
1339 )
1340 }
1341
1342 fn ValueAsNumber(&self) -> f64 {
1344 self.convert_string_to_number(&self.Value().str())
1345 .unwrap_or(f64::NAN)
1346 }
1347
1348 fn SetValueAsNumber(&self, cx: &mut JSContext, value: f64) -> ErrorResult {
1350 if value.is_infinite() {
1351 Err(Error::Type(c"value is not finite".to_owned()))
1352 } else if !self.does_value_as_number_apply() {
1353 Err(Error::InvalidState(Some(
1354 "Input element value cannot be treated as a number".into(),
1355 )))
1356 } else if value.is_nan() {
1357 self.SetValue(cx, DOMString::new())
1358 } else if let Some(converted) = self.convert_number_to_string(value) {
1359 self.SetValue(cx, converted)
1360 } else {
1361 self.SetValue(cx, DOMString::new())
1366 }
1367 }
1368
1369 make_getter!(Name, "name");
1371
1372 make_atomic_setter!(SetName, "name");
1374
1375 make_getter!(Placeholder, "placeholder");
1377
1378 make_setter!(SetPlaceholder, "placeholder");
1380
1381 make_form_action_getter!(FormAction, "formaction");
1383
1384 make_setter!(SetFormAction, "formaction");
1386
1387 make_enumerated_getter!(
1389 FormEnctype,
1390 "formenctype",
1391 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
1392 invalid => "application/x-www-form-urlencoded"
1393 );
1394
1395 make_setter!(SetFormEnctype, "formenctype");
1397
1398 make_enumerated_getter!(
1400 FormMethod,
1401 "formmethod",
1402 "get" | "post" | "dialog",
1403 invalid => "get"
1404 );
1405
1406 make_setter!(SetFormMethod, "formmethod");
1408
1409 make_getter!(FormTarget, "formtarget");
1411
1412 make_setter!(SetFormTarget, "formtarget");
1414
1415 make_bool_getter!(FormNoValidate, "formnovalidate");
1417
1418 make_bool_setter!(SetFormNoValidate, "formnovalidate");
1420
1421 make_getter!(Max, "max");
1423
1424 make_setter!(SetMax, "max");
1426
1427 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1429
1430 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1432
1433 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
1435
1436 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
1438
1439 make_bool_getter!(Multiple, "multiple");
1441
1442 make_bool_setter!(SetMultiple, "multiple");
1444
1445 make_getter!(Pattern, "pattern");
1447
1448 make_setter!(SetPattern, "pattern");
1450
1451 make_bool_getter!(Required, "required");
1453
1454 make_bool_setter!(SetRequired, "required");
1456
1457 make_url_getter!(Src, "src");
1459
1460 make_url_setter!(SetSrc, "src");
1462
1463 make_getter!(Step, "step");
1465
1466 make_setter!(SetStep, "step");
1468
1469 make_getter!(UseMap, "usemap");
1471
1472 make_setter!(SetUseMap, "usemap");
1474
1475 fn Indeterminate(&self) -> bool {
1477 self.indeterminateness.get()
1478 }
1479
1480 fn SetIndeterminate(&self, _cx: &mut JSContext, val: bool) {
1482 self.indeterminateness.set(val);
1483 self.update_indeterminate_state();
1484 }
1485
1486 fn GetLabels(&self, cx: &mut JSContext) -> Option<DomRoot<NodeList>> {
1490 if matches!(*self.input_type(), InputType::Hidden(_)) {
1491 None
1492 } else {
1493 Some(self.labels_node_list.or_init(|| {
1494 NodeList::new_labels_list(
1495 cx,
1496 self.upcast::<Node>().owner_doc().window(),
1497 self.upcast::<HTMLElement>(),
1498 )
1499 }))
1500 }
1501 }
1502
1503 fn Select(&self, cx: &mut JSContext) {
1505 self.dom_select(cx);
1506 }
1507
1508 fn GetSelectionStart(&self) -> Option<u32> {
1510 self.dom_start().map(|start| start.0)
1511 }
1512
1513 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
1515 self.set_dom_start(start.map(Utf16CodeUnits::from))
1516 }
1517
1518 fn GetSelectionEnd(&self) -> Option<u32> {
1520 self.dom_end().map(|end| end.0)
1521 }
1522
1523 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
1525 self.set_dom_end(end.map(Utf16CodeUnits::from))
1526 }
1527
1528 fn GetSelectionDirection(&self) -> Option<DOMString> {
1530 self.dom_direction()
1531 }
1532
1533 fn SetSelectionDirection(
1535 &self,
1536 _cx: &mut JSContext,
1537 direction: Option<DOMString>,
1538 ) -> ErrorResult {
1539 self.set_dom_direction(direction)
1540 }
1541
1542 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
1544 self.set_dom_range(
1545 Utf16CodeUnits::from(start),
1546 Utf16CodeUnits::from(end),
1547 direction,
1548 )
1549 }
1550
1551 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
1553 self.set_dom_range_text(replacement, None, None, Default::default())
1554 }
1555
1556 fn SetRangeText_(
1558 &self,
1559 replacement: DOMString,
1560 start: u32,
1561 end: u32,
1562 selection_mode: SelectionMode,
1563 ) -> ErrorResult {
1564 self.set_dom_range_text(
1565 replacement,
1566 Some(Utf16CodeUnits::from(start)),
1567 Some(Utf16CodeUnits::from(end)),
1568 selection_mode,
1569 )
1570 }
1571
1572 fn SelectFiles(&self, paths: Vec<DOMString>) {
1575 self.input_type()
1576 .as_specific()
1577 .select_files(self, Some(paths));
1578 }
1579
1580 fn StepUp(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1582 self.step_up_or_down(cx, n, StepDirection::Up)
1583 }
1584
1585 fn StepDown(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1587 self.step_up_or_down(cx, n, StepDirection::Down)
1588 }
1589
1590 fn WillValidate(&self, no_gc: &NoGC) -> bool {
1592 self.is_instance_validatable(no_gc)
1593 }
1594
1595 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
1597 self.validity_state(cx)
1598 }
1599
1600 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
1602 self.check_validity(cx)
1603 }
1604
1605 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
1607 self.report_validity(cx)
1608 }
1609
1610 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
1612 self.validation_message(cx)
1613 }
1614
1615 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
1617 self.validity_state(cx).set_custom_error_message(cx, error);
1618 }
1619}
1620
1621impl HTMLInputElement {
1622 pub(crate) fn form_datums(
1625 &self,
1626 submitter: Option<FormSubmitterElement>,
1627 encoding: Option<&'static Encoding>,
1628 ) -> (Vec<FormDatum>, bool) {
1629 let ty = self.Type();
1630 let name = self.Name();
1631 let is_submitter = match submitter {
1632 Some(FormSubmitterElement::Input(s)) => self == s,
1633 _ => false,
1634 };
1635
1636 match *self.input_type() {
1638 InputType::Submit(_) | InputType::Button(_) | InputType::Reset(_) if !is_submitter => {
1640 return (vec![], true);
1641 },
1642
1643 InputType::Radio(_) | InputType::Checkbox(_) if !self.Checked() => {
1645 return (vec![], true);
1646 },
1647 InputType::Image(_) => {
1648 if !is_submitter {
1650 return (vec![], true);
1651 }
1652 },
1653 _ => {
1655 if name.is_empty() {
1656 return (vec![], true);
1657 }
1658 },
1659 }
1660
1661 let datums = match *self.input_type() {
1662 InputType::Image(ref image_input_type) => {
1664 let prefix = if name.is_empty() {
1669 String::new()
1670 } else {
1671 format!("{name}.")
1672 };
1673
1674 let name_x: DOMString = format!("{prefix}x").into();
1677 let name_y: DOMString = format!("{prefix}y").into();
1678
1679 let coordinate = image_input_type.selected_coordinate();
1683
1684 vec![
1685 FormDatum {
1686 name: name_x,
1687 ty: ty.clone(),
1688 value: FormDatumValue::String(coordinate.x.to_string().into()),
1689 },
1690 FormDatum {
1691 name: name_y,
1692 ty,
1693 value: FormDatumValue::String(coordinate.y.to_string().into()),
1694 },
1695 ]
1696 },
1697 InputType::Checkbox(_) | InputType::Radio(_) => {
1699 let field_value = self.Value();
1701 let value = if field_value.is_empty() {
1702 DOMString::from_static("on")
1703 } else {
1704 field_value
1705 };
1706 vec![FormDatum {
1708 ty,
1709 name,
1710 value: FormDatumValue::String(value),
1711 }]
1712 },
1713
1714 InputType::File(_) => {
1716 let mut datums = vec![];
1717
1718 let name = self.Name();
1720
1721 match self.GetFiles() {
1722 None => {
1724 datums.push(FormDatum {
1725 ty,
1728 name,
1729 value: FormDatumValue::String(DOMString::new()),
1730 })
1731 },
1732 Some(fl) => {
1734 for f in fl.iter_files() {
1735 datums.push(FormDatum {
1736 ty: ty.clone(),
1737 name: name.clone(),
1738 value: FormDatumValue::File(DomRoot::from_ref(f)),
1739 });
1740 }
1741 },
1742 }
1743
1744 datums
1745 },
1746
1747 InputType::Hidden(_) if name.eq_ignore_ascii_case("_charset_") => {
1749 let charset = match encoding {
1751 None => DOMString::from_static("UTF-8"),
1752 Some(enc) => DOMString::from(enc.name()),
1753 };
1754 vec![FormDatum {
1756 ty,
1757 name,
1758 value: FormDatumValue::String(charset),
1759 }]
1760 },
1761
1762 _ => vec![FormDatum {
1764 ty,
1765 name,
1766 value: FormDatumValue::String(self.Value()),
1767 }],
1768 };
1769 (datums, false)
1770 }
1771
1772 pub(crate) fn radio_group_name(&self) -> Option<Atom> {
1774 self.upcast::<Element>()
1775 .get_name()
1776 .filter(|name| !name.is_empty())
1777 }
1778
1779 fn update_checkedness(&self, cx: &mut JSContext, checked: bool, dirty: bool) {
1780 let previously_checked = self.checkedness.replace(checked);
1781 self.update_checked_state();
1782
1783 if dirty {
1784 self.checked_changed.set(true);
1785 }
1786
1787 self.input_type()
1788 .as_specific()
1789 .update_checkedness(cx, self, checked, previously_checked);
1790
1791 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1792 }
1793
1794 fn update_checked_state(&self) {
1796 let should_checked_state_apply = matches!(
1799 *self.input_type(),
1800 InputType::Checkbox(_) | InputType::Radio(_)
1801 ) && self.Checked();
1802 self.upcast::<Element>()
1803 .set_state(ElementState::CHECKED, should_checked_state_apply);
1804 }
1805
1806 pub(crate) fn update_indeterminate_state(&self) {
1808 let should_indeterminate_state_apply = match *self.input_type() {
1813 InputType::Checkbox(_) => self.Indeterminate(),
1814 InputType::Radio(_) => {
1815 let group = self.radio_group_name();
1816 let form = self.form_owner();
1817 let root = self
1818 .upcast::<Node>()
1819 .GetRootNode(&GetRootNodeOptions::empty());
1820 radio_group_iter(self, group.as_ref(), form.as_deref(), &root)
1821 .all(|elem| !elem.Checked())
1822 },
1823 _ => false,
1824 };
1825 self.upcast::<Element>().set_state(
1826 ElementState::INDETERMINATE,
1827 should_indeterminate_state_apply,
1828 );
1829 }
1830
1831 pub(crate) fn is_mutable(&self) -> bool {
1833 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
1836 }
1837
1838 pub(crate) fn reset(&self, cx: &mut JSContext) {
1848 self.value_dirty.set(false);
1849
1850 let mut value = self.DefaultValue();
1852 self.sanitize_value(&mut value);
1853 self.text_input.borrow_mut().set_content(value);
1854
1855 let input_type = &*self.input_type();
1856 if matches!(input_type, InputType::Radio(_) | InputType::Checkbox(_)) {
1857 self.update_checkedness(cx, self.DefaultChecked(), false);
1858 self.checked_changed.set(false);
1859 }
1860
1861 if matches!(input_type, InputType::File(_)) {
1862 input_type
1863 .as_specific()
1864 .set_files(&FileList::new(cx, &self.owner_window(), vec![]));
1865 }
1866
1867 self.value_changed(cx);
1868 }
1869
1870 pub(crate) fn clear(&self, cx: &mut JSContext) {
1873 self.value_dirty.set(false);
1875 self.checked_changed.set(false);
1876 self.text_input.borrow_mut().set_content(DOMString::new());
1878 self.update_checkedness(cx, self.DefaultChecked(), false);
1880 if self.input_type().as_specific().get_files().is_some() {
1882 let window = self.owner_window();
1883 let filelist = FileList::new(cx, &window, vec![]);
1884 self.input_type().as_specific().set_files(&filelist);
1885 }
1886
1887 {
1890 let mut text_input = self.text_input.borrow_mut();
1891 let mut value = text_input.get_content();
1892 self.sanitize_value(&mut value);
1893 text_input.set_content(value);
1894 }
1895
1896 self.value_changed(cx);
1897 }
1898
1899 fn update_placeholder_shown_state(&self) {
1900 if !self.input_type().is_textual_or_password() {
1901 self.upcast::<Element>().set_placeholder_shown_state(false);
1902 } else {
1903 let has_placeholder = !self.placeholder.borrow().is_empty();
1904 let has_value = !self.text_input.borrow().is_empty();
1905 self.upcast::<Element>()
1906 .set_placeholder_shown_state(has_placeholder && !has_value);
1907 }
1908 }
1909
1910 pub(crate) fn select_files_for_webdriver(
1911 &self,
1912 test_paths: Vec<DOMString>,
1913 response_sender: GenericSender<Result<bool, ErrorStatus>>,
1914 ) {
1915 let mut stored_sender = self.pending_webdriver_response.borrow_mut();
1916 assert!(stored_sender.is_none());
1917
1918 *stored_sender = Some(PendingWebDriverResponse {
1919 response_sender,
1920 expected_file_count: test_paths.len(),
1921 });
1922
1923 self.input_type()
1924 .as_specific()
1925 .select_files(self, Some(test_paths));
1926 }
1927
1928 pub(crate) fn take_pending_webdriver_response(&self) -> Option<PendingWebDriverResponse> {
1929 self.pending_webdriver_response.borrow_mut().take()
1930 }
1931
1932 fn sanitize_value(&self, value: &mut DOMString) {
1934 self.input_type().as_specific().sanitize_value(self, value);
1935 }
1936
1937 fn implicit_submission(&self, cx: &mut JSContext) {
1939 let doc = self.owner_document();
1940 let node = doc.upcast::<Node>();
1941 let owner = self.form_owner();
1942 let form = match owner {
1943 None => return,
1944 Some(ref f) => f,
1945 };
1946
1947 if self.upcast::<Element>().click_in_progress() {
1948 return;
1949 }
1950 let submit_button = node
1951 .traverse_preorder(ShadowIncluding::No)
1952 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1953 .filter(|input| matches!(*input.input_type(), InputType::Submit(_)))
1954 .find(|r| r.form_owner() == owner);
1955 match submit_button {
1956 Some(ref button) => {
1957 if button.is_instance_activatable() {
1958 button
1961 .upcast::<Node>()
1962 .fire_synthetic_pointer_event_not_trusted(cx, atom!("click"));
1963 }
1964 },
1965 None => {
1966 let mut inputs = node
1967 .traverse_preorder(ShadowIncluding::No)
1968 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1969 .filter(|input| {
1970 input.form_owner() == owner &&
1971 matches!(
1972 *input.input_type(),
1973 InputType::Text(_) |
1974 InputType::Search(_) |
1975 InputType::Url(_) |
1976 InputType::Tel(_) |
1977 InputType::Email(_) |
1978 InputType::Password(_) |
1979 InputType::Date(_) |
1980 InputType::Month(_) |
1981 InputType::Week(_) |
1982 InputType::Time(_) |
1983 InputType::DatetimeLocal(_) |
1984 InputType::Number(_)
1985 )
1986 });
1987
1988 if inputs.nth(1).is_some() {
1989 return;
1991 }
1992
1993 form.submit(
1994 cx,
1995 SubmittedFrom::NotFromForm,
1996 FormSubmitterElement::Form(form),
1997 );
1998 },
1999 }
2000 }
2001
2002 pub(crate) fn convert_string_to_number(&self, value: &str) -> Option<f64> {
2004 self.input_type()
2005 .as_specific()
2006 .convert_string_to_number(value)
2007 }
2008
2009 fn convert_number_to_string(&self, value: f64) -> Option<DOMString> {
2011 self.input_type()
2012 .as_specific()
2013 .convert_number_to_string(value)
2014 }
2015
2016 fn update_related_validity_states(&self, cx: &mut JSContext) {
2017 match *self.input_type() {
2018 InputType::Radio(_) => {
2019 perform_radio_group_validation(cx, self, self.radio_group_name().as_ref())
2020 },
2021 _ => {
2022 self.validity_state(cx)
2023 .perform_validation_and_update(cx, ValidationFlags::all());
2024 },
2025 }
2026 }
2027
2028 fn value_changed(&self, cx: &mut JSContext) {
2029 self.maybe_update_shared_selection();
2030 self.update_related_validity_states(cx);
2031 self.input_type().as_specific().update_shadow_tree(cx, self);
2032 }
2033
2034 pub(crate) fn fire_value_change_events(&self, cx: &mut JSContext, events: ValueChangeEvents) {
2039 let target = self.upcast::<EventTarget>();
2040
2041 if events.contains(ValueChangeEvents::Input) {
2044 target.fire_event_with_params(
2045 cx,
2046 atom!("input"),
2047 EventBubbles::Bubbles,
2048 EventCancelable::NotCancelable,
2049 EventComposed::Composed,
2050 );
2051 }
2052
2053 if events.contains(ValueChangeEvents::Change) {
2056 target.fire_bubbling_event(cx, atom!("change"));
2057 }
2058 }
2059
2060 pub(crate) fn show_the_picker_if_applicable(&self) {
2062 if !self.is_mutable() {
2066 return;
2067 }
2068
2069 self.input_type()
2072 .as_specific()
2073 .show_the_picker_if_applicable(self);
2074 }
2075
2076 pub(crate) fn handle_color_picker_response(
2077 &self,
2078 cx: &mut JSContext,
2079 response: Option<RgbColor>,
2080 ) {
2081 if let InputType::Color(ref color_input_type) = *self.input_type() {
2082 color_input_type.handle_color_picker_response(cx, self, response)
2083 }
2084 }
2085
2086 pub(crate) fn handle_file_picker_response(
2087 &self,
2088 cx: &mut JSContext,
2089 response: Option<Vec<SelectedFile>>,
2090 ) {
2091 if let InputType::File(ref file_input_type) = *self.input_type() {
2092 file_input_type.handle_file_picker_response(cx, self, response)
2093 }
2094 }
2095
2096 fn handle_focus_event(&self, cx: &mut JSContext, event: &FocusEvent) {
2097 let event_type = event.upcast::<Event>().type_();
2098 let document = self.owner_document();
2099 if *event_type == *"blur" {
2100 document
2101 .embedder_controls()
2102 .hide_embedder_control(self.upcast());
2103 } else if *event_type == *"focus" {
2104 let input_type = &*self.input_type();
2105 let Ok(input_method_type) = input_type.try_into() else {
2106 return;
2107 };
2108
2109 if self.is_textual_or_password() &&
2114 let Some(selection) = document.selection()
2115 {
2116 let _ = selection.Collapse(cx, None, 0);
2117 }
2118
2119 document.embedder_controls().show_embedder_control(
2120 ControlElement::Ime(Dom::from_ref(self.upcast())),
2121 EmbedderControlRequest::InputMethod(InputMethodRequest {
2122 input_method_type,
2123 text: String::from(self.Value()),
2124 insertion_point: self.GetSelectionEnd(),
2125 multiline: false,
2126 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
2128 }),
2129 None,
2130 );
2131 }
2132 }
2133}
2134
2135impl VirtualMethods for HTMLInputElement {
2136 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2137 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2138 }
2139
2140 fn attribute_mutated(
2141 &self,
2142 cx: &mut JSContext,
2143 attr: AttrRef<'_>,
2144 mutation: AttributeMutation,
2145 ) {
2146 let could_have_had_embedder_control = self.may_have_embedder_control();
2147
2148 self.super_type()
2149 .unwrap()
2150 .attribute_mutated(cx, attr, mutation);
2151
2152 match *attr.local_name() {
2153 local_name!("disabled") => {
2154 let disabled_state = match mutation {
2155 AttributeMutation::Set(None) => true,
2156 AttributeMutation::Set(Some(_)) => {
2157 return;
2159 },
2160 AttributeMutation::Removed => false,
2161 };
2162 let el = self.upcast::<Element>();
2163 el.set_disabled_state(disabled_state);
2164 el.set_enabled_state(!disabled_state);
2165 el.check_ancestors_disabled_state_for_form_control(cx.no_gc());
2166
2167 if self.input_type().is_textual() {
2168 let read_write = !(self.ReadOnly() || el.disabled_state());
2169 el.set_read_write_state(read_write);
2170 }
2171 },
2172 local_name!("checked") if !self.checked_changed.get() => {
2173 let checked_state = match mutation {
2174 AttributeMutation::Set(None) => true,
2175 AttributeMutation::Set(Some(_)) => {
2176 return;
2178 },
2179 AttributeMutation::Removed => false,
2180 };
2181 self.update_checkedness(cx, checked_state, false);
2182 },
2183 local_name!("size") => {
2184 let size = mutation.new_value(attr).map(|value| value.as_uint());
2185 self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
2186 },
2187 local_name!("type") => {
2188 match mutation {
2189 AttributeMutation::Set(previous_value) => {
2190 if previous_value
2194 .is_some_and(|previous_value| **previous_value == **attr.value())
2195 {
2196 return;
2197 }
2198
2199 let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
2200 let previously_selectable = self.selection_api_applies();
2201
2202 *self.input_type.borrow_mut() =
2203 InputType::new_from_atom(attr.value().as_atom());
2204 self.is_textual_or_password
2205 .set(self.input_type().is_textual_or_password());
2206
2207 let element = self.upcast::<Element>();
2208 if self.input_type().is_textual() {
2209 let read_write = !(self.ReadOnly() || element.disabled_state());
2210 element.set_read_write_state(read_write);
2211 } else {
2212 element.set_read_write_state(false);
2213 }
2214
2215 let new_value_mode = self.value_mode();
2216 match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
2217 (&ValueMode::Value, false, ValueMode::Default) |
2219 (&ValueMode::Value, false, ValueMode::DefaultOn) => {
2220 self.SetValue(cx, old_idl_value)
2221 .expect("Failed to set input value on type change to a default ValueMode.");
2222 },
2223
2224 (_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
2226 self.SetValue(
2227 cx,
2228 self.upcast::<Element>()
2229 .get_attribute_string_value(&local_name!("value"))
2230 .unwrap_or_default()
2231 .into(),
2232 )
2233 .expect(
2234 "Failed to set input value on type change to ValueMode::Value.",
2235 );
2236 self.value_dirty.set(false);
2237 },
2238
2239 (_, _, ValueMode::Filename)
2241 if old_value_mode != ValueMode::Filename =>
2242 {
2243 self.SetValue(cx, DOMString::new())
2244 .expect("Failed to set input value on type change to ValueMode::Filename.");
2245 },
2246 _ => {},
2247 }
2248
2249 self.input_type().as_specific().signal_type_change(cx, self);
2251
2252 let mut text_input = self.text_input.borrow_mut();
2254 let mut value = text_input.get_content();
2255 self.sanitize_value(&mut value);
2256 text_input.set_content(value);
2257 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
2258
2259 if self.does_minmaxlength_apply() {
2261 text_input
2262 .set_min_length(self.MinLength().to_u32().map(Utf16CodeUnits));
2263 text_input
2264 .set_max_length(self.MaxLength().to_u32().map(Utf16CodeUnits));
2265 } else {
2266 text_input.set_min_length(None);
2267 text_input.set_max_length(None);
2268 }
2269
2270 if !previously_selectable && self.selection_api_applies() {
2272 text_input.clear_selection_to_start();
2273 }
2274 },
2275 AttributeMutation::Removed => {
2276 self.input_type().as_specific().signal_type_change(cx, self);
2277 *self.input_type.borrow_mut() = InputType::new_text();
2278 self.is_textual_or_password
2279 .set(self.input_type().is_textual_or_password());
2280
2281 let element = self.upcast::<Element>();
2282 let read_write = !(self.ReadOnly() || element.disabled_state());
2283 element.set_read_write_state(read_write);
2284 },
2285 }
2286
2287 self.update_placeholder_shown_state();
2288 self.input_type()
2289 .as_specific()
2290 .update_placeholder_contents(cx, self);
2291
2292 self.update_checked_state();
2293 self.update_indeterminate_state();
2294 },
2295 local_name!("value") if !self.value_dirty.get() => {
2296 let value = mutation.new_value(attr).map(|value| (**value).to_owned());
2300 let mut value = value.map_or(DOMString::new(), DOMString::from);
2301
2302 self.sanitize_value(&mut value);
2303 self.text_input.borrow_mut().set_content(value);
2304 self.update_placeholder_shown_state();
2305 },
2306 local_name!("maxlength") if self.does_minmaxlength_apply() => match *attr.value() {
2307 AttrValue::Int(_, value) => {
2308 let mut text_input = self.text_input.borrow_mut();
2309
2310 if value < 0 {
2311 text_input.set_max_length(None);
2312 } else {
2313 text_input.set_max_length(Some(Utf16CodeUnits(value as u32)))
2314 }
2315 },
2316 _ => panic!("Expected an AttrValue::Int"),
2317 },
2318 local_name!("minlength") if self.does_minmaxlength_apply() => match *attr.value() {
2319 AttrValue::Int(_, value) => {
2320 let mut text_input = self.text_input.borrow_mut();
2321
2322 if value < 0 {
2323 text_input.set_min_length(None);
2324 } else {
2325 text_input.set_min_length(Some(Utf16CodeUnits(value as u32)))
2326 }
2327 },
2328 _ => panic!("Expected an AttrValue::Int"),
2329 },
2330 local_name!("placeholder") => {
2331 {
2332 let mut placeholder = self.placeholder.borrow_mut();
2333 placeholder.clear();
2334 if let AttributeMutation::Set(_) = mutation {
2335 placeholder
2336 .extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
2337 }
2338 }
2339 self.update_placeholder_shown_state();
2340 self.input_type()
2341 .as_specific()
2342 .update_placeholder_contents(cx, self);
2343 },
2344 local_name!("readonly") => {
2345 if self.input_type().is_textual() {
2346 let el = self.upcast::<Element>();
2347 match mutation {
2348 AttributeMutation::Set(_) => {
2349 el.set_read_write_state(false);
2350 },
2351 AttributeMutation::Removed => {
2352 el.set_read_write_state(!el.disabled_state());
2353 },
2354 }
2355 }
2356 },
2357 local_name!("form") => {
2358 self.form_attribute_mutated(cx, mutation);
2359 },
2360 _ => {
2361 self.input_type()
2362 .as_specific()
2363 .attribute_mutated(cx, self, attr, mutation);
2364 },
2365 }
2366
2367 self.value_changed(cx);
2368
2369 if could_have_had_embedder_control && !self.may_have_embedder_control() {
2370 self.owner_document()
2371 .embedder_controls()
2372 .hide_embedder_control(self.upcast());
2373 }
2374 }
2375
2376 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2377 match *name {
2378 local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
2379 local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
2380 local_name!("type") => AttrValue::from_atomic(value.into()),
2381 local_name!("maxlength") => {
2382 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
2383 },
2384 local_name!("minlength") => {
2385 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
2386 },
2387 local_name!("width") | local_name!("height") => AttrValue::from_dimension(value.into()),
2388 _ => self
2389 .super_type()
2390 .unwrap()
2391 .parse_plain_attribute(name, value),
2392 }
2393 }
2394
2395 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2396 if let Some(s) = self.super_type() {
2397 s.bind_to_tree(cx, context);
2398 }
2399 self.upcast::<Element>()
2400 .check_ancestors_disabled_state_for_form_control(cx.no_gc());
2401
2402 self.input_type()
2403 .as_specific()
2404 .bind_to_tree(cx, self, context);
2405
2406 self.value_changed(cx);
2407 }
2408
2409 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
2410 self.owner_document()
2412 .embedder_controls()
2413 .hide_embedder_control(self.upcast());
2414
2415 let form_owner = self.form_owner();
2416 self.super_type().unwrap().unbind_from_tree(cx, context);
2417
2418 let node = self.upcast::<Node>();
2419 let el = self.upcast::<Element>();
2420 if node
2421 .ancestors()
2422 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
2423 {
2424 el.check_ancestors_disabled_state_for_form_control(cx.no_gc());
2425 } else {
2426 el.check_disabled_attribute();
2427 }
2428
2429 self.input_type()
2430 .as_specific()
2431 .unbind_from_tree(cx, self, form_owner, context);
2432
2433 self.validity_state(cx)
2434 .perform_validation_and_update(cx, ValidationFlags::all());
2435 }
2436
2437 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
2443 if let Some(event) = event.downcast::<FocusEvent>() {
2444 self.handle_focus_event(cx, event)
2445 }
2446
2447 let value_change_events = self
2448 .input_type()
2449 .as_specific()
2450 .handle_event(cx, self, event);
2451
2452 self.value_changed(cx);
2453
2454 self.fire_value_change_events(cx, value_change_events);
2455
2456 if let Some(super_type) = self.super_type() {
2457 super_type.handle_event(cx, event);
2458 }
2459 }
2460
2461 fn cloning_steps(
2463 &self,
2464 cx: &mut JSContext,
2465 copy: &Node,
2466 maybe_doc: Option<&Document>,
2467 clone_children: CloneChildrenFlag,
2468 ) {
2469 if let Some(s) = self.super_type() {
2470 s.cloning_steps(cx, copy, maybe_doc, clone_children);
2471 }
2472 let elem = copy.downcast::<HTMLInputElement>().unwrap();
2473 elem.value_dirty.set(self.value_dirty.get());
2474 elem.update_checkedness(cx, self.Checked(), false);
2475 elem.checked_changed.set(self.checked_changed.get());
2476 elem.SetIndeterminate(cx, self.Indeterminate());
2479 elem.text_input
2480 .borrow_mut()
2481 .set_content(self.text_input.borrow().get_content());
2482 self.value_changed(cx);
2483 }
2484
2485 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2486 match attr.local_name() {
2487 &local_name!("width") | &local_name!("height") => {
2488 matches!(*self.input_type(), InputType::Image(_))
2489 },
2490 _ => self
2491 .super_type()
2492 .unwrap()
2493 .attribute_affects_presentational_hints(attr),
2494 }
2495 }
2496}
2497
2498impl FormControl for HTMLInputElement {
2499 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2500 self.form_owner.get()
2501 }
2502
2503 fn set_form_owner(&self, cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2504 self.form_owner.set(form);
2505 self.input_type().as_specific().form_owner_changed(cx, self);
2506 }
2507
2508 fn to_html_element(&self) -> &HTMLElement {
2509 self.upcast::<HTMLElement>()
2510 }
2511}
2512
2513impl Validatable for HTMLInputElement {
2514 fn as_element(&self) -> &Element {
2515 self.upcast()
2516 }
2517
2518 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
2519 self.validity_state
2520 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
2521 }
2522
2523 fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
2524 match *self.input_type() {
2531 InputType::Hidden(_) | InputType::Button(_) | InputType::Reset(_) => false,
2532 _ => {
2533 !(self.upcast::<Element>().disabled_state() ||
2534 self.ReadOnly() ||
2535 is_barred_by_datalist_ancestor(no_gc, self.upcast()))
2536 },
2537 }
2538 }
2539
2540 fn perform_validation(
2541 &self,
2542 cx: &mut JSContext,
2543 validate_flags: ValidationFlags,
2544 ) -> ValidationFlags {
2545 let mut failed_flags = ValidationFlags::empty();
2546 let value = self.Value();
2547
2548 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
2549 self.suffers_from_being_missing(&value)
2550 {
2551 failed_flags.insert(ValidationFlags::VALUE_MISSING);
2552 }
2553
2554 if validate_flags.contains(ValidationFlags::TYPE_MISMATCH) &&
2555 self.suffers_from_type_mismatch(&value)
2556 {
2557 failed_flags.insert(ValidationFlags::TYPE_MISMATCH);
2558 }
2559
2560 if validate_flags.contains(ValidationFlags::PATTERN_MISMATCH) &&
2561 self.suffers_from_pattern_mismatch(cx, &value)
2562 {
2563 failed_flags.insert(ValidationFlags::PATTERN_MISMATCH);
2564 }
2565
2566 if validate_flags.contains(ValidationFlags::BAD_INPUT) &&
2567 self.suffers_from_bad_input(&value)
2568 {
2569 failed_flags.insert(ValidationFlags::BAD_INPUT);
2570 }
2571
2572 if validate_flags.intersects(ValidationFlags::TOO_LONG | ValidationFlags::TOO_SHORT) {
2573 failed_flags |= self.suffers_from_length_issues(&value);
2574 }
2575
2576 if validate_flags.intersects(
2577 ValidationFlags::RANGE_UNDERFLOW |
2578 ValidationFlags::RANGE_OVERFLOW |
2579 ValidationFlags::STEP_MISMATCH,
2580 ) {
2581 failed_flags |= self.suffers_from_range_issues(&value);
2582 }
2583
2584 failed_flags & validate_flags
2585 }
2586}
2587
2588impl Activatable for HTMLInputElement {
2589 fn as_element(&self) -> &Element {
2590 self.upcast()
2591 }
2592
2593 fn is_instance_activatable(&self) -> bool {
2595 match *self.input_type() {
2598 InputType::Checkbox(_) | InputType::Radio(_) => true,
2599 InputType::Hidden(_) => false,
2603 _ => self.is_mutable(),
2604 }
2605 }
2606
2607 fn legacy_pre_activation_behavior(&self, cx: &mut JSContext) -> Option<InputActivationState> {
2609 let activation_state = self
2610 .input_type()
2611 .as_specific()
2612 .legacy_pre_activation_behavior(cx, self);
2613
2614 if activation_state.is_some() {
2615 self.value_changed(cx);
2616 }
2617
2618 activation_state
2619 }
2620
2621 fn legacy_canceled_activation_behavior(
2623 &self,
2624 cx: &mut JSContext,
2625 cache: Option<InputActivationState>,
2626 ) {
2627 let ty = self.input_type();
2629 let cache = match cache {
2630 Some(cache) => {
2631 if (cache.was_radio && !matches!(*ty, InputType::Radio(_))) ||
2632 (cache.was_checkbox && !matches!(*ty, InputType::Checkbox(_)))
2633 {
2634 return;
2637 }
2638 cache
2639 },
2640 None => {
2641 return;
2642 },
2643 };
2644
2645 ty.as_specific()
2647 .legacy_canceled_activation_behavior(cx, self, cache);
2648
2649 self.value_changed(cx);
2650 }
2651
2652 fn activation_behavior(&self, cx: &mut JSContext, event: &Event, target: &EventTarget) {
2654 debug_assert!(self.is_instance_activatable());
2657
2658 let input_activation_type = {
2660 let input_type = self.input_type();
2661 InputActivationType::new_from_input_type(&input_type)
2662 };
2663
2664 if let Some(input_activation_type) = input_activation_type {
2665 input_activation_type
2666 .as_specific()
2667 .activation_behavior(cx, self, event, target);
2668 }
2669
2670 }
2678}
2679
2680fn compile_pattern(cx: &mut JSContext, pattern_str: &str, out_regex: MutableHandleObject) -> bool {
2684 if check_js_regex_syntax(cx, pattern_str) {
2686 let pattern_str = format!("^(?:{})$", pattern_str);
2688 let flags = RegExpFlags {
2689 flags_: RegExpFlag_UnicodeSets,
2690 };
2691 new_js_regex(cx, &pattern_str, flags, out_regex)
2692 } else {
2693 false
2694 }
2695}
2696
2697#[expect(unsafe_code)]
2698fn check_js_regex_syntax(cx: &mut JSContext, pattern: &str) -> bool {
2701 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2702 rooted!(&in(cx) let mut exception = UndefinedValue());
2703
2704 let valid = unsafe {
2705 CheckRegExpSyntax(
2706 cx,
2707 pattern.as_ptr(),
2708 pattern.len(),
2709 RegExpFlags {
2710 flags_: RegExpFlag_UnicodeSets,
2711 },
2712 exception.handle_mut(),
2713 )
2714 };
2715
2716 if !valid {
2717 unsafe { JS_ClearPendingException(cx) };
2718 return false;
2719 }
2720
2721 exception.is_undefined()
2724}
2725
2726#[expect(unsafe_code)]
2727fn new_js_regex(
2728 cx: &mut JSContext,
2729 pattern: &str,
2730 flags: RegExpFlags,
2731 mut out_regex: MutableHandleObject,
2732) -> bool {
2733 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2734 out_regex.set(unsafe { NewUCRegExpObject(cx, pattern.as_ptr(), pattern.len(), flags) });
2735
2736 if out_regex.is_null() {
2737 unsafe { JS_ClearPendingException(cx) };
2738 return false;
2739 }
2740 true
2741}
2742
2743#[expect(unsafe_code)]
2744fn matches_js_regex(cx: &mut JSContext, regex_obj: HandleObject, value: &str) -> Result<bool, ()> {
2745 let mut value: Vec<u16> = value.encode_utf16().collect();
2746
2747 let mut is_regex = false;
2748 assert!(unsafe { ObjectIsRegExp(cx, regex_obj, &mut is_regex) });
2749 assert!(is_regex);
2750
2751 rooted!(&in(cx) let mut rval = UndefinedValue());
2752 let mut index = 0;
2753
2754 let ok = unsafe {
2755 ExecuteRegExpNoStatics(
2756 cx,
2757 regex_obj,
2758 value.as_mut_ptr(),
2759 value.len(),
2760 &mut index,
2761 true,
2762 rval.handle_mut(),
2763 )
2764 };
2765
2766 if ok {
2767 Ok(!rval.is_null())
2768 } else {
2769 unsafe { JS_ClearPendingException(cx) };
2770 Err(())
2771 }
2772}
2773
2774#[derive(MallocSizeOf)]
2778pub(crate) struct PendingWebDriverResponse {
2779 response_sender: GenericSender<Result<bool, ErrorStatus>>,
2781 expected_file_count: usize,
2783}
2784
2785impl PendingWebDriverResponse {
2786 pub(crate) fn finish(self, number_files_selected: usize) {
2787 if number_files_selected == self.expected_file_count {
2788 let _ = self.response_sender.send(Ok(false));
2789 } else {
2790 let _ = self.response_sender.send(Err(ErrorStatus::InvalidArgument));
2793 }
2794 }
2795}