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 fonts::{ByteIndex, TextByteRange};
12use html5ever::{LocalName, Prefix, local_name};
13use js::context::JSContext;
14use js::jsapi::{
15 ClippedTime, JS_ClearPendingException, JSObject, NewDateObject, NewUCRegExpObject,
16 RegExpFlag_UnicodeSets, RegExpFlags,
17};
18use js::jsval::UndefinedValue;
19use js::rust::wrappers::{CheckRegExpSyntax, ExecuteRegExpNoStatics, ObjectIsRegExp};
20use js::rust::wrappers2::{DateGetMsecSinceEpoch, ObjectIsDate};
21use js::rust::{HandleObject, MutableHandleObject};
22use layout_api::{ScriptSelection, SharedSelection};
23use num_traits::ToPrimitive;
24use script_bindings::cell::{DomRefCell, Ref};
25use script_bindings::domstring::parse_floating_point_number;
26use servo_base::generic_channel::GenericSender;
27use servo_base::text::Utf16CodeUnitLength;
28use style::attr::AttrValue;
29use style::str::split_commas;
30use stylo_atoms::Atom;
31use stylo_dom::ElementState;
32use time::OffsetDateTime;
33use unicode_bidi::{BidiClass, bidi_class};
34use webdriver::error::ErrorStatus;
35
36use crate::clipboard_provider::EmbedderClipboardProvider;
37use crate::dom::activation::Activatable;
38use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
39use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
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::{DomRoot, LayoutDom, MutNullableDom};
48use crate::dom::bindings::str::{DOMString, USVString};
49use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
50use crate::dom::compositionevent::CompositionEvent;
51use crate::dom::document::Document;
52use crate::dom::document_embedder_controls::ControlElement;
53use crate::dom::element::attributes::storage::AttrRef;
54use crate::dom::element::{AttributeMutation, Element};
55use crate::dom::event::Event;
56use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
57use crate::dom::eventtarget::EventTarget;
58use crate::dom::filelist::FileList;
59use crate::dom::globalscope::GlobalScope;
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::htmlinputelement::radio_input_type::{
67 broadcast_radio_checked, perform_radio_group_validation,
68};
69use crate::dom::input_element::input_type::{InputActivationType, InputType};
70use crate::dom::iterators::ShadowIncluding;
71use crate::dom::keyboardevent::KeyboardEvent;
72use crate::dom::node::virtualmethods::VirtualMethods;
73use crate::dom::node::{
74 BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
75};
76use crate::dom::nodelist::NodeList;
77use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
78use crate::dom::types::{FocusEvent, MouseEvent};
79use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
80use crate::dom::validitystate::{ValidationFlags, ValidityState};
81use crate::realms::enter_realm;
82use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
83use crate::textinput::{ClipboardEventFlags, IsComposing, KeyReaction, Lines, TextInput};
84
85pub(crate) mod button_input_type;
86pub(crate) mod checkbox_input_type;
87pub(crate) mod color_input_type;
88pub(crate) mod date_input_type;
89pub(crate) mod datetime_local_input_type;
90pub(crate) mod email_input_type;
91pub(crate) mod file_input_type;
92pub(crate) mod hidden_input_type;
93pub(crate) mod image_input_type;
94pub(crate) mod input_type;
95pub(crate) mod month_input_type;
96pub(crate) mod number_input_type;
97pub(crate) mod password_input_type;
98pub(crate) mod radio_input_type;
99pub(crate) mod range_input_type;
100pub(crate) mod reset_input_type;
101pub(crate) mod search_input_type;
102pub(crate) mod submit_input_type;
103pub(crate) mod tel_input_type;
104pub(crate) mod text_input_type;
105pub(crate) mod text_input_widget;
106pub(crate) mod text_value_widget;
107pub(crate) mod time_input_type;
108pub(crate) mod url_input_type;
109pub(crate) mod week_input_type;
110
111#[derive(Debug, PartialEq)]
112enum ValueMode {
113 Value,
115
116 Default,
118
119 DefaultOn,
121
122 Filename,
124}
125
126#[derive(Debug, PartialEq)]
127enum StepDirection {
128 Up,
129 Down,
130}
131
132#[dom_struct]
133pub(crate) struct HTMLInputElement {
134 htmlelement: HTMLElement,
135 input_type: DomRefCell<InputType>,
136
137 is_textual_or_password: Cell<bool>,
140
141 checked_changed: Cell<bool>,
143 placeholder: DomRefCell<DOMString>,
144 size: Cell<u32>,
145 maxlength: Cell<i32>,
146 minlength: Cell<i32>,
147 #[no_trace]
148 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
149 value_dirty: Cell<bool>,
151 #[no_trace]
154 #[conditional_malloc_size_of]
155 shared_selection: SharedSelection,
156
157 form_owner: MutNullableDom<HTMLFormElement>,
158 labels_node_list: MutNullableDom<NodeList>,
159 validity_state: MutNullableDom<ValidityState>,
160 #[no_trace]
161 pending_webdriver_response: RefCell<Option<PendingWebDriverResponse>>,
162
163 has_scheduled_selectionchange_event: Cell<bool>,
165}
166
167#[derive(JSTraceable)]
168pub(crate) struct InputActivationState {
169 indeterminate: bool,
170 checked: bool,
171 checked_radio: Option<DomRoot<HTMLInputElement>>,
172 was_radio: bool,
173 was_checkbox: bool,
174 }
176
177static DEFAULT_INPUT_SIZE: u32 = 20;
178static DEFAULT_MAX_LENGTH: i32 = -1;
179static DEFAULT_MIN_LENGTH: i32 = -1;
180
181#[expect(non_snake_case)]
182impl HTMLInputElement {
183 fn new_inherited(
184 local_name: LocalName,
185 prefix: Option<Prefix>,
186 document: &Document,
187 ) -> HTMLInputElement {
188 let embedder_sender = document
189 .window()
190 .as_global_scope()
191 .script_to_embedder_chan()
192 .clone();
193 HTMLInputElement {
194 htmlelement: HTMLElement::new_inherited_with_state(
195 ElementState::ENABLED | ElementState::READWRITE,
196 local_name,
197 prefix,
198 document,
199 ),
200 input_type: DomRefCell::new(InputType::new_text()),
201 is_textual_or_password: Cell::new(true),
202 placeholder: DomRefCell::new(DOMString::new()),
203 checked_changed: Cell::new(false),
204 maxlength: Cell::new(DEFAULT_MAX_LENGTH),
205 minlength: Cell::new(DEFAULT_MIN_LENGTH),
206 size: Cell::new(DEFAULT_INPUT_SIZE),
207 textinput: DomRefCell::new(TextInput::new(
208 Lines::Single,
209 DOMString::new(),
210 EmbedderClipboardProvider {
211 embedder_sender,
212 webview_id: document.webview_id(),
213 },
214 )),
215 value_dirty: Cell::new(false),
216 shared_selection: Default::default(),
217 form_owner: Default::default(),
218 labels_node_list: MutNullableDom::new(None),
219 validity_state: Default::default(),
220 pending_webdriver_response: Default::default(),
221 has_scheduled_selectionchange_event: Default::default(),
222 }
223 }
224
225 pub(crate) fn new(
226 cx: &mut js::context::JSContext,
227 local_name: LocalName,
228 prefix: Option<Prefix>,
229 document: &Document,
230 proto: Option<HandleObject>,
231 ) -> DomRoot<HTMLInputElement> {
232 Node::reflect_node_with_proto(
233 cx,
234 Box::new(HTMLInputElement::new_inherited(
235 local_name, prefix, document,
236 )),
237 document,
238 proto,
239 )
240 }
241
242 pub(crate) fn auto_directionality(&self) -> Option<String> {
243 match *self.input_type() {
244 InputType::Text(_) | InputType::Search(_) | InputType::Url(_) | InputType::Email(_) => {
245 let value: String = String::from(self.Value());
246 Some(HTMLInputElement::directionality_from_value(&value))
247 },
248 _ => None,
249 }
250 }
251
252 pub(crate) fn directionality_from_value(value: &str) -> String {
253 if HTMLInputElement::is_first_strong_character_rtl(value) {
254 "rtl".to_owned()
255 } else {
256 "ltr".to_owned()
257 }
258 }
259
260 fn is_first_strong_character_rtl(value: &str) -> bool {
261 for ch in value.chars() {
262 return match bidi_class(ch) {
263 BidiClass::L => false,
264 BidiClass::AL => true,
265 BidiClass::R => true,
266 _ => continue,
267 };
268 }
269 false
270 }
271
272 fn value_mode(&self) -> ValueMode {
275 match *self.input_type() {
276 InputType::Submit(_) |
277 InputType::Reset(_) |
278 InputType::Button(_) |
279 InputType::Image(_) |
280 InputType::Hidden(_) => ValueMode::Default,
281
282 InputType::Checkbox(_) | InputType::Radio(_) => ValueMode::DefaultOn,
283
284 InputType::Color(_) |
285 InputType::Date(_) |
286 InputType::DatetimeLocal(_) |
287 InputType::Email(_) |
288 InputType::Month(_) |
289 InputType::Number(_) |
290 InputType::Password(_) |
291 InputType::Range(_) |
292 InputType::Search(_) |
293 InputType::Tel(_) |
294 InputType::Text(_) |
295 InputType::Time(_) |
296 InputType::Url(_) |
297 InputType::Week(_) => ValueMode::Value,
298
299 InputType::File(_) => ValueMode::Filename,
300 }
301 }
302
303 #[inline]
304 pub(crate) fn input_type(&self) -> Ref<'_, InputType> {
305 self.input_type.borrow()
306 }
307
308 pub(crate) fn is_nontypeable(&self) -> bool {
310 matches!(
311 *self.input_type(),
312 InputType::Button(_) |
313 InputType::Checkbox(_) |
314 InputType::Color(_) |
315 InputType::File(_) |
316 InputType::Hidden(_) |
317 InputType::Image(_) |
318 InputType::Radio(_) |
319 InputType::Range(_) |
320 InputType::Reset(_) |
321 InputType::Submit(_)
322 )
323 }
324
325 #[inline]
326 pub(crate) fn is_submit_button(&self) -> bool {
327 matches!(
328 *self.input_type(),
329 InputType::Submit(_) | InputType::Image(_)
330 )
331 }
332
333 pub(crate) fn is_auto_directionality_form_associated_element(&self) -> bool {
335 matches!(
336 *self.input_type(),
337 InputType::Hidden(_) |
338 InputType::Text(_) |
339 InputType::Search(_) |
340 InputType::Tel(_) |
341 InputType::Url(_) |
342 InputType::Email(_) |
343 InputType::Password(_) |
344 InputType::Submit(_) |
345 InputType::Reset(_) |
346 InputType::Button(_)
347 )
348 }
349
350 fn does_minmaxlength_apply(&self) -> bool {
351 matches!(
352 *self.input_type(),
353 InputType::Text(_) |
354 InputType::Search(_) |
355 InputType::Url(_) |
356 InputType::Tel(_) |
357 InputType::Email(_) |
358 InputType::Password(_)
359 )
360 }
361
362 fn does_pattern_apply(&self) -> bool {
363 matches!(
364 *self.input_type(),
365 InputType::Text(_) |
366 InputType::Search(_) |
367 InputType::Url(_) |
368 InputType::Tel(_) |
369 InputType::Email(_) |
370 InputType::Password(_)
371 )
372 }
373
374 fn does_multiple_apply(&self) -> bool {
375 matches!(*self.input_type(), InputType::Email(_))
376 }
377
378 fn does_value_as_number_apply(&self) -> bool {
381 matches!(
382 *self.input_type(),
383 InputType::Date(_) |
384 InputType::Month(_) |
385 InputType::Week(_) |
386 InputType::Time(_) |
387 InputType::DatetimeLocal(_) |
388 InputType::Number(_) |
389 InputType::Range(_)
390 )
391 }
392
393 fn does_value_as_date_apply(&self) -> bool {
394 matches!(
395 *self.input_type(),
396 InputType::Date(_) | InputType::Month(_) | InputType::Week(_) | InputType::Time(_)
397 )
398 }
399
400 fn allowed_value_step(&self) -> Option<f64> {
402 let default_step = self.default_step()?;
405
406 let Some(step_value) = self
409 .upcast::<Element>()
410 .get_attribute_string_value(&local_name!("step"))
411 else {
412 return Some(default_step * self.step_scale_factor());
413 };
414
415 if step_value.eq_ignore_ascii_case("any") {
418 return None;
419 }
420
421 let Some(parsed_value) =
425 parse_floating_point_number(&step_value).filter(|value| *value > 0.0)
426 else {
427 return Some(default_step * self.step_scale_factor());
428 };
429
430 Some(parsed_value * self.step_scale_factor())
434 }
435
436 fn minimum(&self) -> Option<f64> {
438 self.upcast::<Element>()
439 .get_attribute_string_value(&local_name!("min"))
440 .and_then(|value| self.convert_string_to_number(&value))
441 .or_else(|| self.default_minimum())
442 }
443
444 fn maximum(&self) -> Option<f64> {
446 self.upcast::<Element>()
447 .get_attribute_string_value(&local_name!("max"))
448 .and_then(|value| self.convert_string_to_number(&value))
449 .or_else(|| self.default_maximum())
450 }
451
452 fn stepped_minimum(&self) -> Option<f64> {
455 match (self.minimum(), self.allowed_value_step()) {
456 (Some(min), Some(allowed_step)) => {
457 let step_base = self.step_base();
458 let nsteps = (min - step_base) / allowed_step;
460 Some(step_base + (allowed_step * nsteps.ceil()))
462 },
463 (_, _) => None,
464 }
465 }
466
467 fn stepped_maximum(&self) -> Option<f64> {
470 match (self.maximum(), self.allowed_value_step()) {
471 (Some(max), Some(allowed_step)) => {
472 let step_base = self.step_base();
473 let nsteps = (max - step_base) / allowed_step;
475 Some(step_base + (allowed_step * nsteps.floor()))
477 },
478 (_, _) => None,
479 }
480 }
481
482 fn default_minimum(&self) -> Option<f64> {
484 match *self.input_type() {
485 InputType::Range(_) => Some(0.0),
486 _ => None,
487 }
488 }
489
490 fn default_maximum(&self) -> Option<f64> {
492 match *self.input_type() {
493 InputType::Range(_) => Some(100.0),
494 _ => None,
495 }
496 }
497
498 fn default_range_value(&self) -> f64 {
500 let min = self.minimum().unwrap_or(0.0);
501 let max = self.maximum().unwrap_or(100.0);
502 if max < min {
503 min
504 } else {
505 min + (max - min) * 0.5
506 }
507 }
508
509 fn default_step(&self) -> Option<f64> {
511 match *self.input_type() {
512 InputType::Date(_) => Some(1.0),
513 InputType::Month(_) => Some(1.0),
514 InputType::Week(_) => Some(1.0),
515 InputType::Time(_) => Some(60.0),
516 InputType::DatetimeLocal(_) => Some(60.0),
517 InputType::Number(_) => Some(1.0),
518 InputType::Range(_) => Some(1.0),
519 _ => None,
520 }
521 }
522
523 fn step_scale_factor(&self) -> f64 {
525 match *self.input_type() {
526 InputType::Date(_) => 86400000.0,
527 InputType::Month(_) => 1.0,
528 InputType::Week(_) => 604800000.0,
529 InputType::Time(_) => 1000.0,
530 InputType::DatetimeLocal(_) => 1000.0,
531 InputType::Number(_) => 1.0,
532 InputType::Range(_) => 1.0,
533 _ => unreachable!(),
534 }
535 }
536
537 fn step_base(&self) -> f64 {
539 if let Some(minimum) = self
543 .upcast::<Element>()
544 .get_attribute_string_value(&local_name!("min"))
545 .and_then(|value| self.convert_string_to_number(&value))
546 {
547 return minimum;
548 }
549
550 if let Some(value) = self
554 .upcast::<Element>()
555 .get_attribute_string_value(&local_name!("value"))
556 .and_then(|value| self.convert_string_to_number(&value))
557 {
558 return value;
559 }
560
561 if let Some(default_step_base) = self.default_step_base() {
563 return default_step_base;
564 }
565
566 0.0
568 }
569
570 fn default_step_base(&self) -> Option<f64> {
572 match *self.input_type() {
573 InputType::Week(_) => Some(-259200000.0),
574 _ => None,
575 }
576 }
577
578 fn step_up_or_down(&self, cx: &mut JSContext, n: i32, dir: StepDirection) -> ErrorResult {
582 if !self.does_value_as_number_apply() {
585 return Err(Error::InvalidState(None));
586 }
587 let step_base = self.step_base();
588
589 let Some(allowed_value_step) = self.allowed_value_step() else {
591 return Err(Error::InvalidState(None));
592 };
593
594 let minimum = self.minimum();
597 let maximum = self.maximum();
598 if let (Some(min), Some(max)) = (minimum, maximum) {
599 if min > max {
600 return Ok(());
601 }
602
603 if let Some(stepped_minimum) = self.stepped_minimum() &&
607 stepped_minimum > max
608 {
609 return Ok(());
610 }
611 }
612
613 let mut value: f64 = self
617 .convert_string_to_number(&self.Value().str())
618 .unwrap_or(0.0);
619
620 let valueBeforeStepping = value;
622
623 if (value - step_base) % allowed_value_step != 0.0 {
628 value = match dir {
629 StepDirection::Down =>
630 {
632 let intervals_from_base = ((value - step_base) / allowed_value_step).floor();
633 intervals_from_base * allowed_value_step + step_base
634 },
635 StepDirection::Up =>
636 {
638 let intervals_from_base = ((value - step_base) / allowed_value_step).ceil();
639 intervals_from_base * allowed_value_step + step_base
640 },
641 };
642 }
643 else {
645 value += match dir {
650 StepDirection::Down => -f64::from(n) * allowed_value_step,
651 StepDirection::Up => f64::from(n) * allowed_value_step,
652 };
653 }
654
655 if let Some(min) = minimum &&
659 value < min
660 {
661 value = self.stepped_minimum().unwrap_or(value);
662 }
663
664 if let Some(max) = maximum &&
668 value > max
669 {
670 value = self.stepped_maximum().unwrap_or(value);
671 }
672
673 match dir {
677 StepDirection::Down => {
678 if value > valueBeforeStepping {
679 return Ok(());
680 }
681 },
682 StepDirection::Up => {
683 if value < valueBeforeStepping {
684 return Ok(());
685 }
686 },
687 }
688
689 self.SetValueAsNumber(cx, value)
693 }
694
695 fn suggestions_source_element(&self) -> Option<DomRoot<HTMLDataListElement>> {
697 let list_string = self
698 .upcast::<Element>()
699 .get_string_attribute(&local_name!("list"));
700 if list_string.is_empty() {
701 return None;
702 }
703 let ancestor = self
704 .upcast::<Node>()
705 .GetRootNode(&GetRootNodeOptions::empty());
706 let first_with_id = &ancestor
707 .traverse_preorder(ShadowIncluding::No)
708 .find(|node| {
709 node.downcast::<Element>()
710 .is_some_and(|e| e.Id() == list_string)
711 });
712 first_with_id
713 .as_ref()
714 .and_then(|el| el.downcast::<HTMLDataListElement>())
715 .map(DomRoot::from_ref)
716 }
717
718 fn suffers_from_being_missing(&self, value: &DOMString) -> bool {
720 self.input_type()
721 .as_specific()
722 .suffers_from_being_missing(self, value)
723 }
724
725 fn suffers_from_type_mismatch(&self, value: &DOMString) -> bool {
727 if value.is_empty() {
728 return false;
729 }
730
731 self.input_type()
732 .as_specific()
733 .suffers_from_type_mismatch(self, value)
734 }
735
736 fn suffers_from_pattern_mismatch(&self, value: &DOMString, can_gc: CanGc) -> bool {
738 let pattern_str = self.Pattern();
741 if value.is_empty() || pattern_str.is_empty() || !self.does_pattern_apply() {
742 return false;
743 }
744
745 let cx = GlobalScope::get_cx();
747 let _ac = enter_realm(self);
748 rooted!(in(*cx) let mut pattern = ptr::null_mut::<JSObject>());
749
750 if compile_pattern(cx, &pattern_str.str(), pattern.handle_mut(), can_gc) {
751 if self.Multiple() && self.does_multiple_apply() {
752 !split_commas(&value.str())
753 .all(|s| matches_js_regex(cx, pattern.handle(), s, can_gc).unwrap_or(true))
754 } else {
755 !matches_js_regex(cx, pattern.handle(), &value.str(), can_gc).unwrap_or(true)
756 }
757 } else {
758 false
760 }
761 }
762
763 fn suffers_from_bad_input(&self, value: &DOMString) -> bool {
765 if value.is_empty() {
766 return false;
767 }
768
769 self.input_type()
770 .as_specific()
771 .suffers_from_bad_input(value)
772 }
773
774 fn suffers_from_length_issues(&self, value: &DOMString) -> ValidationFlags {
777 let value_dirty = self.value_dirty.get();
780 let textinput = self.textinput.borrow();
781 let edit_by_user = !textinput.was_last_change_by_set_content();
782
783 if value.is_empty() || !value_dirty || !edit_by_user || !self.does_minmaxlength_apply() {
784 return ValidationFlags::empty();
785 }
786
787 let mut failed_flags = ValidationFlags::empty();
788 let Utf16CodeUnitLength(value_len) = textinput.len_utf16();
789 let min_length = self.MinLength();
790 let max_length = self.MaxLength();
791
792 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
793 failed_flags.insert(ValidationFlags::TOO_SHORT);
794 }
795
796 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
797 failed_flags.insert(ValidationFlags::TOO_LONG);
798 }
799
800 failed_flags
801 }
802
803 fn suffers_from_range_issues(&self, value: &DOMString) -> ValidationFlags {
807 if value.is_empty() || !self.does_value_as_number_apply() {
808 return ValidationFlags::empty();
809 }
810
811 let Some(value_as_number) = self.convert_string_to_number(&value.str()) else {
812 return ValidationFlags::empty();
813 };
814
815 let mut failed_flags = ValidationFlags::empty();
816 let min_value = self.minimum();
817 let max_value = self.maximum();
818
819 let has_reversed_range = match (min_value, max_value) {
821 (Some(min), Some(max)) => self.input_type().has_periodic_domain() && min > max,
822 _ => false,
823 };
824
825 if has_reversed_range {
826 if value_as_number > max_value.unwrap() && value_as_number < min_value.unwrap() {
828 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
829 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
830 }
831 } else {
832 if let Some(min_value) = min_value &&
834 value_as_number < min_value
835 {
836 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
837 }
838 if let Some(max_value) = max_value &&
840 value_as_number > max_value
841 {
842 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
843 }
844 }
845
846 if let Some(step) = self.allowed_value_step() {
848 let diff = (self.step_base() - value_as_number) % step / value_as_number;
852 if diff.abs() > 1e-12 {
853 failed_flags.insert(ValidationFlags::STEP_MISMATCH);
854 }
855 }
856
857 failed_flags
858 }
859
860 pub(crate) fn is_textual_or_password(&self) -> bool {
862 self.is_textual_or_password.get()
863 }
864
865 fn may_have_embedder_control(&self) -> bool {
866 let el = self.upcast::<Element>();
867 matches!(*self.input_type(), InputType::Color(_)) && !el.disabled_state()
868 }
869
870 fn handle_key_reaction(
871 &self,
872 cx: &mut js::context::JSContext,
873 action: KeyReaction,
874 event: &Event,
875 ) {
876 match action {
877 KeyReaction::TriggerDefaultAction => {
878 self.implicit_submission(cx);
879 event.mark_as_handled();
880 },
881 KeyReaction::DispatchInput(text, is_composing, input_type) => {
882 if event.IsTrusted() {
883 self.textinput.borrow().queue_input_event(
884 self.upcast(),
885 text,
886 is_composing,
887 input_type,
888 );
889 }
890 self.value_dirty.set(true);
891 self.update_placeholder_shown_state();
892 self.upcast::<Node>().dirty(NodeDamage::Other);
893 event.mark_as_handled();
894 },
895 KeyReaction::RedrawSelection => {
896 self.maybe_update_shared_selection();
897 event.mark_as_handled();
898 },
899 KeyReaction::Nothing => (),
900 }
901 }
902
903 fn value_for_shadow_dom(&self) -> DOMString {
905 let input_type = &*self.input_type();
906 match input_type {
907 InputType::Checkbox(_) |
908 InputType::Radio(_) |
909 InputType::Image(_) |
910 InputType::Hidden(_) |
911 InputType::Range(_) => input_type.as_specific().value_for_shadow_dom(self),
912 _ => {
913 if let Some(attribute_value) = self
914 .upcast::<Element>()
915 .get_attribute_string_value(&local_name!("value"))
916 {
917 return attribute_value.into();
918 }
919 input_type.as_specific().value_for_shadow_dom(self)
920 },
921 }
922 }
923
924 fn textinput_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
925 self.textinput.borrow_mut()
926 }
927
928 fn schedule_a_selection_change_event(&self) {
930 if self.has_scheduled_selectionchange_event.get() {
932 return;
933 }
934 self.has_scheduled_selectionchange_event.set(true);
936 let this = Trusted::new(self);
938 self.owner_global()
939 .task_manager()
940 .user_interaction_task_source()
941 .queue(
942 task!(selectionchange_task_steps: move |cx| {
944 let this = this.root();
945 this.has_scheduled_selectionchange_event.set(false);
947 this.upcast::<EventTarget>().fire_event_with_params(
949 cx,
950 atom!("selectionchange"),
951 EventBubbles::Bubbles,
952 EventCancelable::NotCancelable,
953 EventComposed::Composed,
954 );
955 }),
960 );
961 }
962}
963
964impl<'dom> LayoutDom<'dom, HTMLInputElement> {
965 pub(crate) fn size_for_layout(self) -> u32 {
975 self.unsafe_get().size.get()
976 }
977
978 pub(crate) fn selection_for_layout(self) -> Option<SharedSelection> {
979 if !self.unsafe_get().is_textual_or_password.get() {
980 return None;
981 }
982 Some(self.unsafe_get().shared_selection.clone())
983 }
984}
985
986impl TextControlElement for HTMLInputElement {
987 fn selection_api_applies(&self) -> bool {
989 matches!(
990 *self.input_type(),
991 InputType::Text(_) |
992 InputType::Search(_) |
993 InputType::Url(_) |
994 InputType::Tel(_) |
995 InputType::Password(_)
996 )
997 }
998
999 fn has_selectable_text(&self) -> bool {
1007 self.is_textual_or_password() && !self.textinput.borrow().get_content().is_empty()
1008 }
1009
1010 fn has_uncollapsed_selection(&self) -> bool {
1011 self.textinput.borrow().has_uncollapsed_selection()
1012 }
1013
1014 fn set_dirty_value_flag(&self, value: bool) {
1015 self.value_dirty.set(value)
1016 }
1017
1018 fn select_all(&self) {
1019 self.textinput.borrow_mut().select_all();
1020 self.maybe_update_shared_selection();
1021 }
1022
1023 fn maybe_update_shared_selection(&self) {
1024 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
1025 let (start, end) = (offsets.start.0, offsets.end.0);
1026 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
1027 let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
1028
1029 let mut shared_selection = self.shared_selection.borrow_mut();
1030 let range_remained_equal = range == shared_selection.range;
1031 if range_remained_equal && enabled == shared_selection.enabled {
1032 return;
1033 }
1034
1035 if !range_remained_equal {
1036 self.schedule_a_selection_change_event();
1041 }
1042
1043 *shared_selection = ScriptSelection {
1044 range,
1045 character_range: self
1046 .textinput
1047 .borrow()
1048 .sorted_selection_character_offsets_range(),
1049 enabled,
1050 };
1051 self.owner_window().layout().set_needs_new_display_list();
1052 }
1053
1054 fn is_password_field(&self) -> bool {
1055 matches!(*self.input_type(), InputType::Password(_))
1056 }
1057
1058 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
1059 self.placeholder.borrow()
1060 }
1061
1062 fn value_text(&self) -> DOMString {
1063 self.Value()
1064 }
1065}
1066
1067impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
1068 make_getter!(Accept, "accept");
1070
1071 make_setter!(SetAccept, "accept");
1073
1074 make_bool_getter!(Alpha, "alpha");
1076
1077 make_bool_setter!(SetAlpha, "alpha");
1079
1080 make_getter!(Alt, "alt");
1082
1083 make_setter!(SetAlt, "alt");
1085
1086 make_getter!(DirName, "dirname");
1088
1089 make_setter!(SetDirName, "dirname");
1091
1092 make_bool_getter!(Disabled, "disabled");
1094
1095 make_bool_setter!(SetDisabled, "disabled");
1097
1098 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
1100 self.form_owner()
1101 }
1102
1103 fn GetFiles(&self) -> Option<DomRoot<FileList>> {
1105 self.input_type()
1106 .as_specific()
1107 .get_files()
1108 .as_ref()
1109 .cloned()
1110 }
1111
1112 fn SetFiles(&self, _cx: &mut js::context::JSContext, files: Option<&FileList>) {
1114 if let Some(files) = files {
1115 self.input_type().as_specific().set_files(files)
1116 }
1117 }
1118
1119 make_bool_getter!(DefaultChecked, "checked");
1121
1122 make_bool_setter!(SetDefaultChecked, "checked");
1124
1125 fn Checked(&self) -> bool {
1127 self.upcast::<Element>()
1128 .state()
1129 .contains(ElementState::CHECKED)
1130 }
1131
1132 fn SetChecked(&self, cx: &mut JSContext, checked: bool) {
1134 self.update_checked_state(cx, checked, true);
1135 self.value_changed(cx);
1136 }
1137
1138 make_enumerated_getter!(
1140 ColorSpace,
1141 "colorspace",
1142 "limited-srgb" | "display-p3",
1143 missing => "limited-srgb",
1144 invalid => "limited-srgb"
1145 );
1146
1147 make_setter!(SetColorSpace, "colorspace");
1149
1150 make_bool_getter!(ReadOnly, "readonly");
1152
1153 make_bool_setter!(SetReadOnly, "readonly");
1155
1156 make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE);
1158
1159 make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
1161
1162 fn Type(&self) -> DOMString {
1164 DOMString::from(self.input_type().as_str())
1165 }
1166
1167 make_atomic_setter!(SetType, "type");
1169
1170 fn Value(&self) -> DOMString {
1172 match self.value_mode() {
1173 ValueMode::Value => self.textinput.borrow().get_content(),
1174 ValueMode::Default => self
1175 .upcast::<Element>()
1176 .get_attribute_string_value(&local_name!("value"))
1177 .map(|value| value.into())
1178 .unwrap_or_default(),
1179 ValueMode::DefaultOn => self
1180 .upcast::<Element>()
1181 .get_attribute_string_value(&local_name!("value"))
1182 .map(|value| value.into())
1183 .unwrap_or(DOMString::from("on")),
1184 ValueMode::Filename => {
1185 let mut path = DOMString::from("");
1186 match self.input_type().as_specific().get_files() {
1187 Some(ref fl) => match fl.Item(0) {
1188 Some(ref f) => {
1189 path.push_str("C:\\fakepath\\");
1190 path.push_str(&f.name().str());
1191 path
1192 },
1193 None => path,
1194 },
1195 None => path,
1196 }
1197 },
1198 }
1199 }
1200
1201 fn SetValue(&self, cx: &mut JSContext, mut value: DOMString) -> ErrorResult {
1203 match self.value_mode() {
1204 ValueMode::Value => {
1205 {
1206 self.value_dirty.set(true);
1208
1209 self.sanitize_value(&mut value);
1212
1213 let mut textinput = self.textinput.borrow_mut();
1214
1215 if textinput.get_content() != value {
1220 textinput.set_content(value);
1222
1223 textinput.clear_selection_to_end();
1224 }
1225 }
1226
1227 self.update_placeholder_shown_state();
1231 self.maybe_update_shared_selection();
1232 },
1233 ValueMode::Default | ValueMode::DefaultOn => {
1234 self.upcast::<Element>()
1235 .set_string_attribute(cx, &local_name!("value"), value);
1236 },
1237 ValueMode::Filename => {
1238 if value.is_empty() {
1239 let window = self.owner_window();
1240 let fl = FileList::new(&window, vec![], CanGc::from_cx(cx));
1241 self.input_type().as_specific().set_files(&fl)
1242 } else {
1243 return Err(Error::InvalidState(None));
1244 }
1245 },
1246 }
1247
1248 self.value_changed(cx);
1249 self.upcast::<Node>().dirty(NodeDamage::Other);
1250 Ok(())
1251 }
1252
1253 make_getter!(DefaultValue, "value");
1255
1256 make_setter!(SetDefaultValue, "value");
1258
1259 make_getter!(Min, "min");
1261
1262 make_setter!(SetMin, "min");
1264
1265 fn GetList(&self) -> Option<DomRoot<HTMLDataListElement>> {
1267 self.suggestions_source_element()
1268 }
1269
1270 #[expect(unsafe_code)]
1272 fn GetValueAsDate(&self, cx: SafeJSContext, mut return_value: MutableHandleObject) {
1273 if let Some(date_time) = self
1274 .input_type()
1275 .as_specific()
1276 .convert_string_to_naive_datetime(self.Value())
1277 {
1278 let time = ClippedTime {
1279 t: (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64,
1280 };
1281 return_value.set(unsafe { NewDateObject(*cx, time) });
1282 }
1283 }
1284
1285 #[expect(unsafe_code)]
1287 fn SetValueAsDate(&self, cx: &mut JSContext, value: *mut JSObject) -> ErrorResult {
1288 rooted!(&in(cx) let value = value);
1289 if !self.does_value_as_date_apply() {
1290 return Err(Error::InvalidState(None));
1291 }
1292 if value.is_null() {
1293 return self.SetValue(cx, DOMString::from(""));
1294 }
1295 let mut msecs: f64 = 0.0;
1296 unsafe {
1300 let mut is_date = false;
1301 if !ObjectIsDate(cx, value.handle(), &mut is_date) {
1302 return Err(Error::JSFailed);
1303 }
1304 if !is_date {
1305 return Err(Error::Type(c"Value was not a date".to_owned()));
1306 }
1307 if !DateGetMsecSinceEpoch(cx, value.handle(), &mut msecs) {
1308 return Err(Error::JSFailed);
1309 }
1310 if !msecs.is_finite() {
1311 return self.SetValue(cx, DOMString::from(""));
1312 }
1313 }
1314
1315 let Ok(date_time) = OffsetDateTime::from_unix_timestamp_nanos((msecs * 1e6) as i128) else {
1316 return self.SetValue(cx, DOMString::from(""));
1317 };
1318 self.SetValue(
1319 cx,
1320 self.input_type()
1321 .as_specific()
1322 .convert_datetime_to_dom_string(date_time),
1323 )
1324 }
1325
1326 fn ValueAsNumber(&self) -> f64 {
1328 self.convert_string_to_number(&self.Value().str())
1329 .unwrap_or(f64::NAN)
1330 }
1331
1332 fn SetValueAsNumber(&self, cx: &mut JSContext, value: f64) -> ErrorResult {
1334 if value.is_infinite() {
1335 Err(Error::Type(c"value is not finite".to_owned()))
1336 } else if !self.does_value_as_number_apply() {
1337 Err(Error::InvalidState(None))
1338 } else if value.is_nan() {
1339 self.SetValue(cx, DOMString::from(""))
1340 } else if let Some(converted) = self.convert_number_to_string(value) {
1341 self.SetValue(cx, converted)
1342 } else {
1343 self.SetValue(cx, DOMString::from(""))
1348 }
1349 }
1350
1351 make_getter!(Name, "name");
1353
1354 make_atomic_setter!(SetName, "name");
1356
1357 make_getter!(Placeholder, "placeholder");
1359
1360 make_setter!(SetPlaceholder, "placeholder");
1362
1363 make_form_action_getter!(FormAction, "formaction");
1365
1366 make_setter!(SetFormAction, "formaction");
1368
1369 make_enumerated_getter!(
1371 FormEnctype,
1372 "formenctype",
1373 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
1374 invalid => "application/x-www-form-urlencoded"
1375 );
1376
1377 make_setter!(SetFormEnctype, "formenctype");
1379
1380 make_enumerated_getter!(
1382 FormMethod,
1383 "formmethod",
1384 "get" | "post" | "dialog",
1385 invalid => "get"
1386 );
1387
1388 make_setter!(SetFormMethod, "formmethod");
1390
1391 make_getter!(FormTarget, "formtarget");
1393
1394 make_setter!(SetFormTarget, "formtarget");
1396
1397 make_bool_getter!(FormNoValidate, "formnovalidate");
1399
1400 make_bool_setter!(SetFormNoValidate, "formnovalidate");
1402
1403 make_getter!(Max, "max");
1405
1406 make_setter!(SetMax, "max");
1408
1409 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1411
1412 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1414
1415 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
1417
1418 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
1420
1421 make_bool_getter!(Multiple, "multiple");
1423
1424 make_bool_setter!(SetMultiple, "multiple");
1426
1427 make_getter!(Pattern, "pattern");
1429
1430 make_setter!(SetPattern, "pattern");
1432
1433 make_bool_getter!(Required, "required");
1435
1436 make_bool_setter!(SetRequired, "required");
1438
1439 make_url_getter!(Src, "src");
1441
1442 make_url_setter!(SetSrc, "src");
1444
1445 make_getter!(Step, "step");
1447
1448 make_setter!(SetStep, "step");
1450
1451 make_getter!(UseMap, "usemap");
1453
1454 make_setter!(SetUseMap, "usemap");
1456
1457 fn Indeterminate(&self) -> bool {
1459 self.upcast::<Element>()
1460 .state()
1461 .contains(ElementState::INDETERMINATE)
1462 }
1463
1464 fn SetIndeterminate(&self, _cx: &mut JSContext, val: bool) {
1466 self.upcast::<Element>()
1467 .set_state(ElementState::INDETERMINATE, val)
1468 }
1469
1470 fn GetLabels(&self, cx: &mut JSContext) -> Option<DomRoot<NodeList>> {
1474 if matches!(*self.input_type(), InputType::Hidden(_)) {
1475 None
1476 } else {
1477 Some(self.labels_node_list.or_init(|| {
1478 NodeList::new_labels_list(
1479 self.upcast::<Node>().owner_doc().window(),
1480 self.upcast::<HTMLElement>(),
1481 CanGc::from_cx(cx),
1482 )
1483 }))
1484 }
1485 }
1486
1487 fn Select(&self) {
1489 self.selection().dom_select();
1490 }
1491
1492 fn GetSelectionStart(&self) -> Option<u32> {
1494 self.selection().dom_start().map(|start| start.0 as u32)
1495 }
1496
1497 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
1499 self.selection()
1500 .set_dom_start(start.map(Utf16CodeUnitLength::from))
1501 }
1502
1503 fn GetSelectionEnd(&self) -> Option<u32> {
1505 self.selection().dom_end().map(|end| end.0 as u32)
1506 }
1507
1508 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
1510 self.selection()
1511 .set_dom_end(end.map(Utf16CodeUnitLength::from))
1512 }
1513
1514 fn GetSelectionDirection(&self) -> Option<DOMString> {
1516 self.selection().dom_direction()
1517 }
1518
1519 fn SetSelectionDirection(
1521 &self,
1522 _cx: &mut JSContext,
1523 direction: Option<DOMString>,
1524 ) -> ErrorResult {
1525 self.selection().set_dom_direction(direction)
1526 }
1527
1528 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
1530 self.selection().set_dom_range(
1531 Utf16CodeUnitLength::from(start),
1532 Utf16CodeUnitLength::from(end),
1533 direction,
1534 )
1535 }
1536
1537 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
1539 self.selection()
1540 .set_dom_range_text(replacement, None, None, Default::default())
1541 }
1542
1543 fn SetRangeText_(
1545 &self,
1546 replacement: DOMString,
1547 start: u32,
1548 end: u32,
1549 selection_mode: SelectionMode,
1550 ) -> ErrorResult {
1551 self.selection().set_dom_range_text(
1552 replacement,
1553 Some(Utf16CodeUnitLength::from(start)),
1554 Some(Utf16CodeUnitLength::from(end)),
1555 selection_mode,
1556 )
1557 }
1558
1559 fn SelectFiles(&self, paths: Vec<DOMString>) {
1562 self.input_type()
1563 .as_specific()
1564 .select_files(self, Some(paths));
1565 }
1566
1567 fn StepUp(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1569 self.step_up_or_down(cx, n, StepDirection::Up)
1570 }
1571
1572 fn StepDown(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1574 self.step_up_or_down(cx, n, StepDirection::Down)
1575 }
1576
1577 fn WillValidate(&self) -> bool {
1579 self.is_instance_validatable()
1580 }
1581
1582 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
1584 self.validity_state(cx)
1585 }
1586
1587 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
1589 self.check_validity(cx)
1590 }
1591
1592 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
1594 self.report_validity(cx)
1595 }
1596
1597 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
1599 self.validation_message(cx)
1600 }
1601
1602 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
1604 self.validity_state(cx).set_custom_error_message(cx, error);
1605 }
1606}
1607
1608impl HTMLInputElement {
1609 pub(crate) fn form_datums(
1612 &self,
1613 submitter: Option<FormSubmitterElement>,
1614 encoding: Option<&'static Encoding>,
1615 ) -> (Vec<FormDatum>, bool) {
1616 let ty = self.Type();
1617 let name = self.Name();
1618 let is_submitter = match submitter {
1619 Some(FormSubmitterElement::Input(s)) => self == s,
1620 _ => false,
1621 };
1622
1623 match *self.input_type() {
1625 InputType::Submit(_) | InputType::Button(_) | InputType::Reset(_) if !is_submitter => {
1627 return (vec![], true);
1628 },
1629
1630 InputType::Radio(_) | InputType::Checkbox(_) if !self.Checked() => {
1632 return (vec![], true);
1633 },
1634
1635 InputType::Image(_) => return (vec![], true), _ => {
1640 if name.is_empty() {
1641 return (vec![], true);
1642 }
1643 },
1644 }
1645
1646 let datums = match *self.input_type() {
1647 InputType::Checkbox(_) | InputType::Radio(_) => {
1649 let field_value = self.Value();
1651 let value = if field_value.is_empty() {
1652 DOMString::from("on")
1653 } else {
1654 field_value
1655 };
1656 vec![FormDatum {
1658 ty,
1659 name,
1660 value: FormDatumValue::String(value),
1661 }]
1662 },
1663
1664 InputType::File(_) => {
1666 let mut datums = vec![];
1667
1668 let name = self.Name();
1670
1671 match self.GetFiles() {
1672 None => {
1674 datums.push(FormDatum {
1675 ty,
1678 name,
1679 value: FormDatumValue::String(DOMString::from("")),
1680 })
1681 },
1682 Some(fl) => {
1684 for f in fl.iter_files() {
1685 datums.push(FormDatum {
1686 ty: ty.clone(),
1687 name: name.clone(),
1688 value: FormDatumValue::File(DomRoot::from_ref(f)),
1689 });
1690 }
1691 },
1692 }
1693
1694 datums
1695 },
1696
1697 InputType::Hidden(_) if name.to_ascii_lowercase() == "_charset_" => {
1699 let charset = match encoding {
1701 None => DOMString::from("UTF-8"),
1702 Some(enc) => DOMString::from(enc.name()),
1703 };
1704 vec![FormDatum {
1706 ty,
1707 name,
1708 value: FormDatumValue::String(charset),
1709 }]
1710 },
1711
1712 _ => vec![FormDatum {
1714 ty,
1715 name,
1716 value: FormDatumValue::String(self.Value()),
1717 }],
1718 };
1719 (datums, false)
1720 }
1721
1722 fn radio_group_name(&self) -> Option<Atom> {
1724 self.upcast::<Element>()
1725 .get_name()
1726 .filter(|name| !name.is_empty())
1727 }
1728
1729 fn update_checked_state(&self, cx: &mut JSContext, checked: bool, dirty: bool) {
1730 self.upcast::<Element>()
1731 .set_state(ElementState::CHECKED, checked);
1732
1733 if dirty {
1734 self.checked_changed.set(true);
1735 }
1736
1737 if matches!(*self.input_type(), InputType::Radio(_)) && checked {
1738 broadcast_radio_checked(cx, self, self.radio_group_name().as_ref());
1739 }
1740
1741 self.upcast::<Node>().dirty(NodeDamage::Other);
1742 }
1743
1744 pub(crate) fn is_mutable(&self) -> bool {
1746 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
1749 }
1750
1751 pub(crate) fn reset(&self, cx: &mut JSContext) {
1761 self.value_dirty.set(false);
1762
1763 let mut value = self.DefaultValue();
1765 self.sanitize_value(&mut value);
1766 self.textinput.borrow_mut().set_content(value);
1767
1768 let input_type = &*self.input_type();
1769 if matches!(input_type, InputType::Radio(_) | InputType::Checkbox(_)) {
1770 self.update_checked_state(cx, self.DefaultChecked(), false);
1771 self.checked_changed.set(false);
1772 }
1773
1774 if matches!(input_type, InputType::File(_)) {
1775 input_type.as_specific().set_files(&FileList::new(
1776 &self.owner_window(),
1777 vec![],
1778 CanGc::from_cx(cx),
1779 ));
1780 }
1781
1782 self.value_changed(cx);
1783 }
1784
1785 pub(crate) fn clear(&self, cx: &mut JSContext) {
1788 self.value_dirty.set(false);
1790 self.checked_changed.set(false);
1791 self.textinput.borrow_mut().set_content(DOMString::from(""));
1793 self.update_checked_state(cx, self.DefaultChecked(), false);
1795 if self.input_type().as_specific().get_files().is_some() {
1797 let window = self.owner_window();
1798 let filelist = FileList::new(&window, vec![], CanGc::from_cx(cx));
1799 self.input_type().as_specific().set_files(&filelist);
1800 }
1801
1802 {
1805 let mut textinput = self.textinput.borrow_mut();
1806 let mut value = textinput.get_content();
1807 self.sanitize_value(&mut value);
1808 textinput.set_content(value);
1809 }
1810
1811 self.value_changed(cx);
1812 }
1813
1814 fn update_placeholder_shown_state(&self) {
1815 if !self.input_type().is_textual_or_password() {
1816 self.upcast::<Element>().set_placeholder_shown_state(false);
1817 } else {
1818 let has_placeholder = !self.placeholder.borrow().is_empty();
1819 let has_value = !self.textinput.borrow().is_empty();
1820 self.upcast::<Element>()
1821 .set_placeholder_shown_state(has_placeholder && !has_value);
1822 }
1823 }
1824
1825 pub(crate) fn select_files_for_webdriver(
1826 &self,
1827 test_paths: Vec<DOMString>,
1828 response_sender: GenericSender<Result<bool, ErrorStatus>>,
1829 ) {
1830 let mut stored_sender = self.pending_webdriver_response.borrow_mut();
1831 assert!(stored_sender.is_none());
1832
1833 *stored_sender = Some(PendingWebDriverResponse {
1834 response_sender,
1835 expected_file_count: test_paths.len(),
1836 });
1837
1838 self.input_type()
1839 .as_specific()
1840 .select_files(self, Some(test_paths));
1841 }
1842
1843 fn sanitize_value(&self, value: &mut DOMString) {
1845 self.input_type().as_specific().sanitize_value(self, value);
1846 }
1847
1848 fn selection(&self) -> TextControlSelection<'_, Self> {
1849 TextControlSelection::new(self, &self.textinput)
1850 }
1851
1852 fn implicit_submission(&self, cx: &mut js::context::JSContext) {
1854 let doc = self.owner_document();
1855 let node = doc.upcast::<Node>();
1856 let owner = self.form_owner();
1857 let form = match owner {
1858 None => return,
1859 Some(ref f) => f,
1860 };
1861
1862 if self.upcast::<Element>().click_in_progress() {
1863 return;
1864 }
1865 let submit_button = node
1866 .traverse_preorder(ShadowIncluding::No)
1867 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1868 .filter(|input| matches!(*input.input_type(), InputType::Submit(_)))
1869 .find(|r| r.form_owner() == owner);
1870 match submit_button {
1871 Some(ref button) => {
1872 if button.is_instance_activatable() {
1873 button
1876 .upcast::<Node>()
1877 .fire_synthetic_pointer_event_not_trusted(cx, atom!("click"));
1878 }
1879 },
1880 None => {
1881 let mut inputs = node
1882 .traverse_preorder(ShadowIncluding::No)
1883 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1884 .filter(|input| {
1885 input.form_owner() == owner &&
1886 matches!(
1887 *input.input_type(),
1888 InputType::Text(_) |
1889 InputType::Search(_) |
1890 InputType::Url(_) |
1891 InputType::Tel(_) |
1892 InputType::Email(_) |
1893 InputType::Password(_) |
1894 InputType::Date(_) |
1895 InputType::Month(_) |
1896 InputType::Week(_) |
1897 InputType::Time(_) |
1898 InputType::DatetimeLocal(_) |
1899 InputType::Number(_)
1900 )
1901 });
1902
1903 if inputs.nth(1).is_some() {
1904 return;
1906 }
1907 form.submit(
1908 cx,
1909 SubmittedFrom::NotFromForm,
1910 FormSubmitterElement::Form(form),
1911 );
1912 },
1913 }
1914 }
1915
1916 fn convert_string_to_number(&self, value: &str) -> Option<f64> {
1918 self.input_type()
1919 .as_specific()
1920 .convert_string_to_number(value)
1921 }
1922
1923 fn convert_number_to_string(&self, value: f64) -> Option<DOMString> {
1925 self.input_type()
1926 .as_specific()
1927 .convert_number_to_string(value)
1928 }
1929
1930 fn update_related_validity_states(&self, cx: &mut JSContext) {
1931 match *self.input_type() {
1932 InputType::Radio(_) => {
1933 perform_radio_group_validation(cx, self, self.radio_group_name().as_ref())
1934 },
1935 _ => {
1936 self.validity_state(cx)
1937 .perform_validation_and_update(cx, ValidationFlags::all());
1938 },
1939 }
1940 }
1941
1942 fn value_changed(&self, cx: &mut JSContext) {
1943 self.maybe_update_shared_selection();
1944 self.update_related_validity_states(cx);
1945 self.input_type().as_specific().update_shadow_tree(cx, self);
1946 }
1947
1948 fn show_the_picker_if_applicable(&self) {
1950 if !self.is_mutable() {
1954 return;
1955 }
1956
1957 self.input_type()
1960 .as_specific()
1961 .show_the_picker_if_applicable(self);
1962 }
1963
1964 pub(crate) fn handle_color_picker_response(
1965 &self,
1966 cx: &mut js::context::JSContext,
1967 response: Option<RgbColor>,
1968 ) {
1969 if let InputType::Color(ref color_input_type) = *self.input_type() {
1970 color_input_type.handle_color_picker_response(cx, self, response)
1971 }
1972 }
1973
1974 pub(crate) fn handle_file_picker_response(
1975 &self,
1976 cx: &mut js::context::JSContext,
1977 response: Option<Vec<SelectedFile>>,
1978 ) {
1979 if let InputType::File(ref file_input_type) = *self.input_type() {
1980 file_input_type.handle_file_picker_response(cx, self, response)
1981 }
1982 }
1983
1984 fn handle_focus_event(&self, event: &FocusEvent) {
1985 let event_type = event.upcast::<Event>().type_();
1986 if *event_type == *"blur" {
1987 self.owner_document()
1988 .embedder_controls()
1989 .hide_embedder_control(self.upcast());
1990 } else if *event_type == *"focus" {
1991 let input_type = &*self.input_type();
1992 let Ok(input_method_type) = input_type.try_into() else {
1993 return;
1994 };
1995
1996 self.owner_document()
1997 .embedder_controls()
1998 .show_embedder_control(
1999 ControlElement::Ime(DomRoot::from_ref(self.upcast())),
2000 EmbedderControlRequest::InputMethod(InputMethodRequest {
2001 input_method_type,
2002 text: String::from(self.Value()),
2003 insertion_point: self.GetSelectionEnd(),
2004 multiline: false,
2005 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
2007 }),
2008 None,
2009 );
2010 }
2011 }
2012
2013 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
2014 if mouse_event.upcast::<Event>().DefaultPrevented() {
2015 return;
2016 }
2017
2018 if !self.input_type().is_textual_or_password() || self.textinput.borrow().is_empty() {
2021 return;
2022 }
2023 let node = self.upcast();
2024 if self
2025 .textinput
2026 .borrow_mut()
2027 .handle_mouse_event(node, mouse_event)
2028 {
2029 self.maybe_update_shared_selection();
2030 }
2031 }
2032}
2033
2034impl VirtualMethods for HTMLInputElement {
2035 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2036 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2037 }
2038
2039 fn attribute_mutated(
2040 &self,
2041 cx: &mut JSContext,
2042 attr: AttrRef<'_>,
2043 mutation: AttributeMutation,
2044 ) {
2045 let could_have_had_embedder_control = self.may_have_embedder_control();
2046
2047 self.super_type()
2048 .unwrap()
2049 .attribute_mutated(cx, attr, mutation);
2050
2051 match *attr.local_name() {
2052 local_name!("disabled") => {
2053 let disabled_state = match mutation {
2054 AttributeMutation::Set(None, _) => true,
2055 AttributeMutation::Set(Some(_), _) => {
2056 return;
2058 },
2059 AttributeMutation::Removed => false,
2060 };
2061 let el = self.upcast::<Element>();
2062 el.set_disabled_state(disabled_state);
2063 el.set_enabled_state(!disabled_state);
2064 el.check_ancestors_disabled_state_for_form_control();
2065
2066 if self.input_type().is_textual() {
2067 let read_write = !(self.ReadOnly() || el.disabled_state());
2068 el.set_read_write_state(read_write);
2069 }
2070 },
2071 local_name!("checked") if !self.checked_changed.get() => {
2072 let checked_state = match mutation {
2073 AttributeMutation::Set(None, _) => true,
2074 AttributeMutation::Set(Some(_), _) => {
2075 return;
2077 },
2078 AttributeMutation::Removed => false,
2079 };
2080 self.update_checked_state(cx, checked_state, false);
2081 },
2082 local_name!("size") => {
2083 let size = mutation.new_value(attr).map(|value| value.as_uint());
2084 self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
2085 },
2086 local_name!("type") => {
2087 match mutation {
2088 AttributeMutation::Set(previous_value, _) => {
2089 if previous_value
2093 .is_some_and(|previous_value| **previous_value == **attr.value())
2094 {
2095 return;
2096 }
2097
2098 let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
2099 let previously_selectable = self.selection_api_applies();
2100
2101 *self.input_type.borrow_mut() =
2102 InputType::new_from_atom(attr.value().as_atom());
2103 self.is_textual_or_password
2104 .set(self.input_type().is_textual_or_password());
2105
2106 let element = self.upcast::<Element>();
2107 if self.input_type().is_textual() {
2108 let read_write = !(self.ReadOnly() || element.disabled_state());
2109 element.set_read_write_state(read_write);
2110 } else {
2111 element.set_read_write_state(false);
2112 }
2113
2114 let new_value_mode = self.value_mode();
2115 match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
2116 (&ValueMode::Value, false, ValueMode::Default) |
2118 (&ValueMode::Value, false, ValueMode::DefaultOn) => {
2119 self.SetValue(cx, old_idl_value)
2120 .expect("Failed to set input value on type change to a default ValueMode.");
2121 },
2122
2123 (_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
2125 self.SetValue(
2126 cx,
2127 self.upcast::<Element>()
2128 .get_attribute_string_value(&local_name!("value"))
2129 .unwrap_or_default()
2130 .into(),
2131 )
2132 .expect(
2133 "Failed to set input value on type change to ValueMode::Value.",
2134 );
2135 self.value_dirty.set(false);
2136 },
2137
2138 (_, _, ValueMode::Filename)
2140 if old_value_mode != ValueMode::Filename =>
2141 {
2142 self.SetValue(cx, DOMString::from(""))
2143 .expect("Failed to set input value on type change to ValueMode::Filename.");
2144 },
2145 _ => {},
2146 }
2147
2148 self.input_type().as_specific().signal_type_change(cx, self);
2150
2151 let mut textinput = self.textinput.borrow_mut();
2153 let mut value = textinput.get_content();
2154 self.sanitize_value(&mut value);
2155 textinput.set_content(value);
2156 self.upcast::<Node>().dirty(NodeDamage::Other);
2157
2158 if self.does_minmaxlength_apply() {
2160 textinput.set_min_length(
2161 self.MinLength().to_usize().map(Utf16CodeUnitLength),
2162 );
2163 textinput.set_max_length(
2164 self.MaxLength().to_usize().map(Utf16CodeUnitLength),
2165 );
2166 } else {
2167 textinput.set_min_length(None);
2168 textinput.set_max_length(None);
2169 }
2170
2171 if !previously_selectable && self.selection_api_applies() {
2173 textinput.clear_selection_to_start();
2174 }
2175 },
2176 AttributeMutation::Removed => {
2177 self.input_type().as_specific().signal_type_change(cx, self);
2178 *self.input_type.borrow_mut() = InputType::new_text();
2179 self.is_textual_or_password
2180 .set(self.input_type().is_textual_or_password());
2181
2182 let element = self.upcast::<Element>();
2183 let read_write = !(self.ReadOnly() || element.disabled_state());
2184 element.set_read_write_state(read_write);
2185 },
2186 }
2187
2188 self.update_placeholder_shown_state();
2189 self.input_type()
2190 .as_specific()
2191 .update_placeholder_contents(cx, self);
2192 },
2193 local_name!("value") if !self.value_dirty.get() => {
2194 let value = mutation.new_value(attr).map(|value| (**value).to_owned());
2198 let mut value = value.map_or(DOMString::new(), DOMString::from);
2199
2200 self.sanitize_value(&mut value);
2201 self.textinput.borrow_mut().set_content(value);
2202 self.update_placeholder_shown_state();
2203 },
2204 local_name!("maxlength") if self.does_minmaxlength_apply() => match *attr.value() {
2205 AttrValue::Int(_, value) => {
2206 let mut textinput = self.textinput.borrow_mut();
2207
2208 if value < 0 {
2209 textinput.set_max_length(None);
2210 } else {
2211 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
2212 }
2213 },
2214 _ => panic!("Expected an AttrValue::Int"),
2215 },
2216 local_name!("minlength") if self.does_minmaxlength_apply() => match *attr.value() {
2217 AttrValue::Int(_, value) => {
2218 let mut textinput = self.textinput.borrow_mut();
2219
2220 if value < 0 {
2221 textinput.set_min_length(None);
2222 } else {
2223 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
2224 }
2225 },
2226 _ => panic!("Expected an AttrValue::Int"),
2227 },
2228 local_name!("placeholder") => {
2229 {
2230 let mut placeholder = self.placeholder.borrow_mut();
2231 placeholder.clear();
2232 if let AttributeMutation::Set(..) = mutation {
2233 placeholder
2234 .extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
2235 }
2236 }
2237 self.update_placeholder_shown_state();
2238 self.input_type()
2239 .as_specific()
2240 .update_placeholder_contents(cx, self);
2241 },
2242 local_name!("readonly") => {
2243 if self.input_type().is_textual() {
2244 let el = self.upcast::<Element>();
2245 match mutation {
2246 AttributeMutation::Set(..) => {
2247 el.set_read_write_state(false);
2248 },
2249 AttributeMutation::Removed => {
2250 el.set_read_write_state(!el.disabled_state());
2251 },
2252 }
2253 }
2254 },
2255 local_name!("form") => {
2256 self.form_attribute_mutated(cx, mutation);
2257 },
2258 _ => {
2259 self.input_type()
2260 .as_specific()
2261 .attribute_mutated(cx, self, attr, mutation);
2262 },
2263 }
2264
2265 self.value_changed(cx);
2266
2267 if could_have_had_embedder_control && !self.may_have_embedder_control() {
2268 self.owner_document()
2269 .embedder_controls()
2270 .hide_embedder_control(self.upcast());
2271 }
2272 }
2273
2274 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2275 match *name {
2276 local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
2277 local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
2278 local_name!("type") => AttrValue::from_atomic(value.into()),
2279 local_name!("maxlength") => {
2280 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
2281 },
2282 local_name!("minlength") => {
2283 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
2284 },
2285 _ => self
2286 .super_type()
2287 .unwrap()
2288 .parse_plain_attribute(name, value),
2289 }
2290 }
2291
2292 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2293 if let Some(s) = self.super_type() {
2294 s.bind_to_tree(cx, context);
2295 }
2296 self.upcast::<Element>()
2297 .check_ancestors_disabled_state_for_form_control();
2298
2299 self.input_type()
2300 .as_specific()
2301 .bind_to_tree(cx, self, context);
2302
2303 self.value_changed(cx);
2304 }
2305
2306 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
2307 let form_owner = self.form_owner();
2308 self.super_type().unwrap().unbind_from_tree(cx, context);
2309
2310 let node = self.upcast::<Node>();
2311 let el = self.upcast::<Element>();
2312 if node
2313 .ancestors()
2314 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
2315 {
2316 el.check_ancestors_disabled_state_for_form_control();
2317 } else {
2318 el.check_disabled_attribute();
2319 }
2320
2321 self.input_type()
2322 .as_specific()
2323 .unbind_from_tree(cx, self, form_owner, context);
2324
2325 self.validity_state(cx)
2326 .perform_validation_and_update(cx, ValidationFlags::all());
2327 }
2328
2329 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
2335 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
2336 self.handle_mouse_event(mouse_event);
2337 event.mark_as_handled();
2338 } else if event.type_() == atom!("keydown") &&
2339 !event.DefaultPrevented() &&
2340 self.input_type().is_textual_or_password()
2341 {
2342 if let Some(keyevent) = event.downcast::<KeyboardEvent>() {
2343 let action = self.textinput.borrow_mut().handle_keydown(keyevent);
2346 self.handle_key_reaction(cx, action, event);
2347 }
2348 } else if (event.type_() == atom!("compositionstart") ||
2349 event.type_() == atom!("compositionupdate") ||
2350 event.type_() == atom!("compositionend")) &&
2351 self.input_type().is_textual_or_password()
2352 {
2353 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
2354 if event.type_() == atom!("compositionend") {
2355 let action = self
2356 .textinput
2357 .borrow_mut()
2358 .handle_compositionend(compositionevent);
2359 self.handle_key_reaction(cx, action, event);
2360 self.upcast::<Node>().dirty(NodeDamage::Other);
2361 self.update_placeholder_shown_state();
2362 } else if event.type_() == atom!("compositionupdate") {
2363 let action = self
2364 .textinput
2365 .borrow_mut()
2366 .handle_compositionupdate(compositionevent);
2367 self.handle_key_reaction(cx, action, event);
2368 self.upcast::<Node>().dirty(NodeDamage::Other);
2369 self.update_placeholder_shown_state();
2370 } else if event.type_() == atom!("compositionstart") {
2371 self.update_placeholder_shown_state();
2373 }
2374 event.mark_as_handled();
2375 }
2376 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
2377 let reaction = self
2378 .textinput
2379 .borrow_mut()
2380 .handle_clipboard_event(clipboard_event);
2381 let flags = reaction.flags;
2382 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
2383 self.owner_document().event_handler().fire_clipboard_event(
2384 cx,
2385 None,
2386 ClipboardEventType::Change,
2387 );
2388 }
2389 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
2390 self.textinput.borrow().queue_input_event(
2391 self.upcast(),
2392 reaction.text,
2393 IsComposing::NotComposing,
2394 reaction.input_type,
2395 );
2396 }
2397 if !flags.is_empty() {
2398 event.mark_as_handled();
2399 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
2400 }
2401 } else if let Some(event) = event.downcast::<FocusEvent>() {
2402 self.handle_focus_event(event)
2403 }
2404
2405 self.value_changed(cx);
2406
2407 if let Some(super_type) = self.super_type() {
2408 super_type.handle_event(cx, event);
2409 }
2410 }
2411
2412 fn cloning_steps(
2414 &self,
2415 cx: &mut JSContext,
2416 copy: &Node,
2417 maybe_doc: Option<&Document>,
2418 clone_children: CloneChildrenFlag,
2419 ) {
2420 if let Some(s) = self.super_type() {
2421 s.cloning_steps(cx, copy, maybe_doc, clone_children);
2422 }
2423 let elem = copy.downcast::<HTMLInputElement>().unwrap();
2424 elem.value_dirty.set(self.value_dirty.get());
2425 elem.checked_changed.set(self.checked_changed.get());
2426 elem.upcast::<Element>()
2427 .set_state(ElementState::CHECKED, self.Checked());
2428 elem.upcast::<Element>()
2431 .set_state(ElementState::INDETERMINATE, self.Indeterminate());
2432 elem.textinput
2433 .borrow_mut()
2434 .set_content(self.textinput.borrow().get_content());
2435 self.value_changed(cx);
2436 }
2437}
2438
2439impl FormControl for HTMLInputElement {
2440 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2441 self.form_owner.get()
2442 }
2443
2444 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2445 self.form_owner.set(form);
2446 }
2447
2448 fn to_element(&self) -> &Element {
2449 self.upcast::<Element>()
2450 }
2451}
2452
2453impl Validatable for HTMLInputElement {
2454 fn as_element(&self) -> &Element {
2455 self.upcast()
2456 }
2457
2458 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
2459 self.validity_state
2460 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
2461 }
2462
2463 fn is_instance_validatable(&self) -> bool {
2464 match *self.input_type() {
2471 InputType::Hidden(_) | InputType::Button(_) | InputType::Reset(_) => false,
2472 _ => {
2473 !(self.upcast::<Element>().disabled_state() ||
2474 self.ReadOnly() ||
2475 is_barred_by_datalist_ancestor(self.upcast()))
2476 },
2477 }
2478 }
2479
2480 fn perform_validation(
2481 &self,
2482 cx: &mut JSContext,
2483 validate_flags: ValidationFlags,
2484 ) -> ValidationFlags {
2485 let mut failed_flags = ValidationFlags::empty();
2486 let value = self.Value();
2487
2488 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
2489 self.suffers_from_being_missing(&value)
2490 {
2491 failed_flags.insert(ValidationFlags::VALUE_MISSING);
2492 }
2493
2494 if validate_flags.contains(ValidationFlags::TYPE_MISMATCH) &&
2495 self.suffers_from_type_mismatch(&value)
2496 {
2497 failed_flags.insert(ValidationFlags::TYPE_MISMATCH);
2498 }
2499
2500 if validate_flags.contains(ValidationFlags::PATTERN_MISMATCH) &&
2501 self.suffers_from_pattern_mismatch(&value, CanGc::from_cx(cx))
2502 {
2503 failed_flags.insert(ValidationFlags::PATTERN_MISMATCH);
2504 }
2505
2506 if validate_flags.contains(ValidationFlags::BAD_INPUT) &&
2507 self.suffers_from_bad_input(&value)
2508 {
2509 failed_flags.insert(ValidationFlags::BAD_INPUT);
2510 }
2511
2512 if validate_flags.intersects(ValidationFlags::TOO_LONG | ValidationFlags::TOO_SHORT) {
2513 failed_flags |= self.suffers_from_length_issues(&value);
2514 }
2515
2516 if validate_flags.intersects(
2517 ValidationFlags::RANGE_UNDERFLOW |
2518 ValidationFlags::RANGE_OVERFLOW |
2519 ValidationFlags::STEP_MISMATCH,
2520 ) {
2521 failed_flags |= self.suffers_from_range_issues(&value);
2522 }
2523
2524 failed_flags & validate_flags
2525 }
2526}
2527
2528impl Activatable for HTMLInputElement {
2529 fn as_element(&self) -> &Element {
2530 self.upcast()
2531 }
2532
2533 fn is_instance_activatable(&self) -> bool {
2534 match *self.input_type() {
2535 InputType::Submit(_) |
2542 InputType::Reset(_) |
2543 InputType::File(_) |
2544 InputType::Image(_) |
2545 InputType::Button(_) => self.is_mutable(),
2546 InputType::Checkbox(_) | InputType::Radio(_) | InputType::Color(_) => true,
2550 _ => false,
2551 }
2552 }
2553
2554 fn legacy_pre_activation_behavior(&self, cx: &mut JSContext) -> Option<InputActivationState> {
2556 let activation_state = self
2557 .input_type()
2558 .as_specific()
2559 .legacy_pre_activation_behavior(cx, self);
2560
2561 if activation_state.is_some() {
2562 self.value_changed(cx);
2563 }
2564
2565 activation_state
2566 }
2567
2568 fn legacy_canceled_activation_behavior(
2570 &self,
2571 cx: &mut JSContext,
2572 cache: Option<InputActivationState>,
2573 ) {
2574 let ty = self.input_type();
2576 let cache = match cache {
2577 Some(cache) => {
2578 if (cache.was_radio && !matches!(*ty, InputType::Radio(_))) ||
2579 (cache.was_checkbox && !matches!(*ty, InputType::Checkbox(_)))
2580 {
2581 return;
2584 }
2585 cache
2586 },
2587 None => {
2588 return;
2589 },
2590 };
2591
2592 ty.as_specific()
2594 .legacy_canceled_activation_behavior(cx, self, cache);
2595
2596 self.value_changed(cx);
2597 }
2598
2599 fn activation_behavior(
2601 &self,
2602 cx: &mut js::context::JSContext,
2603 event: &Event,
2604 target: &EventTarget,
2605 ) {
2606 let input_activation_type = {
2607 let input_type = self.input_type();
2608 InputActivationType::new_from_input_type(&input_type)
2609 };
2610
2611 if let Some(input_activation_type) = input_activation_type {
2612 input_activation_type
2613 .as_specific()
2614 .activation_behavior(cx, self, event, target);
2615 }
2616 }
2617}
2618
2619fn compile_pattern(
2623 cx: SafeJSContext,
2624 pattern_str: &str,
2625 out_regex: MutableHandleObject,
2626 can_gc: CanGc,
2627) -> bool {
2628 if check_js_regex_syntax(cx, pattern_str, can_gc) {
2630 let pattern_str = format!("^(?:{})$", pattern_str);
2632 let flags = RegExpFlags {
2633 flags_: RegExpFlag_UnicodeSets,
2634 };
2635 new_js_regex(cx, &pattern_str, flags, out_regex, can_gc)
2636 } else {
2637 false
2638 }
2639}
2640
2641#[expect(unsafe_code)]
2642fn check_js_regex_syntax(cx: SafeJSContext, pattern: &str, _can_gc: CanGc) -> bool {
2645 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2646 unsafe {
2647 rooted!(in(*cx) let mut exception = UndefinedValue());
2648
2649 let valid = CheckRegExpSyntax(
2650 *cx,
2651 pattern.as_ptr(),
2652 pattern.len(),
2653 RegExpFlags {
2654 flags_: RegExpFlag_UnicodeSets,
2655 },
2656 exception.handle_mut(),
2657 );
2658
2659 if !valid {
2660 JS_ClearPendingException(*cx);
2661 return false;
2662 }
2663
2664 exception.is_undefined()
2667 }
2668}
2669
2670#[expect(unsafe_code)]
2671pub(crate) fn new_js_regex(
2672 cx: SafeJSContext,
2673 pattern: &str,
2674 flags: RegExpFlags,
2675 mut out_regex: MutableHandleObject,
2676 _can_gc: CanGc,
2677) -> bool {
2678 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2679 unsafe {
2680 out_regex.set(NewUCRegExpObject(
2681 *cx,
2682 pattern.as_ptr(),
2683 pattern.len(),
2684 flags,
2685 ));
2686 if out_regex.is_null() {
2687 JS_ClearPendingException(*cx);
2688 return false;
2689 }
2690 }
2691 true
2692}
2693
2694#[expect(unsafe_code)]
2695fn matches_js_regex(
2696 cx: SafeJSContext,
2697 regex_obj: HandleObject,
2698 value: &str,
2699 _can_gc: CanGc,
2700) -> Result<bool, ()> {
2701 let mut value: Vec<u16> = value.encode_utf16().collect();
2702
2703 unsafe {
2704 let mut is_regex = false;
2705 assert!(ObjectIsRegExp(*cx, regex_obj, &mut is_regex));
2706 assert!(is_regex);
2707
2708 rooted!(in(*cx) let mut rval = UndefinedValue());
2709 let mut index = 0;
2710
2711 let ok = ExecuteRegExpNoStatics(
2712 *cx,
2713 regex_obj,
2714 value.as_mut_ptr(),
2715 value.len(),
2716 &mut index,
2717 true,
2718 rval.handle_mut(),
2719 );
2720
2721 if ok {
2722 Ok(!rval.is_null())
2723 } else {
2724 JS_ClearPendingException(*cx);
2725 Err(())
2726 }
2727 }
2728}
2729
2730#[derive(MallocSizeOf)]
2734struct PendingWebDriverResponse {
2735 response_sender: GenericSender<Result<bool, ErrorStatus>>,
2737 expected_file_count: usize,
2739}
2740
2741impl PendingWebDriverResponse {
2742 fn finish(self, number_files_selected: usize) {
2743 if number_files_selected == self.expected_file_count {
2744 let _ = self.response_sender.send(Ok(false));
2745 } else {
2746 let _ = self.response_sender.send(Err(ErrorStatus::InvalidArgument));
2749 }
2750 }
2751}