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;
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;
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;
33
34use crate::dom::activation::Activatable;
35use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
36use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
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::compositionevent::CompositionEvent;
47use crate::dom::document::Document;
48use crate::dom::document_embedder_controls::ControlElement;
49use crate::dom::element::attributes::storage::AttrRef;
50use crate::dom::element::{AttributeMutation, Element};
51use crate::dom::event::Event;
52use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
53use crate::dom::eventtarget::EventTarget;
54use crate::dom::filelist::FileList;
55use crate::dom::html::form_controls::input_type::radio_input_type::{
56 broadcast_radio_checked, perform_radio_group_validation,
57};
58use crate::dom::html::form_controls::input_type::{InputActivationType, InputType};
59use crate::dom::html::form_controls::text_control::TextControlElement;
60use crate::dom::html::form_controls::text_input::{KeyReaction, Lines, TextInput};
61use crate::dom::html::htmldatalistelement::HTMLDataListElement;
62use crate::dom::html::htmlelement::HTMLElement;
63use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
64use crate::dom::html::htmlformelement::{
65 FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, SubmittedFrom,
66};
67use crate::dom::inputevent::HitTestResult;
68use crate::dom::iterators::ShadowIncluding;
69use crate::dom::keyboardevent::KeyboardEvent;
70use crate::dom::node::virtualmethods::VirtualMethods;
71use crate::dom::node::{
72 BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
73};
74use crate::dom::nodelist::NodeList;
75use crate::dom::text_input::EmbedderClipboardProvider;
76use crate::dom::types::{FocusEvent, MouseEvent};
77use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
78use crate::dom::validitystate::{ValidationFlags, ValidityState};
79use crate::realms::enter_auto_realm;
80
81#[derive(Debug, PartialEq)]
82pub(crate) enum ValueMode {
83 Value,
85
86 Default,
88
89 DefaultOn,
91
92 Filename,
94}
95
96#[derive(Debug, PartialEq)]
97enum StepDirection {
98 Up,
99 Down,
100}
101
102#[dom_struct]
103pub(crate) struct HTMLInputElement {
104 htmlelement: HTMLElement,
105 input_type: DomRefCell<InputType>,
106
107 is_textual_or_password: Cell<bool>,
110
111 placeholder: DomRefCell<DOMString>,
112 size: Cell<u32>,
113 maxlength: Cell<i32>,
114 minlength: Cell<i32>,
115 checkedness: Cell<bool>,
119 checked_changed: Cell<bool>,
121 #[no_trace]
122 text_input: DomRefCell<TextInput<EmbedderClipboardProvider>>,
123 form_owner: MutNullableDom<HTMLFormElement>,
124 labels_node_list: MutNullableDom<NodeList>,
125 validity_state: MutNullableDom<ValidityState>,
126 #[no_trace]
127 pending_webdriver_response: RefCell<Option<PendingWebDriverResponse>>,
128 value_dirty: Cell<bool>,
130
131 has_scheduled_selectionchange_event: Cell<bool>,
133}
134
135#[derive(JSTraceable)]
136pub(crate) struct InputActivationState {
137 pub(crate) indeterminate: bool,
138 pub(crate) checked: bool,
139 pub(crate) checked_radio: Option<DomRoot<HTMLInputElement>>,
140 pub(crate) was_radio: bool,
141 pub(crate) was_checkbox: bool,
142 }
144
145static DEFAULT_INPUT_SIZE: u32 = 20;
146static DEFAULT_MAX_LENGTH: i32 = -1;
147static DEFAULT_MIN_LENGTH: i32 = -1;
148
149#[expect(non_snake_case)]
150impl HTMLInputElement {
151 fn new_inherited(
152 local_name: LocalName,
153 prefix: Option<Prefix>,
154 document: &Document,
155 ) -> HTMLInputElement {
156 let embedder_sender = document
157 .window()
158 .as_global_scope()
159 .script_to_embedder_chan()
160 .clone();
161 HTMLInputElement {
162 htmlelement: HTMLElement::new_inherited_with_state(
163 ElementState::ENABLED | ElementState::READWRITE,
164 local_name,
165 prefix,
166 document,
167 ),
168 input_type: DomRefCell::new(InputType::new_text()),
169 is_textual_or_password: Cell::new(true),
170 placeholder: DomRefCell::new(DOMString::new()),
171 checkedness: Cell::new(false),
172 checked_changed: Cell::new(false),
173 maxlength: Cell::new(DEFAULT_MAX_LENGTH),
174 minlength: Cell::new(DEFAULT_MIN_LENGTH),
175 size: Cell::new(DEFAULT_INPUT_SIZE),
176 text_input: DomRefCell::new(TextInput::new(
177 Lines::Single,
178 DOMString::new(),
179 EmbedderClipboardProvider {
180 embedder_sender,
181 webview_id: document.webview_id(),
182 },
183 )),
184 value_dirty: Cell::new(false),
185 form_owner: Default::default(),
186 labels_node_list: MutNullableDom::new(None),
187 validity_state: Default::default(),
188 pending_webdriver_response: Default::default(),
189 has_scheduled_selectionchange_event: Default::default(),
190 }
191 }
192
193 pub(crate) fn new(
194 cx: &mut JSContext,
195 local_name: LocalName,
196 prefix: Option<Prefix>,
197 document: &Document,
198 proto: Option<HandleObject>,
199 ) -> DomRoot<HTMLInputElement> {
200 Node::reflect_node_with_proto(
201 cx,
202 Box::new(HTMLInputElement::new_inherited(
203 local_name, prefix, document,
204 )),
205 document,
206 proto,
207 )
208 }
209
210 pub(crate) fn auto_directionality(&self) -> Option<String> {
211 match *self.input_type() {
212 InputType::Text(_) | InputType::Search(_) | InputType::Url(_) | InputType::Email(_) => {
213 let value: String = String::from(self.Value());
214 Some(HTMLInputElement::directionality_from_value(&value))
215 },
216 _ => None,
217 }
218 }
219
220 pub(crate) fn directionality_from_value(value: &str) -> String {
221 if HTMLInputElement::is_first_strong_character_rtl(value) {
222 "rtl".to_owned()
223 } else {
224 "ltr".to_owned()
225 }
226 }
227
228 fn is_first_strong_character_rtl(value: &str) -> bool {
229 for ch in value.chars() {
230 return match bidi_class(ch) {
231 BidiClass::L => false,
232 BidiClass::AL => true,
233 BidiClass::R => true,
234 _ => continue,
235 };
236 }
237 false
238 }
239
240 pub(crate) fn value_mode(&self) -> ValueMode {
243 match *self.input_type() {
244 InputType::Submit(_) |
245 InputType::Reset(_) |
246 InputType::Button(_) |
247 InputType::Image(_) |
248 InputType::Hidden(_) => ValueMode::Default,
249
250 InputType::Checkbox(_) | InputType::Radio(_) => ValueMode::DefaultOn,
251
252 InputType::Color(_) |
253 InputType::Date(_) |
254 InputType::DatetimeLocal(_) |
255 InputType::Email(_) |
256 InputType::Month(_) |
257 InputType::Number(_) |
258 InputType::Password(_) |
259 InputType::Range(_) |
260 InputType::Search(_) |
261 InputType::Tel(_) |
262 InputType::Text(_) |
263 InputType::Time(_) |
264 InputType::Url(_) |
265 InputType::Week(_) => ValueMode::Value,
266
267 InputType::File(_) => ValueMode::Filename,
268 }
269 }
270
271 #[inline]
272 pub(crate) fn input_type(&self) -> Ref<'_, InputType> {
273 self.input_type.borrow()
274 }
275
276 pub(crate) fn is_nontypeable(&self) -> bool {
278 matches!(
279 *self.input_type(),
280 InputType::Button(_) |
281 InputType::Checkbox(_) |
282 InputType::Color(_) |
283 InputType::File(_) |
284 InputType::Hidden(_) |
285 InputType::Image(_) |
286 InputType::Radio(_) |
287 InputType::Range(_) |
288 InputType::Reset(_) |
289 InputType::Submit(_)
290 )
291 }
292
293 #[inline]
294 pub(crate) fn is_submit_button(&self) -> bool {
295 matches!(
296 *self.input_type(),
297 InputType::Submit(_) | InputType::Image(_)
298 )
299 }
300
301 pub(crate) fn is_auto_directionality_form_associated_element(&self) -> bool {
303 matches!(
304 *self.input_type(),
305 InputType::Hidden(_) |
306 InputType::Text(_) |
307 InputType::Search(_) |
308 InputType::Tel(_) |
309 InputType::Url(_) |
310 InputType::Email(_) |
311 InputType::Password(_) |
312 InputType::Submit(_) |
313 InputType::Reset(_) |
314 InputType::Button(_)
315 )
316 }
317
318 fn does_minmaxlength_apply(&self) -> bool {
319 matches!(
320 *self.input_type(),
321 InputType::Text(_) |
322 InputType::Search(_) |
323 InputType::Url(_) |
324 InputType::Tel(_) |
325 InputType::Email(_) |
326 InputType::Password(_)
327 )
328 }
329
330 fn does_pattern_apply(&self) -> bool {
331 matches!(
332 *self.input_type(),
333 InputType::Text(_) |
334 InputType::Search(_) |
335 InputType::Url(_) |
336 InputType::Tel(_) |
337 InputType::Email(_) |
338 InputType::Password(_)
339 )
340 }
341
342 fn does_multiple_apply(&self) -> bool {
343 matches!(*self.input_type(), InputType::Email(_))
344 }
345
346 fn does_value_as_number_apply(&self) -> bool {
349 matches!(
350 *self.input_type(),
351 InputType::Date(_) |
352 InputType::Month(_) |
353 InputType::Week(_) |
354 InputType::Time(_) |
355 InputType::DatetimeLocal(_) |
356 InputType::Number(_) |
357 InputType::Range(_)
358 )
359 }
360
361 fn does_value_as_date_apply(&self) -> bool {
362 matches!(
363 *self.input_type(),
364 InputType::Date(_) | InputType::Month(_) | InputType::Week(_) | InputType::Time(_)
365 )
366 }
367
368 pub(crate) fn allowed_value_step(&self) -> Option<f64> {
370 let default_step = self.default_step()?;
373
374 let Some(step_value) = self
377 .upcast::<Element>()
378 .get_attribute_string_value(&local_name!("step"))
379 else {
380 return Some(default_step * self.step_scale_factor());
381 };
382
383 if step_value.eq_ignore_ascii_case("any") {
386 return None;
387 }
388
389 let Some(parsed_value) =
393 parse_floating_point_number(&step_value).filter(|value| *value > 0.0)
394 else {
395 return Some(default_step * self.step_scale_factor());
396 };
397
398 Some(parsed_value * self.step_scale_factor())
402 }
403
404 pub(crate) fn minimum(&self) -> Option<f64> {
406 self.upcast::<Element>()
407 .get_attribute_string_value(&local_name!("min"))
408 .and_then(|value| self.convert_string_to_number(&value))
409 .or_else(|| self.default_minimum())
410 }
411
412 pub(crate) fn maximum(&self) -> Option<f64> {
414 self.upcast::<Element>()
415 .get_attribute_string_value(&local_name!("max"))
416 .and_then(|value| self.convert_string_to_number(&value))
417 .or_else(|| self.default_maximum())
418 }
419
420 pub(crate) fn stepped_minimum(&self) -> Option<f64> {
423 match (self.minimum(), self.allowed_value_step()) {
424 (Some(min), Some(allowed_step)) => {
425 let step_base = self.step_base();
426 let nsteps = (min - step_base) / allowed_step;
428 Some(step_base + (allowed_step * nsteps.ceil()))
430 },
431 (_, _) => None,
432 }
433 }
434
435 pub(crate) fn stepped_maximum(&self) -> Option<f64> {
438 match (self.maximum(), self.allowed_value_step()) {
439 (Some(max), Some(allowed_step)) => {
440 let step_base = self.step_base();
441 let nsteps = (max - step_base) / allowed_step;
443 Some(step_base + (allowed_step * nsteps.floor()))
445 },
446 (_, _) => None,
447 }
448 }
449
450 fn default_minimum(&self) -> Option<f64> {
452 match *self.input_type() {
453 InputType::Range(_) => Some(0.0),
454 _ => None,
455 }
456 }
457
458 fn default_maximum(&self) -> Option<f64> {
460 match *self.input_type() {
461 InputType::Range(_) => Some(100.0),
462 _ => None,
463 }
464 }
465
466 pub(crate) fn default_range_value(&self) -> f64 {
468 let min = self.minimum().unwrap_or(0.0);
469 let max = self.maximum().unwrap_or(100.0);
470 if max < min {
471 min
472 } else {
473 min + (max - min) * 0.5
474 }
475 }
476
477 fn default_step(&self) -> Option<f64> {
479 match *self.input_type() {
480 InputType::Date(_) => Some(1.0),
481 InputType::Month(_) => Some(1.0),
482 InputType::Week(_) => Some(1.0),
483 InputType::Time(_) => Some(60.0),
484 InputType::DatetimeLocal(_) => Some(60.0),
485 InputType::Number(_) => Some(1.0),
486 InputType::Range(_) => Some(1.0),
487 _ => None,
488 }
489 }
490
491 fn step_scale_factor(&self) -> f64 {
493 match *self.input_type() {
494 InputType::Date(_) => 86400000.0,
495 InputType::Month(_) => 1.0,
496 InputType::Week(_) => 604800000.0,
497 InputType::Time(_) => 1000.0,
498 InputType::DatetimeLocal(_) => 1000.0,
499 InputType::Number(_) => 1.0,
500 InputType::Range(_) => 1.0,
501 _ => unreachable!(),
502 }
503 }
504
505 pub(crate) fn step_base(&self) -> f64 {
507 if let Some(minimum) = self
511 .upcast::<Element>()
512 .get_attribute_string_value(&local_name!("min"))
513 .and_then(|value| self.convert_string_to_number(&value))
514 {
515 return minimum;
516 }
517
518 if let Some(value) = self
522 .upcast::<Element>()
523 .get_attribute_string_value(&local_name!("value"))
524 .and_then(|value| self.convert_string_to_number(&value))
525 {
526 return value;
527 }
528
529 if let Some(default_step_base) = self.default_step_base() {
531 return default_step_base;
532 }
533
534 0.0
536 }
537
538 fn default_step_base(&self) -> Option<f64> {
540 match *self.input_type() {
541 InputType::Week(_) => Some(-259200000.0),
542 _ => None,
543 }
544 }
545
546 fn step_up_or_down(&self, cx: &mut JSContext, n: i32, dir: StepDirection) -> ErrorResult {
550 if !self.does_value_as_number_apply() {
553 return Err(Error::InvalidState(Some(
554 "Input element does not implement `stepDown()` or `stepUp()`".into(),
555 )));
556 }
557 let step_base = self.step_base();
558
559 let Some(allowed_value_step) = self.allowed_value_step() else {
561 return Err(Error::InvalidState(Some(
562 "Input element does not have a value step".into(),
563 )));
564 };
565
566 let minimum = self.minimum();
569 let maximum = self.maximum();
570 if let (Some(min), Some(max)) = (minimum, maximum) {
571 if min > max {
572 return Ok(());
573 }
574
575 if let Some(stepped_minimum) = self.stepped_minimum() &&
579 stepped_minimum > max
580 {
581 return Ok(());
582 }
583 }
584
585 let mut value: f64 = self
589 .convert_string_to_number(&self.Value().str())
590 .unwrap_or(0.0);
591
592 let valueBeforeStepping = value;
594
595 if (value - step_base) % allowed_value_step != 0.0 {
600 value = match dir {
601 StepDirection::Down =>
602 {
604 let intervals_from_base = ((value - step_base) / allowed_value_step).floor();
605 intervals_from_base * allowed_value_step + step_base
606 },
607 StepDirection::Up =>
608 {
610 let intervals_from_base = ((value - step_base) / allowed_value_step).ceil();
611 intervals_from_base * allowed_value_step + step_base
612 },
613 };
614 }
615 else {
617 value += match dir {
622 StepDirection::Down => -f64::from(n) * allowed_value_step,
623 StepDirection::Up => f64::from(n) * allowed_value_step,
624 };
625 }
626
627 if let Some(min) = minimum &&
631 value < min
632 {
633 value = self.stepped_minimum().unwrap_or(value);
634 }
635
636 if let Some(max) = maximum &&
640 value > max
641 {
642 value = self.stepped_maximum().unwrap_or(value);
643 }
644
645 match dir {
649 StepDirection::Down => {
650 if value > valueBeforeStepping {
651 return Ok(());
652 }
653 },
654 StepDirection::Up => {
655 if value < valueBeforeStepping {
656 return Ok(());
657 }
658 },
659 }
660
661 self.SetValueAsNumber(cx, value)
665 }
666
667 fn suggestions_source_element(&self) -> Option<DomRoot<HTMLDataListElement>> {
669 let list_string = self
670 .upcast::<Element>()
671 .get_string_attribute(&local_name!("list"));
672 if list_string.is_empty() {
673 return None;
674 }
675 let ancestor = self
676 .upcast::<Node>()
677 .GetRootNode(&GetRootNodeOptions::empty());
678 let first_with_id = &ancestor
679 .traverse_preorder(ShadowIncluding::No)
680 .find(|node| {
681 node.downcast::<Element>()
682 .is_some_and(|e| e.Id() == list_string)
683 });
684 first_with_id
685 .as_ref()
686 .and_then(|el| el.downcast::<HTMLDataListElement>())
687 .map(DomRoot::from_ref)
688 }
689
690 fn suffers_from_being_missing(&self, value: &DOMString) -> bool {
692 self.input_type()
693 .as_specific()
694 .suffers_from_being_missing(self, value)
695 }
696
697 fn suffers_from_type_mismatch(&self, value: &DOMString) -> bool {
699 if value.is_empty() {
700 return false;
701 }
702
703 self.input_type()
704 .as_specific()
705 .suffers_from_type_mismatch(self, value)
706 }
707
708 fn suffers_from_pattern_mismatch(&self, cx: &mut JSContext, value: &DOMString) -> bool {
710 let pattern_str = self.Pattern();
713 if value.is_empty() || pattern_str.is_empty() || !self.does_pattern_apply() {
714 return false;
715 }
716
717 let mut realm = enter_auto_realm(cx, self);
718 let cx = &mut realm;
719
720 rooted!(&in(cx) let mut pattern = ptr::null_mut::<JSObject>());
722 if compile_pattern(cx, &pattern_str.str(), pattern.handle_mut()) {
723 if self.Multiple() && self.does_multiple_apply() {
724 !split_commas(&value.str())
725 .all(|s| matches_js_regex(cx, pattern.handle(), s).unwrap_or(true))
726 } else {
727 !matches_js_regex(cx, pattern.handle(), &value.str()).unwrap_or(true)
728 }
729 } else {
730 false
732 }
733 }
734
735 fn suffers_from_bad_input(&self, value: &DOMString) -> bool {
737 if value.is_empty() {
738 return false;
739 }
740
741 self.input_type()
742 .as_specific()
743 .suffers_from_bad_input(value)
744 }
745
746 fn suffers_from_length_issues(&self, value: &DOMString) -> ValidationFlags {
749 let value_dirty = self.value_dirty.get();
752 let text_input = self.text_input.borrow();
753 let edit_by_user = !text_input.was_last_change_by_set_content();
754
755 if value.is_empty() || !value_dirty || !edit_by_user || !self.does_minmaxlength_apply() {
756 return ValidationFlags::empty();
757 }
758
759 let mut failed_flags = ValidationFlags::empty();
760 let Utf16CodeUnits(value_len) = text_input.len_utf16();
761 let min_length = self.MinLength();
762 let max_length = self.MaxLength();
763
764 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as u32) {
765 failed_flags.insert(ValidationFlags::TOO_SHORT);
766 }
767
768 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as u32) {
769 failed_flags.insert(ValidationFlags::TOO_LONG);
770 }
771
772 failed_flags
773 }
774
775 fn suffers_from_range_issues(&self, value: &DOMString) -> ValidationFlags {
779 if value.is_empty() || !self.does_value_as_number_apply() {
780 return ValidationFlags::empty();
781 }
782
783 let Some(value_as_number) = self.convert_string_to_number(&value.str()) else {
784 return ValidationFlags::empty();
785 };
786
787 let mut failed_flags = ValidationFlags::empty();
788 let min_value = self.minimum();
789 let max_value = self.maximum();
790
791 let has_reversed_range = match (min_value, max_value) {
793 (Some(min), Some(max)) => self.input_type().has_periodic_domain() && min > max,
794 _ => false,
795 };
796
797 if has_reversed_range {
798 if value_as_number > max_value.unwrap() && value_as_number < min_value.unwrap() {
800 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
801 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
802 }
803 } else {
804 if let Some(min_value) = min_value &&
806 value_as_number < min_value
807 {
808 failed_flags.insert(ValidationFlags::RANGE_UNDERFLOW);
809 }
810 if let Some(max_value) = max_value &&
812 value_as_number > max_value
813 {
814 failed_flags.insert(ValidationFlags::RANGE_OVERFLOW);
815 }
816 }
817
818 if let Some(step) = self.allowed_value_step() {
820 let diff = (self.step_base() - value_as_number) % step / value_as_number;
824 if diff.abs() > 1e-12 {
825 failed_flags.insert(ValidationFlags::STEP_MISMATCH);
826 }
827 }
828
829 failed_flags
830 }
831
832 pub(crate) fn is_textual_or_password(&self) -> bool {
834 self.is_textual_or_password.get()
835 }
836
837 fn may_have_embedder_control(&self) -> bool {
838 let el = self.upcast::<Element>();
839 matches!(*self.input_type(), InputType::Color(_)) && !el.disabled_state()
840 }
841
842 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction, event: &Event) {
843 match action {
844 KeyReaction::TriggerDefaultAction => {
845 self.implicit_submission(cx);
846 event.mark_as_handled();
847 },
848 KeyReaction::DispatchInput(text, is_composing, input_type) => {
849 if event.IsTrusted() {
850 self.queue_input_event(text, is_composing, input_type);
851 }
852 self.value_dirty.set(true);
853 self.update_placeholder_shown_state();
854 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
855 event.mark_as_handled();
856 },
857 KeyReaction::RedrawSelection => {
858 self.maybe_update_shared_selection();
859 event.mark_as_handled();
860 },
861 KeyReaction::Nothing => (),
862 }
863 }
864
865 pub(crate) fn value_for_shadow_dom(&self) -> DOMString {
867 let input_type = &*self.input_type();
868 match input_type {
869 InputType::Checkbox(_) |
870 InputType::Radio(_) |
871 InputType::Image(_) |
872 InputType::Hidden(_) |
873 InputType::Range(_) => input_type.as_specific().value_for_shadow_dom(self),
874 _ => {
875 if let Some(attribute_value) = self
876 .upcast::<Element>()
877 .get_attribute_string_value(&local_name!("value"))
878 {
879 return attribute_value.into();
880 }
881 input_type.as_specific().value_for_shadow_dom(self)
882 },
883 }
884 }
885
886 fn schedule_a_selection_change_event(&self) {
888 if self.has_scheduled_selectionchange_event.get() {
890 return;
891 }
892 self.has_scheduled_selectionchange_event.set(true);
894 let this = Trusted::new(self);
896 self.owner_global()
897 .task_manager()
898 .user_interaction_task_source()
899 .queue(
900 task!(selectionchange_task_steps: move |cx| {
902 let this = this.root();
903 this.has_scheduled_selectionchange_event.set(false);
905 this.upcast::<EventTarget>().fire_event_with_params(
907 cx,
908 atom!("selectionchange"),
909 EventBubbles::Bubbles,
910 EventCancelable::NotCancelable,
911 EventComposed::Composed,
912 );
913 }),
918 );
919 }
920}
921
922impl<'dom> LayoutDom<'dom, HTMLInputElement> {
923 pub(crate) fn size_for_layout(self) -> u32 {
933 self.unsafe_get().size.get()
934 }
935
936 pub(crate) fn selection_for_layout(self) -> Option<RangeAny<Utf32CodeUnits>> {
937 let element = self.unsafe_get();
938 if !element.is_textual_or_password.get() {
939 return None;
940 }
941 #[expect(unsafe_code)]
942 let text_input = unsafe { element.text_input.borrow_for_layout() };
943 text_input.selection_for_layout
944 }
945}
946
947impl TextControlElement for HTMLInputElement {
948 fn as_element(&self) -> &Element {
949 self.upcast()
950 }
951
952 fn text_input(&self) -> Ref<'_, TextInput<EmbedderClipboardProvider>> {
953 self.text_input.borrow()
954 }
955
956 fn text_input_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
957 self.text_input.borrow_mut()
958 }
959
960 fn selection_api_applies(&self) -> bool {
962 matches!(
963 *self.input_type(),
964 InputType::Text(_) |
965 InputType::Search(_) |
966 InputType::Url(_) |
967 InputType::Tel(_) |
968 InputType::Password(_)
969 )
970 }
971
972 fn has_selectable_text(&self) -> bool {
980 self.is_textual_or_password() && !self.text_input.borrow().get_content().is_empty()
981 }
982
983 fn has_uncollapsed_selection(&self) -> bool {
984 self.text_input.borrow().has_uncollapsed_selection()
985 }
986
987 fn set_dirty_value_flag(&self, value: bool) {
988 self.value_dirty.set(value)
989 }
990
991 fn select_all(&self) {
992 self.text_input.borrow_mut().select_all();
993 self.maybe_update_shared_selection();
994 }
995
996 fn maybe_update_shared_selection(&self) {
997 let selection = {
998 let mut text_input = self.text_input.borrow_mut();
999 let selection_range = text_input.selection_start()..text_input.selection_end();
1000 let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
1001
1002 let range_remained_equal = selection_range == text_input.previous_selection_range;
1003 if range_remained_equal && enabled == text_input.selection_for_layout.is_some() {
1004 return;
1005 }
1006
1007 if !range_remained_equal {
1008 self.schedule_a_selection_change_event();
1013 }
1014
1015 let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range());
1016 text_input.previous_selection_range = selection_range;
1017 text_input.selection_for_layout = selection;
1018 selection
1019 };
1020
1021 if let Some(text_input_widget) = self.input_type.borrow().as_specific().text_input_widget()
1022 {
1023 if text_input_widget.borrow().set_text_run_selection(selection) {
1024 self.owner_window().layout().set_needs_new_display_list();
1026 } else {
1027 }
1029 } else {
1030 }
1032 }
1033
1034 fn is_password_field(&self) -> bool {
1035 matches!(*self.input_type(), InputType::Password(_))
1036 }
1037
1038 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
1039 self.placeholder.borrow()
1040 }
1041
1042 fn value_text(&self) -> DOMString {
1043 self.Value()
1044 }
1045
1046 fn read_only_or_disabled(&self) -> bool {
1047 self.ReadOnly() || self.Disabled()
1048 }
1049
1050 fn handle_text_content_changed(&self, cx: &mut JSContext) {
1051 self.update_placeholder_shown_state();
1052 self.upcast::<Node>()
1053 .dirty(cx.no_gc(), NodeDamage::ContentOrHeritage);
1054 }
1055}
1056
1057impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
1058 make_getter!(Accept, "accept");
1060
1061 make_setter!(SetAccept, "accept");
1063
1064 make_bool_getter!(Alpha, "alpha");
1066
1067 make_bool_setter!(SetAlpha, "alpha");
1069
1070 make_getter!(Alt, "alt");
1072
1073 make_setter!(SetAlt, "alt");
1075
1076 make_getter!(DirName, "dirname");
1078
1079 make_setter!(SetDirName, "dirname");
1081
1082 make_bool_getter!(Disabled, "disabled");
1084
1085 make_bool_setter!(SetDisabled, "disabled");
1087
1088 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
1090 self.form_owner()
1091 }
1092
1093 fn GetFiles(&self) -> Option<DomRoot<FileList>> {
1095 self.input_type()
1096 .as_specific()
1097 .get_files()
1098 .as_ref()
1099 .cloned()
1100 }
1101
1102 fn SetFiles(&self, _cx: &mut JSContext, files: Option<&FileList>) {
1104 if let Some(files) = files {
1105 self.input_type().as_specific().set_files(files)
1106 }
1107 }
1108
1109 make_bool_getter!(DefaultChecked, "checked");
1111
1112 make_bool_setter!(SetDefaultChecked, "checked");
1114
1115 fn Checked(&self) -> bool {
1117 self.checkedness.get()
1118 }
1119
1120 fn SetChecked(&self, cx: &mut JSContext, checked: bool) {
1122 self.update_checkedness(cx, checked, true);
1123 self.value_changed(cx);
1124 }
1125
1126 make_enumerated_getter!(
1128 ColorSpace,
1129 "colorspace",
1130 "limited-srgb" | "display-p3",
1131 missing => "limited-srgb",
1132 invalid => "limited-srgb"
1133 );
1134
1135 make_setter!(SetColorSpace, "colorspace");
1137
1138 make_bool_getter!(ReadOnly, "readonly");
1140
1141 make_bool_setter!(SetReadOnly, "readonly");
1143
1144 make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE);
1146
1147 make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
1149
1150 fn Type(&self) -> DOMString {
1152 DOMString::from(self.input_type().as_str())
1153 }
1154
1155 make_atomic_setter!(SetType, "type");
1157
1158 fn Value(&self) -> DOMString {
1160 match self.value_mode() {
1161 ValueMode::Value => self.text_input.borrow().get_content(),
1162 ValueMode::Default => self
1163 .upcast::<Element>()
1164 .get_attribute_string_value(&local_name!("value"))
1165 .map(|value| value.into())
1166 .unwrap_or_default(),
1167 ValueMode::DefaultOn => self
1168 .upcast::<Element>()
1169 .get_attribute_string_value(&local_name!("value"))
1170 .map(|value| value.into())
1171 .unwrap_or(DOMString::from_static("on")),
1172 ValueMode::Filename => {
1173 let mut path = DOMString::new();
1174 match self.input_type().as_specific().get_files() {
1175 Some(ref fl) => match fl.Item(0) {
1176 Some(ref f) => {
1177 path.push_str("C:\\fakepath\\");
1178 path.push_str(&f.name().str());
1179 path
1180 },
1181 None => path,
1182 },
1183 None => path,
1184 }
1185 },
1186 }
1187 }
1188
1189 fn SetValue(&self, cx: &mut JSContext, mut value: DOMString) -> ErrorResult {
1191 match self.value_mode() {
1192 ValueMode::Value => {
1193 {
1194 self.value_dirty.set(true);
1196
1197 self.sanitize_value(&mut value);
1200
1201 let mut text_input = self.text_input.borrow_mut();
1202
1203 if text_input.get_content() != value {
1208 text_input.set_content(value);
1210
1211 text_input.clear_selection_to_end();
1212 }
1213 }
1214
1215 self.update_placeholder_shown_state();
1219 self.maybe_update_shared_selection();
1220 },
1221 ValueMode::Default | ValueMode::DefaultOn => {
1222 self.upcast::<Element>()
1223 .set_string_attribute(cx, &local_name!("value"), value);
1224 },
1225 ValueMode::Filename => {
1226 if value.is_empty() {
1227 let window = self.owner_window();
1228 let fl = FileList::new(cx, &window, vec![]);
1229 self.input_type().as_specific().set_files(&fl)
1230 } else {
1231 return Err(Error::InvalidState(Some(
1232 "Non-empty value provided for filename".into(),
1233 )));
1234 }
1235 },
1236 }
1237
1238 self.value_changed(cx);
1239 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1240 Ok(())
1241 }
1242
1243 make_getter!(DefaultValue, "value");
1245
1246 make_setter!(SetDefaultValue, "value");
1248
1249 make_getter!(Min, "min");
1251
1252 make_setter!(SetMin, "min");
1254
1255 fn GetList(&self) -> Option<DomRoot<HTMLDataListElement>> {
1257 self.suggestions_source_element()
1258 }
1259
1260 #[expect(unsafe_code)]
1262 fn GetValueAsDate(&self, cx: &mut JSContext, mut return_value: MutableHandleObject) {
1263 if let Some(date_time) = self
1264 .input_type()
1265 .as_specific()
1266 .convert_string_to_naive_datetime(self.Value())
1267 {
1268 let time = ClippedTime {
1269 t: (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64,
1270 };
1271 return_value.set(unsafe { NewDateObject(cx, time) });
1272 }
1273 }
1274
1275 #[expect(unsafe_code)]
1277 fn SetValueAsDate(&self, cx: &mut JSContext, value: *mut JSObject) -> ErrorResult {
1278 rooted!(&in(cx) let value = value);
1279 if !self.does_value_as_date_apply() {
1280 return Err(Error::InvalidState(Some(
1281 "Input element cannot be treated as a date".into(),
1282 )));
1283 }
1284 if value.is_null() {
1285 return self.SetValue(cx, DOMString::new());
1286 }
1287 let mut msecs: f64 = 0.0;
1288 unsafe {
1292 let mut is_date = false;
1293 if !ObjectIsDate(cx, value.handle(), &mut is_date) {
1294 return Err(Error::JSFailed);
1295 }
1296 if !is_date {
1297 return Err(Error::Type(c"Value was not a date".to_owned()));
1298 }
1299 if !DateGetMsecSinceEpoch(cx, value.handle(), &mut msecs) {
1300 return Err(Error::JSFailed);
1301 }
1302 if !msecs.is_finite() {
1303 return self.SetValue(cx, DOMString::new());
1304 }
1305 }
1306
1307 let Ok(date_time) = OffsetDateTime::from_unix_timestamp_nanos((msecs * 1e6) as i128) else {
1308 return self.SetValue(cx, DOMString::new());
1309 };
1310 self.SetValue(
1311 cx,
1312 self.input_type()
1313 .as_specific()
1314 .convert_datetime_to_dom_string(date_time),
1315 )
1316 }
1317
1318 fn ValueAsNumber(&self) -> f64 {
1320 self.convert_string_to_number(&self.Value().str())
1321 .unwrap_or(f64::NAN)
1322 }
1323
1324 fn SetValueAsNumber(&self, cx: &mut JSContext, value: f64) -> ErrorResult {
1326 if value.is_infinite() {
1327 Err(Error::Type(c"value is not finite".to_owned()))
1328 } else if !self.does_value_as_number_apply() {
1329 Err(Error::InvalidState(Some(
1330 "Input element value cannot be treated as a number".into(),
1331 )))
1332 } else if value.is_nan() {
1333 self.SetValue(cx, DOMString::new())
1334 } else if let Some(converted) = self.convert_number_to_string(value) {
1335 self.SetValue(cx, converted)
1336 } else {
1337 self.SetValue(cx, DOMString::new())
1342 }
1343 }
1344
1345 make_getter!(Name, "name");
1347
1348 make_atomic_setter!(SetName, "name");
1350
1351 make_getter!(Placeholder, "placeholder");
1353
1354 make_setter!(SetPlaceholder, "placeholder");
1356
1357 make_form_action_getter!(FormAction, "formaction");
1359
1360 make_setter!(SetFormAction, "formaction");
1362
1363 make_enumerated_getter!(
1365 FormEnctype,
1366 "formenctype",
1367 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
1368 invalid => "application/x-www-form-urlencoded"
1369 );
1370
1371 make_setter!(SetFormEnctype, "formenctype");
1373
1374 make_enumerated_getter!(
1376 FormMethod,
1377 "formmethod",
1378 "get" | "post" | "dialog",
1379 invalid => "get"
1380 );
1381
1382 make_setter!(SetFormMethod, "formmethod");
1384
1385 make_getter!(FormTarget, "formtarget");
1387
1388 make_setter!(SetFormTarget, "formtarget");
1390
1391 make_bool_getter!(FormNoValidate, "formnovalidate");
1393
1394 make_bool_setter!(SetFormNoValidate, "formnovalidate");
1396
1397 make_getter!(Max, "max");
1399
1400 make_setter!(SetMax, "max");
1402
1403 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1405
1406 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1408
1409 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
1411
1412 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
1414
1415 make_bool_getter!(Multiple, "multiple");
1417
1418 make_bool_setter!(SetMultiple, "multiple");
1420
1421 make_getter!(Pattern, "pattern");
1423
1424 make_setter!(SetPattern, "pattern");
1426
1427 make_bool_getter!(Required, "required");
1429
1430 make_bool_setter!(SetRequired, "required");
1432
1433 make_url_getter!(Src, "src");
1435
1436 make_url_setter!(SetSrc, "src");
1438
1439 make_getter!(Step, "step");
1441
1442 make_setter!(SetStep, "step");
1444
1445 make_getter!(UseMap, "usemap");
1447
1448 make_setter!(SetUseMap, "usemap");
1450
1451 fn Indeterminate(&self) -> bool {
1453 self.upcast::<Element>()
1454 .state()
1455 .contains(ElementState::INDETERMINATE)
1456 }
1457
1458 fn SetIndeterminate(&self, _cx: &mut JSContext, val: bool) {
1460 self.upcast::<Element>()
1461 .set_state(ElementState::INDETERMINATE, val)
1462 }
1463
1464 fn GetLabels(&self, cx: &mut JSContext) -> Option<DomRoot<NodeList>> {
1468 if matches!(*self.input_type(), InputType::Hidden(_)) {
1469 None
1470 } else {
1471 Some(self.labels_node_list.or_init(|| {
1472 NodeList::new_labels_list(
1473 cx,
1474 self.upcast::<Node>().owner_doc().window(),
1475 self.upcast::<HTMLElement>(),
1476 )
1477 }))
1478 }
1479 }
1480
1481 fn Select(&self) {
1483 self.dom_select();
1484 }
1485
1486 fn GetSelectionStart(&self) -> Option<u32> {
1488 self.dom_start().map(|start| start.0)
1489 }
1490
1491 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
1493 self.set_dom_start(start.map(Utf16CodeUnits::from))
1494 }
1495
1496 fn GetSelectionEnd(&self) -> Option<u32> {
1498 self.dom_end().map(|end| end.0)
1499 }
1500
1501 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
1503 self.set_dom_end(end.map(Utf16CodeUnits::from))
1504 }
1505
1506 fn GetSelectionDirection(&self) -> Option<DOMString> {
1508 self.dom_direction()
1509 }
1510
1511 fn SetSelectionDirection(
1513 &self,
1514 _cx: &mut JSContext,
1515 direction: Option<DOMString>,
1516 ) -> ErrorResult {
1517 self.set_dom_direction(direction)
1518 }
1519
1520 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
1522 self.set_dom_range(
1523 Utf16CodeUnits::from(start),
1524 Utf16CodeUnits::from(end),
1525 direction,
1526 )
1527 }
1528
1529 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
1531 self.set_dom_range_text(replacement, None, None, Default::default())
1532 }
1533
1534 fn SetRangeText_(
1536 &self,
1537 replacement: DOMString,
1538 start: u32,
1539 end: u32,
1540 selection_mode: SelectionMode,
1541 ) -> ErrorResult {
1542 self.set_dom_range_text(
1543 replacement,
1544 Some(Utf16CodeUnits::from(start)),
1545 Some(Utf16CodeUnits::from(end)),
1546 selection_mode,
1547 )
1548 }
1549
1550 fn SelectFiles(&self, paths: Vec<DOMString>) {
1553 self.input_type()
1554 .as_specific()
1555 .select_files(self, Some(paths));
1556 }
1557
1558 fn StepUp(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1560 self.step_up_or_down(cx, n, StepDirection::Up)
1561 }
1562
1563 fn StepDown(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1565 self.step_up_or_down(cx, n, StepDirection::Down)
1566 }
1567
1568 fn WillValidate(&self) -> bool {
1570 self.is_instance_validatable()
1571 }
1572
1573 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
1575 self.validity_state(cx)
1576 }
1577
1578 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
1580 self.check_validity(cx)
1581 }
1582
1583 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
1585 self.report_validity(cx)
1586 }
1587
1588 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
1590 self.validation_message(cx)
1591 }
1592
1593 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
1595 self.validity_state(cx).set_custom_error_message(cx, error);
1596 }
1597}
1598
1599impl HTMLInputElement {
1600 pub(crate) fn form_datums(
1603 &self,
1604 submitter: Option<FormSubmitterElement>,
1605 encoding: Option<&'static Encoding>,
1606 ) -> (Vec<FormDatum>, bool) {
1607 let ty = self.Type();
1608 let name = self.Name();
1609 let is_submitter = match submitter {
1610 Some(FormSubmitterElement::Input(s)) => self == s,
1611 _ => false,
1612 };
1613
1614 match *self.input_type() {
1616 InputType::Submit(_) | InputType::Button(_) | InputType::Reset(_) if !is_submitter => {
1618 return (vec![], true);
1619 },
1620
1621 InputType::Radio(_) | InputType::Checkbox(_) if !self.Checked() => {
1623 return (vec![], true);
1624 },
1625
1626 InputType::Image(_) => return (vec![], true), _ => {
1631 if name.is_empty() {
1632 return (vec![], true);
1633 }
1634 },
1635 }
1636
1637 let datums = match *self.input_type() {
1638 InputType::Checkbox(_) | InputType::Radio(_) => {
1640 let field_value = self.Value();
1642 let value = if field_value.is_empty() {
1643 DOMString::from_static("on")
1644 } else {
1645 field_value
1646 };
1647 vec![FormDatum {
1649 ty,
1650 name,
1651 value: FormDatumValue::String(value),
1652 }]
1653 },
1654
1655 InputType::File(_) => {
1657 let mut datums = vec![];
1658
1659 let name = self.Name();
1661
1662 match self.GetFiles() {
1663 None => {
1665 datums.push(FormDatum {
1666 ty,
1669 name,
1670 value: FormDatumValue::String(DOMString::new()),
1671 })
1672 },
1673 Some(fl) => {
1675 for f in fl.iter_files() {
1676 datums.push(FormDatum {
1677 ty: ty.clone(),
1678 name: name.clone(),
1679 value: FormDatumValue::File(DomRoot::from_ref(f)),
1680 });
1681 }
1682 },
1683 }
1684
1685 datums
1686 },
1687
1688 InputType::Hidden(_) if name.eq_ignore_ascii_case("_charset_") => {
1690 let charset = match encoding {
1692 None => DOMString::from_static("UTF-8"),
1693 Some(enc) => DOMString::from(enc.name()),
1694 };
1695 vec![FormDatum {
1697 ty,
1698 name,
1699 value: FormDatumValue::String(charset),
1700 }]
1701 },
1702
1703 _ => vec![FormDatum {
1705 ty,
1706 name,
1707 value: FormDatumValue::String(self.Value()),
1708 }],
1709 };
1710 (datums, false)
1711 }
1712
1713 pub(crate) fn radio_group_name(&self) -> Option<Atom> {
1715 self.upcast::<Element>()
1716 .get_name()
1717 .filter(|name| !name.is_empty())
1718 }
1719
1720 fn update_checkedness(&self, cx: &mut JSContext, checked: bool, dirty: bool) {
1721 self.checkedness.set(checked);
1722 self.update_checked_state();
1723
1724 if dirty {
1725 self.checked_changed.set(true);
1726 }
1727
1728 if matches!(*self.input_type(), InputType::Radio(_)) && checked {
1729 broadcast_radio_checked(cx, self, self.radio_group_name().as_ref());
1730 }
1731
1732 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1733 }
1734
1735 fn update_checked_state(&self) {
1737 let should_checked_state_apply = matches!(
1740 *self.input_type(),
1741 InputType::Checkbox(_) | InputType::Radio(_)
1742 ) && self.Checked();
1743 self.upcast::<Element>()
1744 .set_state(ElementState::CHECKED, should_checked_state_apply);
1745 }
1746
1747 pub(crate) fn is_mutable(&self) -> bool {
1749 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
1752 }
1753
1754 pub(crate) fn reset(&self, cx: &mut JSContext) {
1764 self.value_dirty.set(false);
1765
1766 let mut value = self.DefaultValue();
1768 self.sanitize_value(&mut value);
1769 self.text_input.borrow_mut().set_content(value);
1770
1771 let input_type = &*self.input_type();
1772 if matches!(input_type, InputType::Radio(_) | InputType::Checkbox(_)) {
1773 self.update_checkedness(cx, self.DefaultChecked(), false);
1774 self.checked_changed.set(false);
1775 }
1776
1777 if matches!(input_type, InputType::File(_)) {
1778 input_type
1779 .as_specific()
1780 .set_files(&FileList::new(cx, &self.owner_window(), vec![]));
1781 }
1782
1783 self.value_changed(cx);
1784 }
1785
1786 pub(crate) fn clear(&self, cx: &mut JSContext) {
1789 self.value_dirty.set(false);
1791 self.checked_changed.set(false);
1792 self.text_input.borrow_mut().set_content(DOMString::new());
1794 self.update_checkedness(cx, self.DefaultChecked(), false);
1796 if self.input_type().as_specific().get_files().is_some() {
1798 let window = self.owner_window();
1799 let filelist = FileList::new(cx, &window, vec![]);
1800 self.input_type().as_specific().set_files(&filelist);
1801 }
1802
1803 {
1806 let mut text_input = self.text_input.borrow_mut();
1807 let mut value = text_input.get_content();
1808 self.sanitize_value(&mut value);
1809 text_input.set_content(value);
1810 }
1811
1812 self.value_changed(cx);
1813 }
1814
1815 fn update_placeholder_shown_state(&self) {
1816 if !self.input_type().is_textual_or_password() {
1817 self.upcast::<Element>().set_placeholder_shown_state(false);
1818 } else {
1819 let has_placeholder = !self.placeholder.borrow().is_empty();
1820 let has_value = !self.text_input.borrow().is_empty();
1821 self.upcast::<Element>()
1822 .set_placeholder_shown_state(has_placeholder && !has_value);
1823 }
1824 }
1825
1826 pub(crate) fn select_files_for_webdriver(
1827 &self,
1828 test_paths: Vec<DOMString>,
1829 response_sender: GenericSender<Result<bool, ErrorStatus>>,
1830 ) {
1831 let mut stored_sender = self.pending_webdriver_response.borrow_mut();
1832 assert!(stored_sender.is_none());
1833
1834 *stored_sender = Some(PendingWebDriverResponse {
1835 response_sender,
1836 expected_file_count: test_paths.len(),
1837 });
1838
1839 self.input_type()
1840 .as_specific()
1841 .select_files(self, Some(test_paths));
1842 }
1843
1844 pub(crate) fn take_pending_webdriver_response(&self) -> Option<PendingWebDriverResponse> {
1845 self.pending_webdriver_response.borrow_mut().take()
1846 }
1847
1848 fn sanitize_value(&self, value: &mut DOMString) {
1850 self.input_type().as_specific().sanitize_value(self, value);
1851 }
1852
1853 fn implicit_submission(&self, cx: &mut JSContext) {
1855 let doc = self.owner_document();
1856 let node = doc.upcast::<Node>();
1857 let owner = self.form_owner();
1858 let form = match owner {
1859 None => return,
1860 Some(ref f) => f,
1861 };
1862
1863 if self.upcast::<Element>().click_in_progress() {
1864 return;
1865 }
1866 let submit_button = node
1867 .traverse_preorder(ShadowIncluding::No)
1868 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1869 .filter(|input| matches!(*input.input_type(), InputType::Submit(_)))
1870 .find(|r| r.form_owner() == owner);
1871 match submit_button {
1872 Some(ref button) => {
1873 if button.is_instance_activatable() {
1874 button
1877 .upcast::<Node>()
1878 .fire_synthetic_pointer_event_not_trusted(cx, atom!("click"));
1879 }
1880 },
1881 None => {
1882 let mut inputs = node
1883 .traverse_preorder(ShadowIncluding::No)
1884 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1885 .filter(|input| {
1886 input.form_owner() == owner &&
1887 matches!(
1888 *input.input_type(),
1889 InputType::Text(_) |
1890 InputType::Search(_) |
1891 InputType::Url(_) |
1892 InputType::Tel(_) |
1893 InputType::Email(_) |
1894 InputType::Password(_) |
1895 InputType::Date(_) |
1896 InputType::Month(_) |
1897 InputType::Week(_) |
1898 InputType::Time(_) |
1899 InputType::DatetimeLocal(_) |
1900 InputType::Number(_)
1901 )
1902 });
1903
1904 if inputs.nth(1).is_some() {
1905 return;
1907 }
1908 form.submit(
1909 cx,
1910 SubmittedFrom::NotFromForm,
1911 FormSubmitterElement::Form(form),
1912 );
1913 },
1914 }
1915 }
1916
1917 pub(crate) fn convert_string_to_number(&self, value: &str) -> Option<f64> {
1919 self.input_type()
1920 .as_specific()
1921 .convert_string_to_number(value)
1922 }
1923
1924 fn convert_number_to_string(&self, value: f64) -> Option<DOMString> {
1926 self.input_type()
1927 .as_specific()
1928 .convert_number_to_string(value)
1929 }
1930
1931 fn update_related_validity_states(&self, cx: &mut JSContext) {
1932 match *self.input_type() {
1933 InputType::Radio(_) => {
1934 perform_radio_group_validation(cx, self, self.radio_group_name().as_ref())
1935 },
1936 _ => {
1937 self.validity_state(cx)
1938 .perform_validation_and_update(cx, ValidationFlags::all());
1939 },
1940 }
1941 }
1942
1943 fn value_changed(&self, cx: &mut JSContext) {
1944 self.maybe_update_shared_selection();
1945 self.update_related_validity_states(cx);
1946 self.input_type().as_specific().update_shadow_tree(cx, self);
1947 }
1948
1949 pub(crate) fn show_the_picker_if_applicable(&self) {
1951 if !self.is_mutable() {
1955 return;
1956 }
1957
1958 self.input_type()
1961 .as_specific()
1962 .show_the_picker_if_applicable(self);
1963 }
1964
1965 pub(crate) fn handle_color_picker_response(
1966 &self,
1967 cx: &mut JSContext,
1968 response: Option<RgbColor>,
1969 ) {
1970 if let InputType::Color(ref color_input_type) = *self.input_type() {
1971 color_input_type.handle_color_picker_response(cx, self, response)
1972 }
1973 }
1974
1975 pub(crate) fn handle_file_picker_response(
1976 &self,
1977 cx: &mut JSContext,
1978 response: Option<Vec<SelectedFile>>,
1979 ) {
1980 if let InputType::File(ref file_input_type) = *self.input_type() {
1981 file_input_type.handle_file_picker_response(cx, self, response)
1982 }
1983 }
1984
1985 fn handle_focus_event(&self, cx: &mut JSContext, event: &FocusEvent) {
1986 let event_type = event.upcast::<Event>().type_();
1987 let document = self.owner_document();
1988 if *event_type == *"blur" {
1989 document
1990 .embedder_controls()
1991 .hide_embedder_control(self.upcast());
1992 } else if *event_type == *"focus" {
1993 let input_type = &*self.input_type();
1994 let Ok(input_method_type) = input_type.try_into() else {
1995 return;
1996 };
1997
1998 if self.is_textual_or_password() &&
2003 let Some(selection) = document.selection()
2004 {
2005 let _ = selection.Collapse(cx, None, 0);
2006 }
2007
2008 document.embedder_controls().show_embedder_control(
2009 ControlElement::Ime(Dom::from_ref(self.upcast())),
2010 EmbedderControlRequest::InputMethod(InputMethodRequest {
2011 input_method_type,
2012 text: String::from(self.Value()),
2013 insertion_point: self.GetSelectionEnd(),
2014 multiline: false,
2015 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
2017 }),
2018 None,
2019 );
2020 }
2021 }
2022}
2023
2024impl VirtualMethods for HTMLInputElement {
2025 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2026 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2027 }
2028
2029 fn attribute_mutated(
2030 &self,
2031 cx: &mut JSContext,
2032 attr: AttrRef<'_>,
2033 mutation: AttributeMutation,
2034 ) {
2035 let could_have_had_embedder_control = self.may_have_embedder_control();
2036
2037 self.super_type()
2038 .unwrap()
2039 .attribute_mutated(cx, attr, mutation);
2040
2041 match *attr.local_name() {
2042 local_name!("disabled") => {
2043 let disabled_state = match mutation {
2044 AttributeMutation::Set(None, _) => true,
2045 AttributeMutation::Set(Some(_), _) => {
2046 return;
2048 },
2049 AttributeMutation::Removed => false,
2050 };
2051 let el = self.upcast::<Element>();
2052 el.set_disabled_state(disabled_state);
2053 el.set_enabled_state(!disabled_state);
2054 el.check_ancestors_disabled_state_for_form_control();
2055
2056 if self.input_type().is_textual() {
2057 let read_write = !(self.ReadOnly() || el.disabled_state());
2058 el.set_read_write_state(read_write);
2059 }
2060 },
2061 local_name!("checked") if !self.checked_changed.get() => {
2062 let checked_state = match mutation {
2063 AttributeMutation::Set(None, _) => true,
2064 AttributeMutation::Set(Some(_), _) => {
2065 return;
2067 },
2068 AttributeMutation::Removed => false,
2069 };
2070 self.update_checkedness(cx, checked_state, false);
2071 },
2072 local_name!("size") => {
2073 let size = mutation.new_value(attr).map(|value| value.as_uint());
2074 self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
2075 },
2076 local_name!("type") => {
2077 match mutation {
2078 AttributeMutation::Set(previous_value, _) => {
2079 if previous_value
2083 .is_some_and(|previous_value| **previous_value == **attr.value())
2084 {
2085 return;
2086 }
2087
2088 let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
2089 let previously_selectable = self.selection_api_applies();
2090
2091 *self.input_type.borrow_mut() =
2092 InputType::new_from_atom(attr.value().as_atom());
2093 self.is_textual_or_password
2094 .set(self.input_type().is_textual_or_password());
2095
2096 let element = self.upcast::<Element>();
2097 if self.input_type().is_textual() {
2098 let read_write = !(self.ReadOnly() || element.disabled_state());
2099 element.set_read_write_state(read_write);
2100 } else {
2101 element.set_read_write_state(false);
2102 }
2103
2104 let new_value_mode = self.value_mode();
2105 match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
2106 (&ValueMode::Value, false, ValueMode::Default) |
2108 (&ValueMode::Value, false, ValueMode::DefaultOn) => {
2109 self.SetValue(cx, old_idl_value)
2110 .expect("Failed to set input value on type change to a default ValueMode.");
2111 },
2112
2113 (_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
2115 self.SetValue(
2116 cx,
2117 self.upcast::<Element>()
2118 .get_attribute_string_value(&local_name!("value"))
2119 .unwrap_or_default()
2120 .into(),
2121 )
2122 .expect(
2123 "Failed to set input value on type change to ValueMode::Value.",
2124 );
2125 self.value_dirty.set(false);
2126 },
2127
2128 (_, _, ValueMode::Filename)
2130 if old_value_mode != ValueMode::Filename =>
2131 {
2132 self.SetValue(cx, DOMString::new())
2133 .expect("Failed to set input value on type change to ValueMode::Filename.");
2134 },
2135 _ => {},
2136 }
2137
2138 self.input_type().as_specific().signal_type_change(cx, self);
2140
2141 let mut text_input = self.text_input.borrow_mut();
2143 let mut value = text_input.get_content();
2144 self.sanitize_value(&mut value);
2145 text_input.set_content(value);
2146 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
2147
2148 if self.does_minmaxlength_apply() {
2150 text_input
2151 .set_min_length(self.MinLength().to_u32().map(Utf16CodeUnits));
2152 text_input
2153 .set_max_length(self.MaxLength().to_u32().map(Utf16CodeUnits));
2154 } else {
2155 text_input.set_min_length(None);
2156 text_input.set_max_length(None);
2157 }
2158
2159 if !previously_selectable && self.selection_api_applies() {
2161 text_input.clear_selection_to_start();
2162 }
2163 },
2164 AttributeMutation::Removed => {
2165 self.input_type().as_specific().signal_type_change(cx, self);
2166 *self.input_type.borrow_mut() = InputType::new_text();
2167 self.is_textual_or_password
2168 .set(self.input_type().is_textual_or_password());
2169
2170 let element = self.upcast::<Element>();
2171 let read_write = !(self.ReadOnly() || element.disabled_state());
2172 element.set_read_write_state(read_write);
2173 },
2174 }
2175
2176 self.update_placeholder_shown_state();
2177 self.input_type()
2178 .as_specific()
2179 .update_placeholder_contents(cx, self);
2180
2181 self.update_checked_state();
2182 },
2183 local_name!("value") if !self.value_dirty.get() => {
2184 let value = mutation.new_value(attr).map(|value| (**value).to_owned());
2188 let mut value = value.map_or(DOMString::new(), DOMString::from);
2189
2190 self.sanitize_value(&mut value);
2191 self.text_input.borrow_mut().set_content(value);
2192 self.update_placeholder_shown_state();
2193 },
2194 local_name!("maxlength") if self.does_minmaxlength_apply() => match *attr.value() {
2195 AttrValue::Int(_, value) => {
2196 let mut text_input = self.text_input.borrow_mut();
2197
2198 if value < 0 {
2199 text_input.set_max_length(None);
2200 } else {
2201 text_input.set_max_length(Some(Utf16CodeUnits(value as u32)))
2202 }
2203 },
2204 _ => panic!("Expected an AttrValue::Int"),
2205 },
2206 local_name!("minlength") if self.does_minmaxlength_apply() => match *attr.value() {
2207 AttrValue::Int(_, value) => {
2208 let mut text_input = self.text_input.borrow_mut();
2209
2210 if value < 0 {
2211 text_input.set_min_length(None);
2212 } else {
2213 text_input.set_min_length(Some(Utf16CodeUnits(value as u32)))
2214 }
2215 },
2216 _ => panic!("Expected an AttrValue::Int"),
2217 },
2218 local_name!("placeholder") => {
2219 {
2220 let mut placeholder = self.placeholder.borrow_mut();
2221 placeholder.clear();
2222 if let AttributeMutation::Set(..) = mutation {
2223 placeholder
2224 .extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
2225 }
2226 }
2227 self.update_placeholder_shown_state();
2228 self.input_type()
2229 .as_specific()
2230 .update_placeholder_contents(cx, self);
2231 },
2232 local_name!("readonly") => {
2233 if self.input_type().is_textual() {
2234 let el = self.upcast::<Element>();
2235 match mutation {
2236 AttributeMutation::Set(..) => {
2237 el.set_read_write_state(false);
2238 },
2239 AttributeMutation::Removed => {
2240 el.set_read_write_state(!el.disabled_state());
2241 },
2242 }
2243 }
2244 },
2245 local_name!("form") => {
2246 self.form_attribute_mutated(cx, mutation);
2247 },
2248 _ => {
2249 self.input_type()
2250 .as_specific()
2251 .attribute_mutated(cx, self, attr, mutation);
2252 },
2253 }
2254
2255 self.value_changed(cx);
2256
2257 if could_have_had_embedder_control && !self.may_have_embedder_control() {
2258 self.owner_document()
2259 .embedder_controls()
2260 .hide_embedder_control(self.upcast());
2261 }
2262 }
2263
2264 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2265 match *name {
2266 local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
2267 local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
2268 local_name!("type") => AttrValue::from_atomic(value.into()),
2269 local_name!("maxlength") => {
2270 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
2271 },
2272 local_name!("minlength") => {
2273 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
2274 },
2275 _ => self
2276 .super_type()
2277 .unwrap()
2278 .parse_plain_attribute(name, value),
2279 }
2280 }
2281
2282 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2283 if let Some(s) = self.super_type() {
2284 s.bind_to_tree(cx, context);
2285 }
2286 self.upcast::<Element>()
2287 .check_ancestors_disabled_state_for_form_control();
2288
2289 self.input_type()
2290 .as_specific()
2291 .bind_to_tree(cx, self, context);
2292
2293 self.value_changed(cx);
2294 }
2295
2296 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
2297 self.owner_document()
2299 .embedder_controls()
2300 .hide_embedder_control(self.upcast());
2301
2302 let form_owner = self.form_owner();
2303 self.super_type().unwrap().unbind_from_tree(cx, context);
2304
2305 let node = self.upcast::<Node>();
2306 let el = self.upcast::<Element>();
2307 if node
2308 .ancestors()
2309 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
2310 {
2311 el.check_ancestors_disabled_state_for_form_control();
2312 } else {
2313 el.check_disabled_attribute();
2314 }
2315
2316 self.input_type()
2317 .as_specific()
2318 .unbind_from_tree(cx, self, form_owner, context);
2319
2320 self.validity_state(cx)
2321 .perform_validation_and_update(cx, ValidationFlags::all());
2322 }
2323
2324 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
2330 if event.type_() == atom!("keydown") &&
2331 !event.DefaultPrevented() &&
2332 self.input_type().is_textual_or_password()
2333 {
2334 if let Some(keyevent) = event.downcast::<KeyboardEvent>() {
2335 let action = self.text_input.borrow_mut().handle_keydown(keyevent);
2338 self.handle_key_reaction(cx, action, event);
2339 }
2340 } else if (event.type_() == atom!("compositionstart") ||
2341 event.type_() == atom!("compositionupdate") ||
2342 event.type_() == atom!("compositionend")) &&
2343 self.input_type().is_textual_or_password()
2344 {
2345 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
2346 if event.type_() == atom!("compositionend") {
2347 let action = self
2348 .text_input
2349 .borrow_mut()
2350 .handle_compositionend(compositionevent);
2351 self.handle_key_reaction(cx, action, event);
2352 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
2353 self.update_placeholder_shown_state();
2354 } else if event.type_() == atom!("compositionupdate") {
2355 let action = self
2356 .text_input
2357 .borrow_mut()
2358 .handle_compositionupdate(compositionevent);
2359 self.handle_key_reaction(cx, action, event);
2360 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
2361 self.update_placeholder_shown_state();
2362 } else if event.type_() == atom!("compositionstart") {
2363 self.update_placeholder_shown_state();
2365 }
2366 event.mark_as_handled();
2367 }
2368 } else if let Some(event) = event.downcast::<FocusEvent>() {
2369 self.handle_focus_event(cx, event)
2370 }
2371
2372 self.value_changed(cx);
2373
2374 if let Some(super_type) = self.super_type() {
2375 super_type.handle_event(cx, event);
2376 }
2377 }
2378
2379 fn handle_mousedown_event(
2380 &self,
2381 cx: &mut JSContext,
2382 mouse_event: &MouseEvent,
2383 hit_test_result: &HitTestResult,
2384 ) {
2385 if !self.input_type().is_textual_or_password() || self.text_input.borrow().is_empty() {
2388 if let Some(super_type) = self.super_type() {
2389 super_type.handle_mousedown_event(cx, mouse_event, hit_test_result);
2390 }
2391 return;
2392 }
2393
2394 if self.text_input.borrow_mut().handle_mousedown_event(
2395 self.upcast(),
2396 mouse_event,
2397 hit_test_result,
2398 ) {
2399 self.maybe_update_shared_selection();
2400 mouse_event.upcast::<Event>().mark_as_handled();
2401 }
2402 }
2403
2404 fn cloning_steps(
2406 &self,
2407 cx: &mut JSContext,
2408 copy: &Node,
2409 maybe_doc: Option<&Document>,
2410 clone_children: CloneChildrenFlag,
2411 ) {
2412 if let Some(s) = self.super_type() {
2413 s.cloning_steps(cx, copy, maybe_doc, clone_children);
2414 }
2415 let elem = copy.downcast::<HTMLInputElement>().unwrap();
2416 elem.value_dirty.set(self.value_dirty.get());
2417 elem.checkedness.set(self.Checked());
2418 elem.checked_changed.set(self.checked_changed.get());
2419 elem.upcast::<Element>()
2422 .set_state(ElementState::INDETERMINATE, self.Indeterminate());
2423 elem.text_input
2424 .borrow_mut()
2425 .set_content(self.text_input.borrow().get_content());
2426 self.value_changed(cx);
2427 }
2428}
2429
2430impl FormControl for HTMLInputElement {
2431 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2432 self.form_owner.get()
2433 }
2434
2435 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2436 self.form_owner.set(form);
2437 }
2438
2439 fn to_html_element(&self) -> &HTMLElement {
2440 self.upcast::<HTMLElement>()
2441 }
2442}
2443
2444impl Validatable for HTMLInputElement {
2445 fn as_element(&self) -> &Element {
2446 self.upcast()
2447 }
2448
2449 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
2450 self.validity_state
2451 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
2452 }
2453
2454 fn is_instance_validatable(&self) -> bool {
2455 match *self.input_type() {
2462 InputType::Hidden(_) | InputType::Button(_) | InputType::Reset(_) => false,
2463 _ => {
2464 !(self.upcast::<Element>().disabled_state() ||
2465 self.ReadOnly() ||
2466 is_barred_by_datalist_ancestor(self.upcast()))
2467 },
2468 }
2469 }
2470
2471 fn perform_validation(
2472 &self,
2473 cx: &mut JSContext,
2474 validate_flags: ValidationFlags,
2475 ) -> ValidationFlags {
2476 let mut failed_flags = ValidationFlags::empty();
2477 let value = self.Value();
2478
2479 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
2480 self.suffers_from_being_missing(&value)
2481 {
2482 failed_flags.insert(ValidationFlags::VALUE_MISSING);
2483 }
2484
2485 if validate_flags.contains(ValidationFlags::TYPE_MISMATCH) &&
2486 self.suffers_from_type_mismatch(&value)
2487 {
2488 failed_flags.insert(ValidationFlags::TYPE_MISMATCH);
2489 }
2490
2491 if validate_flags.contains(ValidationFlags::PATTERN_MISMATCH) &&
2492 self.suffers_from_pattern_mismatch(cx, &value)
2493 {
2494 failed_flags.insert(ValidationFlags::PATTERN_MISMATCH);
2495 }
2496
2497 if validate_flags.contains(ValidationFlags::BAD_INPUT) &&
2498 self.suffers_from_bad_input(&value)
2499 {
2500 failed_flags.insert(ValidationFlags::BAD_INPUT);
2501 }
2502
2503 if validate_flags.intersects(ValidationFlags::TOO_LONG | ValidationFlags::TOO_SHORT) {
2504 failed_flags |= self.suffers_from_length_issues(&value);
2505 }
2506
2507 if validate_flags.intersects(
2508 ValidationFlags::RANGE_UNDERFLOW |
2509 ValidationFlags::RANGE_OVERFLOW |
2510 ValidationFlags::STEP_MISMATCH,
2511 ) {
2512 failed_flags |= self.suffers_from_range_issues(&value);
2513 }
2514
2515 failed_flags & validate_flags
2516 }
2517}
2518
2519impl Activatable for HTMLInputElement {
2520 fn as_element(&self) -> &Element {
2521 self.upcast()
2522 }
2523
2524 fn is_instance_activatable(&self) -> bool {
2525 match *self.input_type() {
2526 InputType::Submit(_) |
2533 InputType::Reset(_) |
2534 InputType::File(_) |
2535 InputType::Image(_) |
2536 InputType::Button(_) => self.is_mutable(),
2537 InputType::Checkbox(_) | InputType::Radio(_) | InputType::Color(_) => true,
2541 _ => false,
2542 }
2543 }
2544
2545 fn legacy_pre_activation_behavior(&self, cx: &mut JSContext) -> Option<InputActivationState> {
2547 let activation_state = self
2548 .input_type()
2549 .as_specific()
2550 .legacy_pre_activation_behavior(cx, self);
2551
2552 if activation_state.is_some() {
2553 self.value_changed(cx);
2554 }
2555
2556 activation_state
2557 }
2558
2559 fn legacy_canceled_activation_behavior(
2561 &self,
2562 cx: &mut JSContext,
2563 cache: Option<InputActivationState>,
2564 ) {
2565 let ty = self.input_type();
2567 let cache = match cache {
2568 Some(cache) => {
2569 if (cache.was_radio && !matches!(*ty, InputType::Radio(_))) ||
2570 (cache.was_checkbox && !matches!(*ty, InputType::Checkbox(_)))
2571 {
2572 return;
2575 }
2576 cache
2577 },
2578 None => {
2579 return;
2580 },
2581 };
2582
2583 ty.as_specific()
2585 .legacy_canceled_activation_behavior(cx, self, cache);
2586
2587 self.value_changed(cx);
2588 }
2589
2590 fn activation_behavior(&self, cx: &mut JSContext, event: &Event, target: &EventTarget) {
2592 let input_activation_type = {
2593 let input_type = self.input_type();
2594 InputActivationType::new_from_input_type(&input_type)
2595 };
2596
2597 if let Some(input_activation_type) = input_activation_type {
2598 input_activation_type
2599 .as_specific()
2600 .activation_behavior(cx, self, event, target);
2601 }
2602 }
2603}
2604
2605fn compile_pattern(cx: &mut JSContext, pattern_str: &str, out_regex: MutableHandleObject) -> bool {
2609 if check_js_regex_syntax(cx, pattern_str) {
2611 let pattern_str = format!("^(?:{})$", pattern_str);
2613 let flags = RegExpFlags {
2614 flags_: RegExpFlag_UnicodeSets,
2615 };
2616 new_js_regex(cx, &pattern_str, flags, out_regex)
2617 } else {
2618 false
2619 }
2620}
2621
2622#[expect(unsafe_code)]
2623fn check_js_regex_syntax(cx: &mut JSContext, pattern: &str) -> bool {
2626 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2627 rooted!(&in(cx) let mut exception = UndefinedValue());
2628
2629 let valid = unsafe {
2630 CheckRegExpSyntax(
2631 cx,
2632 pattern.as_ptr(),
2633 pattern.len(),
2634 RegExpFlags {
2635 flags_: RegExpFlag_UnicodeSets,
2636 },
2637 exception.handle_mut(),
2638 )
2639 };
2640
2641 if !valid {
2642 unsafe { JS_ClearPendingException(cx) };
2643 return false;
2644 }
2645
2646 exception.is_undefined()
2649}
2650
2651#[expect(unsafe_code)]
2652fn new_js_regex(
2653 cx: &mut JSContext,
2654 pattern: &str,
2655 flags: RegExpFlags,
2656 mut out_regex: MutableHandleObject,
2657) -> bool {
2658 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2659 out_regex.set(unsafe { NewUCRegExpObject(cx, pattern.as_ptr(), pattern.len(), flags) });
2660
2661 if out_regex.is_null() {
2662 unsafe { JS_ClearPendingException(cx) };
2663 return false;
2664 }
2665 true
2666}
2667
2668#[expect(unsafe_code)]
2669fn matches_js_regex(cx: &mut JSContext, regex_obj: HandleObject, value: &str) -> Result<bool, ()> {
2670 let mut value: Vec<u16> = value.encode_utf16().collect();
2671
2672 let mut is_regex = false;
2673 assert!(unsafe { ObjectIsRegExp(cx, regex_obj, &mut is_regex) });
2674 assert!(is_regex);
2675
2676 rooted!(&in(cx) let mut rval = UndefinedValue());
2677 let mut index = 0;
2678
2679 let ok = unsafe {
2680 ExecuteRegExpNoStatics(
2681 cx,
2682 regex_obj,
2683 value.as_mut_ptr(),
2684 value.len(),
2685 &mut index,
2686 true,
2687 rval.handle_mut(),
2688 )
2689 };
2690
2691 if ok {
2692 Ok(!rval.is_null())
2693 } else {
2694 unsafe { JS_ClearPendingException(cx) };
2695 Err(())
2696 }
2697}
2698
2699#[derive(MallocSizeOf)]
2703pub(crate) struct PendingWebDriverResponse {
2704 response_sender: GenericSender<Result<bool, ErrorStatus>>,
2706 expected_file_count: usize,
2708}
2709
2710impl PendingWebDriverResponse {
2711 pub(crate) fn finish(self, number_files_selected: usize) {
2712 if number_files_selected == self.expected_file_count {
2713 let _ = self.response_sender.send(Ok(false));
2714 } else {
2715 let _ = self.response_sender.send(Err(ErrorStatus::InvalidArgument));
2718 }
2719 }
2720}