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::{ClippedTime, JSObject, RegExpFlag_UnicodeSets, RegExpFlags};
15use js::jsval::UndefinedValue;
16use js::rust::wrappers2::{
17 CheckRegExpSyntax, DateGetMsecSinceEpoch, ExecuteRegExpNoStatics, JS_ClearPendingException,
18 NewDateObject, NewUCRegExpObject, ObjectIsDate, ObjectIsRegExp,
19};
20use js::rust::{HandleObject, MutableHandleObject};
21use layout_api::{ScriptSelection, SharedSelection};
22use num_traits::ToPrimitive;
23use script_bindings::cell::{DomRefCell, Ref};
24use script_bindings::domstring::parse_floating_point_number;
25use servo_base::generic_channel::GenericSender;
26use servo_base::text::Utf16CodeUnitLength;
27use style::attr::AttrValue;
28use style::str::split_commas;
29use stylo_atoms::Atom;
30use stylo_dom::ElementState;
31use time::OffsetDateTime;
32use unicode_bidi::{BidiClass, bidi_class};
33use webdriver::error::ErrorStatus;
34
35use crate::dom::activation::Activatable;
36use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
37use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
38use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
39use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
40use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
41use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
42use crate::dom::bindings::error::{Error, ErrorResult};
43use crate::dom::bindings::inheritance::Castable;
44use crate::dom::bindings::refcounted::Trusted;
45use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
46use crate::dom::bindings::str::{DOMString, USVString};
47use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
48use crate::dom::compositionevent::CompositionEvent;
49use crate::dom::document::Document;
50use crate::dom::document_embedder_controls::ControlElement;
51use crate::dom::element::attributes::storage::AttrRef;
52use crate::dom::element::{AttributeMutation, Element};
53use crate::dom::event::Event;
54use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
55use crate::dom::eventtarget::EventTarget;
56use crate::dom::filelist::FileList;
57use crate::dom::html::form_controls::input_type::radio_input_type::{
58 broadcast_radio_checked, perform_radio_group_validation,
59};
60use crate::dom::html::form_controls::input_type::{InputActivationType, InputType};
61use crate::dom::html::form_controls::text_control::{TextControlElement, TextControlSelection};
62use crate::dom::html::form_controls::text_input::{
63 ClipboardEventFlags, EmbedderClipboardProvider, IsComposing, KeyReaction, Lines, TextInput,
64};
65use crate::dom::html::htmldatalistelement::HTMLDataListElement;
66use crate::dom::html::htmlelement::HTMLElement;
67use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
68use crate::dom::html::htmlformelement::{
69 FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, SubmittedFrom,
70};
71use crate::dom::iterators::ShadowIncluding;
72use crate::dom::keyboardevent::KeyboardEvent;
73use crate::dom::node::virtualmethods::VirtualMethods;
74use crate::dom::node::{
75 BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
76};
77use crate::dom::nodelist::NodeList;
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_auto_realm;
82
83#[derive(Debug, PartialEq)]
84pub(crate) enum ValueMode {
85 Value,
87
88 Default,
90
91 DefaultOn,
93
94 Filename,
96}
97
98#[derive(Debug, PartialEq)]
99enum StepDirection {
100 Up,
101 Down,
102}
103
104#[dom_struct]
105pub(crate) struct HTMLInputElement {
106 htmlelement: HTMLElement,
107 input_type: DomRefCell<InputType>,
108
109 is_textual_or_password: Cell<bool>,
112
113 checked_changed: Cell<bool>,
115 placeholder: DomRefCell<DOMString>,
116 size: Cell<u32>,
117 maxlength: Cell<i32>,
118 minlength: Cell<i32>,
119 #[no_trace]
120 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
121 value_dirty: Cell<bool>,
123 #[no_trace]
126 #[conditional_malloc_size_of]
127 shared_selection: SharedSelection,
128
129 form_owner: MutNullableDom<HTMLFormElement>,
130 labels_node_list: MutNullableDom<NodeList>,
131 validity_state: MutNullableDom<ValidityState>,
132 #[no_trace]
133 pending_webdriver_response: RefCell<Option<PendingWebDriverResponse>>,
134
135 has_scheduled_selectionchange_event: Cell<bool>,
137}
138
139#[derive(JSTraceable)]
140pub(crate) struct InputActivationState {
141 pub(crate) indeterminate: bool,
142 pub(crate) checked: bool,
143 pub(crate) checked_radio: Option<DomRoot<HTMLInputElement>>,
144 pub(crate) was_radio: bool,
145 pub(crate) was_checkbox: bool,
146 }
148
149static DEFAULT_INPUT_SIZE: u32 = 20;
150static DEFAULT_MAX_LENGTH: i32 = -1;
151static DEFAULT_MIN_LENGTH: i32 = -1;
152
153#[expect(non_snake_case)]
154impl HTMLInputElement {
155 fn new_inherited(
156 local_name: LocalName,
157 prefix: Option<Prefix>,
158 document: &Document,
159 ) -> HTMLInputElement {
160 let embedder_sender = document
161 .window()
162 .as_global_scope()
163 .script_to_embedder_chan()
164 .clone();
165 HTMLInputElement {
166 htmlelement: HTMLElement::new_inherited_with_state(
167 ElementState::ENABLED | ElementState::READWRITE,
168 local_name,
169 prefix,
170 document,
171 ),
172 input_type: DomRefCell::new(InputType::new_text()),
173 is_textual_or_password: Cell::new(true),
174 placeholder: DomRefCell::new(DOMString::new()),
175 checked_changed: Cell::new(false),
176 maxlength: Cell::new(DEFAULT_MAX_LENGTH),
177 minlength: Cell::new(DEFAULT_MIN_LENGTH),
178 size: Cell::new(DEFAULT_INPUT_SIZE),
179 textinput: DomRefCell::new(TextInput::new(
180 Lines::Single,
181 DOMString::new(),
182 EmbedderClipboardProvider {
183 embedder_sender,
184 webview_id: document.webview_id(),
185 },
186 )),
187 value_dirty: Cell::new(false),
188 shared_selection: Default::default(),
189 form_owner: Default::default(),
190 labels_node_list: MutNullableDom::new(None),
191 validity_state: Default::default(),
192 pending_webdriver_response: Default::default(),
193 has_scheduled_selectionchange_event: Default::default(),
194 }
195 }
196
197 pub(crate) fn new(
198 cx: &mut JSContext,
199 local_name: LocalName,
200 prefix: Option<Prefix>,
201 document: &Document,
202 proto: Option<HandleObject>,
203 ) -> DomRoot<HTMLInputElement> {
204 Node::reflect_node_with_proto(
205 cx,
206 Box::new(HTMLInputElement::new_inherited(
207 local_name, prefix, document,
208 )),
209 document,
210 proto,
211 )
212 }
213
214 pub(crate) fn auto_directionality(&self) -> Option<String> {
215 match *self.input_type() {
216 InputType::Text(_) | InputType::Search(_) | InputType::Url(_) | InputType::Email(_) => {
217 let value: String = String::from(self.Value());
218 Some(HTMLInputElement::directionality_from_value(&value))
219 },
220 _ => None,
221 }
222 }
223
224 pub(crate) fn directionality_from_value(value: &str) -> String {
225 if HTMLInputElement::is_first_strong_character_rtl(value) {
226 "rtl".to_owned()
227 } else {
228 "ltr".to_owned()
229 }
230 }
231
232 fn is_first_strong_character_rtl(value: &str) -> bool {
233 for ch in value.chars() {
234 return match bidi_class(ch) {
235 BidiClass::L => false,
236 BidiClass::AL => true,
237 BidiClass::R => true,
238 _ => continue,
239 };
240 }
241 false
242 }
243
244 pub(crate) fn value_mode(&self) -> ValueMode {
247 match *self.input_type() {
248 InputType::Submit(_) |
249 InputType::Reset(_) |
250 InputType::Button(_) |
251 InputType::Image(_) |
252 InputType::Hidden(_) => ValueMode::Default,
253
254 InputType::Checkbox(_) | InputType::Radio(_) => ValueMode::DefaultOn,
255
256 InputType::Color(_) |
257 InputType::Date(_) |
258 InputType::DatetimeLocal(_) |
259 InputType::Email(_) |
260 InputType::Month(_) |
261 InputType::Number(_) |
262 InputType::Password(_) |
263 InputType::Range(_) |
264 InputType::Search(_) |
265 InputType::Tel(_) |
266 InputType::Text(_) |
267 InputType::Time(_) |
268 InputType::Url(_) |
269 InputType::Week(_) => ValueMode::Value,
270
271 InputType::File(_) => ValueMode::Filename,
272 }
273 }
274
275 #[inline]
276 pub(crate) fn input_type(&self) -> Ref<'_, InputType> {
277 self.input_type.borrow()
278 }
279
280 pub(crate) fn is_nontypeable(&self) -> bool {
282 matches!(
283 *self.input_type(),
284 InputType::Button(_) |
285 InputType::Checkbox(_) |
286 InputType::Color(_) |
287 InputType::File(_) |
288 InputType::Hidden(_) |
289 InputType::Image(_) |
290 InputType::Radio(_) |
291 InputType::Range(_) |
292 InputType::Reset(_) |
293 InputType::Submit(_)
294 )
295 }
296
297 #[inline]
298 pub(crate) fn is_submit_button(&self) -> bool {
299 matches!(
300 *self.input_type(),
301 InputType::Submit(_) | InputType::Image(_)
302 )
303 }
304
305 pub(crate) fn is_auto_directionality_form_associated_element(&self) -> bool {
307 matches!(
308 *self.input_type(),
309 InputType::Hidden(_) |
310 InputType::Text(_) |
311 InputType::Search(_) |
312 InputType::Tel(_) |
313 InputType::Url(_) |
314 InputType::Email(_) |
315 InputType::Password(_) |
316 InputType::Submit(_) |
317 InputType::Reset(_) |
318 InputType::Button(_)
319 )
320 }
321
322 fn does_minmaxlength_apply(&self) -> bool {
323 matches!(
324 *self.input_type(),
325 InputType::Text(_) |
326 InputType::Search(_) |
327 InputType::Url(_) |
328 InputType::Tel(_) |
329 InputType::Email(_) |
330 InputType::Password(_)
331 )
332 }
333
334 fn does_pattern_apply(&self) -> bool {
335 matches!(
336 *self.input_type(),
337 InputType::Text(_) |
338 InputType::Search(_) |
339 InputType::Url(_) |
340 InputType::Tel(_) |
341 InputType::Email(_) |
342 InputType::Password(_)
343 )
344 }
345
346 fn does_multiple_apply(&self) -> bool {
347 matches!(*self.input_type(), InputType::Email(_))
348 }
349
350 fn does_value_as_number_apply(&self) -> bool {
353 matches!(
354 *self.input_type(),
355 InputType::Date(_) |
356 InputType::Month(_) |
357 InputType::Week(_) |
358 InputType::Time(_) |
359 InputType::DatetimeLocal(_) |
360 InputType::Number(_) |
361 InputType::Range(_)
362 )
363 }
364
365 fn does_value_as_date_apply(&self) -> bool {
366 matches!(
367 *self.input_type(),
368 InputType::Date(_) | InputType::Month(_) | InputType::Week(_) | InputType::Time(_)
369 )
370 }
371
372 pub(crate) fn allowed_value_step(&self) -> Option<f64> {
374 let default_step = self.default_step()?;
377
378 let Some(step_value) = self
381 .upcast::<Element>()
382 .get_attribute_string_value(&local_name!("step"))
383 else {
384 return Some(default_step * self.step_scale_factor());
385 };
386
387 if step_value.eq_ignore_ascii_case("any") {
390 return None;
391 }
392
393 let Some(parsed_value) =
397 parse_floating_point_number(&step_value).filter(|value| *value > 0.0)
398 else {
399 return Some(default_step * self.step_scale_factor());
400 };
401
402 Some(parsed_value * self.step_scale_factor())
406 }
407
408 pub(crate) fn minimum(&self) -> Option<f64> {
410 self.upcast::<Element>()
411 .get_attribute_string_value(&local_name!("min"))
412 .and_then(|value| self.convert_string_to_number(&value))
413 .or_else(|| self.default_minimum())
414 }
415
416 pub(crate) fn maximum(&self) -> Option<f64> {
418 self.upcast::<Element>()
419 .get_attribute_string_value(&local_name!("max"))
420 .and_then(|value| self.convert_string_to_number(&value))
421 .or_else(|| self.default_maximum())
422 }
423
424 pub(crate) fn stepped_minimum(&self) -> Option<f64> {
427 match (self.minimum(), self.allowed_value_step()) {
428 (Some(min), Some(allowed_step)) => {
429 let step_base = self.step_base();
430 let nsteps = (min - step_base) / allowed_step;
432 Some(step_base + (allowed_step * nsteps.ceil()))
434 },
435 (_, _) => None,
436 }
437 }
438
439 pub(crate) fn stepped_maximum(&self) -> Option<f64> {
442 match (self.maximum(), self.allowed_value_step()) {
443 (Some(max), Some(allowed_step)) => {
444 let step_base = self.step_base();
445 let nsteps = (max - step_base) / allowed_step;
447 Some(step_base + (allowed_step * nsteps.floor()))
449 },
450 (_, _) => None,
451 }
452 }
453
454 fn default_minimum(&self) -> Option<f64> {
456 match *self.input_type() {
457 InputType::Range(_) => Some(0.0),
458 _ => None,
459 }
460 }
461
462 fn default_maximum(&self) -> Option<f64> {
464 match *self.input_type() {
465 InputType::Range(_) => Some(100.0),
466 _ => None,
467 }
468 }
469
470 pub(crate) fn default_range_value(&self) -> f64 {
472 let min = self.minimum().unwrap_or(0.0);
473 let max = self.maximum().unwrap_or(100.0);
474 if max < min {
475 min
476 } else {
477 min + (max - min) * 0.5
478 }
479 }
480
481 fn default_step(&self) -> Option<f64> {
483 match *self.input_type() {
484 InputType::Date(_) => Some(1.0),
485 InputType::Month(_) => Some(1.0),
486 InputType::Week(_) => Some(1.0),
487 InputType::Time(_) => Some(60.0),
488 InputType::DatetimeLocal(_) => Some(60.0),
489 InputType::Number(_) => Some(1.0),
490 InputType::Range(_) => Some(1.0),
491 _ => None,
492 }
493 }
494
495 fn step_scale_factor(&self) -> f64 {
497 match *self.input_type() {
498 InputType::Date(_) => 86400000.0,
499 InputType::Month(_) => 1.0,
500 InputType::Week(_) => 604800000.0,
501 InputType::Time(_) => 1000.0,
502 InputType::DatetimeLocal(_) => 1000.0,
503 InputType::Number(_) => 1.0,
504 InputType::Range(_) => 1.0,
505 _ => unreachable!(),
506 }
507 }
508
509 pub(crate) fn step_base(&self) -> f64 {
511 if let Some(minimum) = self
515 .upcast::<Element>()
516 .get_attribute_string_value(&local_name!("min"))
517 .and_then(|value| self.convert_string_to_number(&value))
518 {
519 return minimum;
520 }
521
522 if let Some(value) = self
526 .upcast::<Element>()
527 .get_attribute_string_value(&local_name!("value"))
528 .and_then(|value| self.convert_string_to_number(&value))
529 {
530 return value;
531 }
532
533 if let Some(default_step_base) = self.default_step_base() {
535 return default_step_base;
536 }
537
538 0.0
540 }
541
542 fn default_step_base(&self) -> Option<f64> {
544 match *self.input_type() {
545 InputType::Week(_) => Some(-259200000.0),
546 _ => None,
547 }
548 }
549
550 fn step_up_or_down(&self, cx: &mut JSContext, n: i32, dir: StepDirection) -> ErrorResult {
554 if !self.does_value_as_number_apply() {
557 return Err(Error::InvalidState(None));
558 }
559 let step_base = self.step_base();
560
561 let Some(allowed_value_step) = self.allowed_value_step() else {
563 return Err(Error::InvalidState(None));
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 textinput = self.textinput.borrow();
753 let edit_by_user = !textinput.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 Utf16CodeUnitLength(value_len) = textinput.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 usize) {
765 failed_flags.insert(ValidationFlags::TOO_SHORT);
766 }
767
768 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
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.textinput.borrow().queue_input_event(
851 self.upcast(),
852 text,
853 is_composing,
854 input_type,
855 );
856 }
857 self.value_dirty.set(true);
858 self.update_placeholder_shown_state();
859 self.upcast::<Node>().dirty(NodeDamage::Other);
860 event.mark_as_handled();
861 },
862 KeyReaction::RedrawSelection => {
863 self.maybe_update_shared_selection();
864 event.mark_as_handled();
865 },
866 KeyReaction::Nothing => (),
867 }
868 }
869
870 pub(crate) fn value_for_shadow_dom(&self) -> DOMString {
872 let input_type = &*self.input_type();
873 match input_type {
874 InputType::Checkbox(_) |
875 InputType::Radio(_) |
876 InputType::Image(_) |
877 InputType::Hidden(_) |
878 InputType::Range(_) => input_type.as_specific().value_for_shadow_dom(self),
879 _ => {
880 if let Some(attribute_value) = self
881 .upcast::<Element>()
882 .get_attribute_string_value(&local_name!("value"))
883 {
884 return attribute_value.into();
885 }
886 input_type.as_specific().value_for_shadow_dom(self)
887 },
888 }
889 }
890
891 pub(crate) fn textinput_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
892 self.textinput.borrow_mut()
893 }
894
895 fn schedule_a_selection_change_event(&self) {
897 if self.has_scheduled_selectionchange_event.get() {
899 return;
900 }
901 self.has_scheduled_selectionchange_event.set(true);
903 let this = Trusted::new(self);
905 self.owner_global()
906 .task_manager()
907 .user_interaction_task_source()
908 .queue(
909 task!(selectionchange_task_steps: move |cx| {
911 let this = this.root();
912 this.has_scheduled_selectionchange_event.set(false);
914 this.upcast::<EventTarget>().fire_event_with_params(
916 cx,
917 atom!("selectionchange"),
918 EventBubbles::Bubbles,
919 EventCancelable::NotCancelable,
920 EventComposed::Composed,
921 );
922 }),
927 );
928 }
929}
930
931impl<'dom> LayoutDom<'dom, HTMLInputElement> {
932 pub(crate) fn size_for_layout(self) -> u32 {
942 self.unsafe_get().size.get()
943 }
944
945 pub(crate) fn selection_for_layout(self) -> Option<SharedSelection> {
946 if !self.unsafe_get().is_textual_or_password.get() {
947 return None;
948 }
949 Some(self.unsafe_get().shared_selection.clone())
950 }
951}
952
953impl TextControlElement for HTMLInputElement {
954 fn selection_api_applies(&self) -> bool {
956 matches!(
957 *self.input_type(),
958 InputType::Text(_) |
959 InputType::Search(_) |
960 InputType::Url(_) |
961 InputType::Tel(_) |
962 InputType::Password(_)
963 )
964 }
965
966 fn has_selectable_text(&self) -> bool {
974 self.is_textual_or_password() && !self.textinput.borrow().get_content().is_empty()
975 }
976
977 fn has_uncollapsed_selection(&self) -> bool {
978 self.textinput.borrow().has_uncollapsed_selection()
979 }
980
981 fn set_dirty_value_flag(&self, value: bool) {
982 self.value_dirty.set(value)
983 }
984
985 fn select_all(&self) {
986 self.textinput.borrow_mut().select_all();
987 self.maybe_update_shared_selection();
988 }
989
990 fn maybe_update_shared_selection(&self) {
991 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
992 let (start, end) = (offsets.start.0, offsets.end.0);
993 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
994 let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
995
996 let mut shared_selection = self.shared_selection.borrow_mut();
997 let range_remained_equal = range == shared_selection.range;
998 if range_remained_equal && enabled == shared_selection.enabled {
999 return;
1000 }
1001
1002 if !range_remained_equal {
1003 self.schedule_a_selection_change_event();
1008 }
1009
1010 *shared_selection = ScriptSelection {
1011 range,
1012 character_range: self
1013 .textinput
1014 .borrow()
1015 .sorted_selection_character_offsets_range(),
1016 enabled,
1017 };
1018 self.owner_window().layout().set_needs_new_display_list();
1019 }
1020
1021 fn is_password_field(&self) -> bool {
1022 matches!(*self.input_type(), InputType::Password(_))
1023 }
1024
1025 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
1026 self.placeholder.borrow()
1027 }
1028
1029 fn value_text(&self) -> DOMString {
1030 self.Value()
1031 }
1032}
1033
1034impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
1035 make_getter!(Accept, "accept");
1037
1038 make_setter!(SetAccept, "accept");
1040
1041 make_bool_getter!(Alpha, "alpha");
1043
1044 make_bool_setter!(SetAlpha, "alpha");
1046
1047 make_getter!(Alt, "alt");
1049
1050 make_setter!(SetAlt, "alt");
1052
1053 make_getter!(DirName, "dirname");
1055
1056 make_setter!(SetDirName, "dirname");
1058
1059 make_bool_getter!(Disabled, "disabled");
1061
1062 make_bool_setter!(SetDisabled, "disabled");
1064
1065 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
1067 self.form_owner()
1068 }
1069
1070 fn GetFiles(&self) -> Option<DomRoot<FileList>> {
1072 self.input_type()
1073 .as_specific()
1074 .get_files()
1075 .as_ref()
1076 .cloned()
1077 }
1078
1079 fn SetFiles(&self, _cx: &mut JSContext, files: Option<&FileList>) {
1081 if let Some(files) = files {
1082 self.input_type().as_specific().set_files(files)
1083 }
1084 }
1085
1086 make_bool_getter!(DefaultChecked, "checked");
1088
1089 make_bool_setter!(SetDefaultChecked, "checked");
1091
1092 fn Checked(&self) -> bool {
1094 self.upcast::<Element>()
1095 .state()
1096 .contains(ElementState::CHECKED)
1097 }
1098
1099 fn SetChecked(&self, cx: &mut JSContext, checked: bool) {
1101 self.update_checked_state(cx, checked, true);
1102 self.value_changed(cx);
1103 }
1104
1105 make_enumerated_getter!(
1107 ColorSpace,
1108 "colorspace",
1109 "limited-srgb" | "display-p3",
1110 missing => "limited-srgb",
1111 invalid => "limited-srgb"
1112 );
1113
1114 make_setter!(SetColorSpace, "colorspace");
1116
1117 make_bool_getter!(ReadOnly, "readonly");
1119
1120 make_bool_setter!(SetReadOnly, "readonly");
1122
1123 make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE);
1125
1126 make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
1128
1129 fn Type(&self) -> DOMString {
1131 DOMString::from(self.input_type().as_str())
1132 }
1133
1134 make_atomic_setter!(SetType, "type");
1136
1137 fn Value(&self) -> DOMString {
1139 match self.value_mode() {
1140 ValueMode::Value => self.textinput.borrow().get_content(),
1141 ValueMode::Default => self
1142 .upcast::<Element>()
1143 .get_attribute_string_value(&local_name!("value"))
1144 .map(|value| value.into())
1145 .unwrap_or_default(),
1146 ValueMode::DefaultOn => self
1147 .upcast::<Element>()
1148 .get_attribute_string_value(&local_name!("value"))
1149 .map(|value| value.into())
1150 .unwrap_or(DOMString::from("on")),
1151 ValueMode::Filename => {
1152 let mut path = DOMString::from("");
1153 match self.input_type().as_specific().get_files() {
1154 Some(ref fl) => match fl.Item(0) {
1155 Some(ref f) => {
1156 path.push_str("C:\\fakepath\\");
1157 path.push_str(&f.name().str());
1158 path
1159 },
1160 None => path,
1161 },
1162 None => path,
1163 }
1164 },
1165 }
1166 }
1167
1168 fn SetValue(&self, cx: &mut JSContext, mut value: DOMString) -> ErrorResult {
1170 match self.value_mode() {
1171 ValueMode::Value => {
1172 {
1173 self.value_dirty.set(true);
1175
1176 self.sanitize_value(&mut value);
1179
1180 let mut textinput = self.textinput.borrow_mut();
1181
1182 if textinput.get_content() != value {
1187 textinput.set_content(value);
1189
1190 textinput.clear_selection_to_end();
1191 }
1192 }
1193
1194 self.update_placeholder_shown_state();
1198 self.maybe_update_shared_selection();
1199 },
1200 ValueMode::Default | ValueMode::DefaultOn => {
1201 self.upcast::<Element>()
1202 .set_string_attribute(cx, &local_name!("value"), value);
1203 },
1204 ValueMode::Filename => {
1205 if value.is_empty() {
1206 let window = self.owner_window();
1207 let fl = FileList::new(cx, &window, vec![]);
1208 self.input_type().as_specific().set_files(&fl)
1209 } else {
1210 return Err(Error::InvalidState(None));
1211 }
1212 },
1213 }
1214
1215 self.value_changed(cx);
1216 self.upcast::<Node>().dirty(NodeDamage::Other);
1217 Ok(())
1218 }
1219
1220 make_getter!(DefaultValue, "value");
1222
1223 make_setter!(SetDefaultValue, "value");
1225
1226 make_getter!(Min, "min");
1228
1229 make_setter!(SetMin, "min");
1231
1232 fn GetList(&self) -> Option<DomRoot<HTMLDataListElement>> {
1234 self.suggestions_source_element()
1235 }
1236
1237 #[expect(unsafe_code)]
1239 fn GetValueAsDate(&self, cx: &mut JSContext, mut return_value: MutableHandleObject) {
1240 if let Some(date_time) = self
1241 .input_type()
1242 .as_specific()
1243 .convert_string_to_naive_datetime(self.Value())
1244 {
1245 let time = ClippedTime {
1246 t: (date_time - OffsetDateTime::UNIX_EPOCH).whole_milliseconds() as f64,
1247 };
1248 return_value.set(unsafe { NewDateObject(cx, time) });
1249 }
1250 }
1251
1252 #[expect(unsafe_code)]
1254 fn SetValueAsDate(&self, cx: &mut JSContext, value: *mut JSObject) -> ErrorResult {
1255 rooted!(&in(cx) let value = value);
1256 if !self.does_value_as_date_apply() {
1257 return Err(Error::InvalidState(None));
1258 }
1259 if value.is_null() {
1260 return self.SetValue(cx, DOMString::from(""));
1261 }
1262 let mut msecs: f64 = 0.0;
1263 unsafe {
1267 let mut is_date = false;
1268 if !ObjectIsDate(cx, value.handle(), &mut is_date) {
1269 return Err(Error::JSFailed);
1270 }
1271 if !is_date {
1272 return Err(Error::Type(c"Value was not a date".to_owned()));
1273 }
1274 if !DateGetMsecSinceEpoch(cx, value.handle(), &mut msecs) {
1275 return Err(Error::JSFailed);
1276 }
1277 if !msecs.is_finite() {
1278 return self.SetValue(cx, DOMString::from(""));
1279 }
1280 }
1281
1282 let Ok(date_time) = OffsetDateTime::from_unix_timestamp_nanos((msecs * 1e6) as i128) else {
1283 return self.SetValue(cx, DOMString::from(""));
1284 };
1285 self.SetValue(
1286 cx,
1287 self.input_type()
1288 .as_specific()
1289 .convert_datetime_to_dom_string(date_time),
1290 )
1291 }
1292
1293 fn ValueAsNumber(&self) -> f64 {
1295 self.convert_string_to_number(&self.Value().str())
1296 .unwrap_or(f64::NAN)
1297 }
1298
1299 fn SetValueAsNumber(&self, cx: &mut JSContext, value: f64) -> ErrorResult {
1301 if value.is_infinite() {
1302 Err(Error::Type(c"value is not finite".to_owned()))
1303 } else if !self.does_value_as_number_apply() {
1304 Err(Error::InvalidState(None))
1305 } else if value.is_nan() {
1306 self.SetValue(cx, DOMString::from(""))
1307 } else if let Some(converted) = self.convert_number_to_string(value) {
1308 self.SetValue(cx, converted)
1309 } else {
1310 self.SetValue(cx, DOMString::from(""))
1315 }
1316 }
1317
1318 make_getter!(Name, "name");
1320
1321 make_atomic_setter!(SetName, "name");
1323
1324 make_getter!(Placeholder, "placeholder");
1326
1327 make_setter!(SetPlaceholder, "placeholder");
1329
1330 make_form_action_getter!(FormAction, "formaction");
1332
1333 make_setter!(SetFormAction, "formaction");
1335
1336 make_enumerated_getter!(
1338 FormEnctype,
1339 "formenctype",
1340 "application/x-www-form-urlencoded" | "text/plain" | "multipart/form-data",
1341 invalid => "application/x-www-form-urlencoded"
1342 );
1343
1344 make_setter!(SetFormEnctype, "formenctype");
1346
1347 make_enumerated_getter!(
1349 FormMethod,
1350 "formmethod",
1351 "get" | "post" | "dialog",
1352 invalid => "get"
1353 );
1354
1355 make_setter!(SetFormMethod, "formmethod");
1357
1358 make_getter!(FormTarget, "formtarget");
1360
1361 make_setter!(SetFormTarget, "formtarget");
1363
1364 make_bool_getter!(FormNoValidate, "formnovalidate");
1366
1367 make_bool_setter!(SetFormNoValidate, "formnovalidate");
1369
1370 make_getter!(Max, "max");
1372
1373 make_setter!(SetMax, "max");
1375
1376 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1378
1379 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
1381
1382 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
1384
1385 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
1387
1388 make_bool_getter!(Multiple, "multiple");
1390
1391 make_bool_setter!(SetMultiple, "multiple");
1393
1394 make_getter!(Pattern, "pattern");
1396
1397 make_setter!(SetPattern, "pattern");
1399
1400 make_bool_getter!(Required, "required");
1402
1403 make_bool_setter!(SetRequired, "required");
1405
1406 make_url_getter!(Src, "src");
1408
1409 make_url_setter!(SetSrc, "src");
1411
1412 make_getter!(Step, "step");
1414
1415 make_setter!(SetStep, "step");
1417
1418 make_getter!(UseMap, "usemap");
1420
1421 make_setter!(SetUseMap, "usemap");
1423
1424 fn Indeterminate(&self) -> bool {
1426 self.upcast::<Element>()
1427 .state()
1428 .contains(ElementState::INDETERMINATE)
1429 }
1430
1431 fn SetIndeterminate(&self, _cx: &mut JSContext, val: bool) {
1433 self.upcast::<Element>()
1434 .set_state(ElementState::INDETERMINATE, val)
1435 }
1436
1437 fn GetLabels(&self, cx: &mut JSContext) -> Option<DomRoot<NodeList>> {
1441 if matches!(*self.input_type(), InputType::Hidden(_)) {
1442 None
1443 } else {
1444 Some(self.labels_node_list.or_init(|| {
1445 NodeList::new_labels_list(
1446 cx,
1447 self.upcast::<Node>().owner_doc().window(),
1448 self.upcast::<HTMLElement>(),
1449 )
1450 }))
1451 }
1452 }
1453
1454 fn Select(&self) {
1456 self.selection().dom_select();
1457 }
1458
1459 fn GetSelectionStart(&self) -> Option<u32> {
1461 self.selection().dom_start().map(|start| start.0 as u32)
1462 }
1463
1464 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
1466 self.selection()
1467 .set_dom_start(start.map(Utf16CodeUnitLength::from))
1468 }
1469
1470 fn GetSelectionEnd(&self) -> Option<u32> {
1472 self.selection().dom_end().map(|end| end.0 as u32)
1473 }
1474
1475 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
1477 self.selection()
1478 .set_dom_end(end.map(Utf16CodeUnitLength::from))
1479 }
1480
1481 fn GetSelectionDirection(&self) -> Option<DOMString> {
1483 self.selection().dom_direction()
1484 }
1485
1486 fn SetSelectionDirection(
1488 &self,
1489 _cx: &mut JSContext,
1490 direction: Option<DOMString>,
1491 ) -> ErrorResult {
1492 self.selection().set_dom_direction(direction)
1493 }
1494
1495 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
1497 self.selection().set_dom_range(
1498 Utf16CodeUnitLength::from(start),
1499 Utf16CodeUnitLength::from(end),
1500 direction,
1501 )
1502 }
1503
1504 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
1506 self.selection()
1507 .set_dom_range_text(replacement, None, None, Default::default())
1508 }
1509
1510 fn SetRangeText_(
1512 &self,
1513 replacement: DOMString,
1514 start: u32,
1515 end: u32,
1516 selection_mode: SelectionMode,
1517 ) -> ErrorResult {
1518 self.selection().set_dom_range_text(
1519 replacement,
1520 Some(Utf16CodeUnitLength::from(start)),
1521 Some(Utf16CodeUnitLength::from(end)),
1522 selection_mode,
1523 )
1524 }
1525
1526 fn SelectFiles(&self, paths: Vec<DOMString>) {
1529 self.input_type()
1530 .as_specific()
1531 .select_files(self, Some(paths));
1532 }
1533
1534 fn StepUp(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1536 self.step_up_or_down(cx, n, StepDirection::Up)
1537 }
1538
1539 fn StepDown(&self, cx: &mut JSContext, n: i32) -> ErrorResult {
1541 self.step_up_or_down(cx, n, StepDirection::Down)
1542 }
1543
1544 fn WillValidate(&self) -> bool {
1546 self.is_instance_validatable()
1547 }
1548
1549 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
1551 self.validity_state(cx)
1552 }
1553
1554 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
1556 self.check_validity(cx)
1557 }
1558
1559 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
1561 self.report_validity(cx)
1562 }
1563
1564 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
1566 self.validation_message(cx)
1567 }
1568
1569 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
1571 self.validity_state(cx).set_custom_error_message(cx, error);
1572 }
1573}
1574
1575impl HTMLInputElement {
1576 pub(crate) fn form_datums(
1579 &self,
1580 submitter: Option<FormSubmitterElement>,
1581 encoding: Option<&'static Encoding>,
1582 ) -> (Vec<FormDatum>, bool) {
1583 let ty = self.Type();
1584 let name = self.Name();
1585 let is_submitter = match submitter {
1586 Some(FormSubmitterElement::Input(s)) => self == s,
1587 _ => false,
1588 };
1589
1590 match *self.input_type() {
1592 InputType::Submit(_) | InputType::Button(_) | InputType::Reset(_) if !is_submitter => {
1594 return (vec![], true);
1595 },
1596
1597 InputType::Radio(_) | InputType::Checkbox(_) if !self.Checked() => {
1599 return (vec![], true);
1600 },
1601
1602 InputType::Image(_) => return (vec![], true), _ => {
1607 if name.is_empty() {
1608 return (vec![], true);
1609 }
1610 },
1611 }
1612
1613 let datums = match *self.input_type() {
1614 InputType::Checkbox(_) | InputType::Radio(_) => {
1616 let field_value = self.Value();
1618 let value = if field_value.is_empty() {
1619 DOMString::from("on")
1620 } else {
1621 field_value
1622 };
1623 vec![FormDatum {
1625 ty,
1626 name,
1627 value: FormDatumValue::String(value),
1628 }]
1629 },
1630
1631 InputType::File(_) => {
1633 let mut datums = vec![];
1634
1635 let name = self.Name();
1637
1638 match self.GetFiles() {
1639 None => {
1641 datums.push(FormDatum {
1642 ty,
1645 name,
1646 value: FormDatumValue::String(DOMString::from("")),
1647 })
1648 },
1649 Some(fl) => {
1651 for f in fl.iter_files() {
1652 datums.push(FormDatum {
1653 ty: ty.clone(),
1654 name: name.clone(),
1655 value: FormDatumValue::File(DomRoot::from_ref(f)),
1656 });
1657 }
1658 },
1659 }
1660
1661 datums
1662 },
1663
1664 InputType::Hidden(_) if name.to_ascii_lowercase() == "_charset_" => {
1666 let charset = match encoding {
1668 None => DOMString::from("UTF-8"),
1669 Some(enc) => DOMString::from(enc.name()),
1670 };
1671 vec![FormDatum {
1673 ty,
1674 name,
1675 value: FormDatumValue::String(charset),
1676 }]
1677 },
1678
1679 _ => vec![FormDatum {
1681 ty,
1682 name,
1683 value: FormDatumValue::String(self.Value()),
1684 }],
1685 };
1686 (datums, false)
1687 }
1688
1689 pub(crate) fn radio_group_name(&self) -> Option<Atom> {
1691 self.upcast::<Element>()
1692 .get_name()
1693 .filter(|name| !name.is_empty())
1694 }
1695
1696 fn update_checked_state(&self, cx: &mut JSContext, checked: bool, dirty: bool) {
1697 self.upcast::<Element>()
1698 .set_state(ElementState::CHECKED, checked);
1699
1700 if dirty {
1701 self.checked_changed.set(true);
1702 }
1703
1704 if matches!(*self.input_type(), InputType::Radio(_)) && checked {
1705 broadcast_radio_checked(cx, self, self.radio_group_name().as_ref());
1706 }
1707
1708 self.upcast::<Node>().dirty(NodeDamage::Other);
1709 }
1710
1711 pub(crate) fn is_mutable(&self) -> bool {
1713 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
1716 }
1717
1718 pub(crate) fn reset(&self, cx: &mut JSContext) {
1728 self.value_dirty.set(false);
1729
1730 let mut value = self.DefaultValue();
1732 self.sanitize_value(&mut value);
1733 self.textinput.borrow_mut().set_content(value);
1734
1735 let input_type = &*self.input_type();
1736 if matches!(input_type, InputType::Radio(_) | InputType::Checkbox(_)) {
1737 self.update_checked_state(cx, self.DefaultChecked(), false);
1738 self.checked_changed.set(false);
1739 }
1740
1741 if matches!(input_type, InputType::File(_)) {
1742 input_type
1743 .as_specific()
1744 .set_files(&FileList::new(cx, &self.owner_window(), vec![]));
1745 }
1746
1747 self.value_changed(cx);
1748 }
1749
1750 pub(crate) fn clear(&self, cx: &mut JSContext) {
1753 self.value_dirty.set(false);
1755 self.checked_changed.set(false);
1756 self.textinput.borrow_mut().set_content(DOMString::from(""));
1758 self.update_checked_state(cx, self.DefaultChecked(), false);
1760 if self.input_type().as_specific().get_files().is_some() {
1762 let window = self.owner_window();
1763 let filelist = FileList::new(cx, &window, vec![]);
1764 self.input_type().as_specific().set_files(&filelist);
1765 }
1766
1767 {
1770 let mut textinput = self.textinput.borrow_mut();
1771 let mut value = textinput.get_content();
1772 self.sanitize_value(&mut value);
1773 textinput.set_content(value);
1774 }
1775
1776 self.value_changed(cx);
1777 }
1778
1779 fn update_placeholder_shown_state(&self) {
1780 if !self.input_type().is_textual_or_password() {
1781 self.upcast::<Element>().set_placeholder_shown_state(false);
1782 } else {
1783 let has_placeholder = !self.placeholder.borrow().is_empty();
1784 let has_value = !self.textinput.borrow().is_empty();
1785 self.upcast::<Element>()
1786 .set_placeholder_shown_state(has_placeholder && !has_value);
1787 }
1788 }
1789
1790 pub(crate) fn select_files_for_webdriver(
1791 &self,
1792 test_paths: Vec<DOMString>,
1793 response_sender: GenericSender<Result<bool, ErrorStatus>>,
1794 ) {
1795 let mut stored_sender = self.pending_webdriver_response.borrow_mut();
1796 assert!(stored_sender.is_none());
1797
1798 *stored_sender = Some(PendingWebDriverResponse {
1799 response_sender,
1800 expected_file_count: test_paths.len(),
1801 });
1802
1803 self.input_type()
1804 .as_specific()
1805 .select_files(self, Some(test_paths));
1806 }
1807
1808 pub(crate) fn take_pending_webdriver_response(&self) -> Option<PendingWebDriverResponse> {
1809 self.pending_webdriver_response.borrow_mut().take()
1810 }
1811
1812 fn sanitize_value(&self, value: &mut DOMString) {
1814 self.input_type().as_specific().sanitize_value(self, value);
1815 }
1816
1817 fn selection(&self) -> TextControlSelection<'_, Self> {
1818 TextControlSelection::new(self, &self.textinput)
1819 }
1820
1821 fn implicit_submission(&self, cx: &mut JSContext) {
1823 let doc = self.owner_document();
1824 let node = doc.upcast::<Node>();
1825 let owner = self.form_owner();
1826 let form = match owner {
1827 None => return,
1828 Some(ref f) => f,
1829 };
1830
1831 if self.upcast::<Element>().click_in_progress() {
1832 return;
1833 }
1834 let submit_button = node
1835 .traverse_preorder(ShadowIncluding::No)
1836 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1837 .filter(|input| matches!(*input.input_type(), InputType::Submit(_)))
1838 .find(|r| r.form_owner() == owner);
1839 match submit_button {
1840 Some(ref button) => {
1841 if button.is_instance_activatable() {
1842 button
1845 .upcast::<Node>()
1846 .fire_synthetic_pointer_event_not_trusted(cx, atom!("click"));
1847 }
1848 },
1849 None => {
1850 let mut inputs = node
1851 .traverse_preorder(ShadowIncluding::No)
1852 .filter_map(DomRoot::downcast::<HTMLInputElement>)
1853 .filter(|input| {
1854 input.form_owner() == owner &&
1855 matches!(
1856 *input.input_type(),
1857 InputType::Text(_) |
1858 InputType::Search(_) |
1859 InputType::Url(_) |
1860 InputType::Tel(_) |
1861 InputType::Email(_) |
1862 InputType::Password(_) |
1863 InputType::Date(_) |
1864 InputType::Month(_) |
1865 InputType::Week(_) |
1866 InputType::Time(_) |
1867 InputType::DatetimeLocal(_) |
1868 InputType::Number(_)
1869 )
1870 });
1871
1872 if inputs.nth(1).is_some() {
1873 return;
1875 }
1876 form.submit(
1877 cx,
1878 SubmittedFrom::NotFromForm,
1879 FormSubmitterElement::Form(form),
1880 );
1881 },
1882 }
1883 }
1884
1885 pub(crate) fn convert_string_to_number(&self, value: &str) -> Option<f64> {
1887 self.input_type()
1888 .as_specific()
1889 .convert_string_to_number(value)
1890 }
1891
1892 fn convert_number_to_string(&self, value: f64) -> Option<DOMString> {
1894 self.input_type()
1895 .as_specific()
1896 .convert_number_to_string(value)
1897 }
1898
1899 fn update_related_validity_states(&self, cx: &mut JSContext) {
1900 match *self.input_type() {
1901 InputType::Radio(_) => {
1902 perform_radio_group_validation(cx, self, self.radio_group_name().as_ref())
1903 },
1904 _ => {
1905 self.validity_state(cx)
1906 .perform_validation_and_update(cx, ValidationFlags::all());
1907 },
1908 }
1909 }
1910
1911 fn value_changed(&self, cx: &mut JSContext) {
1912 self.maybe_update_shared_selection();
1913 self.update_related_validity_states(cx);
1914 self.input_type().as_specific().update_shadow_tree(cx, self);
1915 }
1916
1917 pub(crate) fn show_the_picker_if_applicable(&self) {
1919 if !self.is_mutable() {
1923 return;
1924 }
1925
1926 self.input_type()
1929 .as_specific()
1930 .show_the_picker_if_applicable(self);
1931 }
1932
1933 pub(crate) fn handle_color_picker_response(
1934 &self,
1935 cx: &mut JSContext,
1936 response: Option<RgbColor>,
1937 ) {
1938 if let InputType::Color(ref color_input_type) = *self.input_type() {
1939 color_input_type.handle_color_picker_response(cx, self, response)
1940 }
1941 }
1942
1943 pub(crate) fn handle_file_picker_response(
1944 &self,
1945 cx: &mut JSContext,
1946 response: Option<Vec<SelectedFile>>,
1947 ) {
1948 if let InputType::File(ref file_input_type) = *self.input_type() {
1949 file_input_type.handle_file_picker_response(cx, self, response)
1950 }
1951 }
1952
1953 fn handle_focus_event(&self, event: &FocusEvent) {
1954 let event_type = event.upcast::<Event>().type_();
1955 if *event_type == *"blur" {
1956 self.owner_document()
1957 .embedder_controls()
1958 .hide_embedder_control(self.upcast());
1959 } else if *event_type == *"focus" {
1960 let input_type = &*self.input_type();
1961 let Ok(input_method_type) = input_type.try_into() else {
1962 return;
1963 };
1964
1965 self.owner_document()
1966 .embedder_controls()
1967 .show_embedder_control(
1968 ControlElement::Ime(Dom::from_ref(self.upcast())),
1969 EmbedderControlRequest::InputMethod(InputMethodRequest {
1970 input_method_type,
1971 text: String::from(self.Value()),
1972 insertion_point: self.GetSelectionEnd(),
1973 multiline: false,
1974 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
1976 }),
1977 None,
1978 );
1979 }
1980 }
1981
1982 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
1983 if mouse_event.upcast::<Event>().DefaultPrevented() {
1984 return;
1985 }
1986
1987 if !self.input_type().is_textual_or_password() || self.textinput.borrow().is_empty() {
1990 return;
1991 }
1992 let node = self.upcast();
1993 if self
1994 .textinput
1995 .borrow_mut()
1996 .handle_mouse_event(node, mouse_event)
1997 {
1998 self.maybe_update_shared_selection();
1999 }
2000 }
2001}
2002
2003impl VirtualMethods for HTMLInputElement {
2004 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2005 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2006 }
2007
2008 fn attribute_mutated(
2009 &self,
2010 cx: &mut JSContext,
2011 attr: AttrRef<'_>,
2012 mutation: AttributeMutation,
2013 ) {
2014 let could_have_had_embedder_control = self.may_have_embedder_control();
2015
2016 self.super_type()
2017 .unwrap()
2018 .attribute_mutated(cx, attr, mutation);
2019
2020 match *attr.local_name() {
2021 local_name!("disabled") => {
2022 let disabled_state = match mutation {
2023 AttributeMutation::Set(None, _) => true,
2024 AttributeMutation::Set(Some(_), _) => {
2025 return;
2027 },
2028 AttributeMutation::Removed => false,
2029 };
2030 let el = self.upcast::<Element>();
2031 el.set_disabled_state(disabled_state);
2032 el.set_enabled_state(!disabled_state);
2033 el.check_ancestors_disabled_state_for_form_control();
2034
2035 if self.input_type().is_textual() {
2036 let read_write = !(self.ReadOnly() || el.disabled_state());
2037 el.set_read_write_state(read_write);
2038 }
2039 },
2040 local_name!("checked") if !self.checked_changed.get() => {
2041 let checked_state = match mutation {
2042 AttributeMutation::Set(None, _) => true,
2043 AttributeMutation::Set(Some(_), _) => {
2044 return;
2046 },
2047 AttributeMutation::Removed => false,
2048 };
2049 self.update_checked_state(cx, checked_state, false);
2050 },
2051 local_name!("size") => {
2052 let size = mutation.new_value(attr).map(|value| value.as_uint());
2053 self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
2054 },
2055 local_name!("type") => {
2056 match mutation {
2057 AttributeMutation::Set(previous_value, _) => {
2058 if previous_value
2062 .is_some_and(|previous_value| **previous_value == **attr.value())
2063 {
2064 return;
2065 }
2066
2067 let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
2068 let previously_selectable = self.selection_api_applies();
2069
2070 *self.input_type.borrow_mut() =
2071 InputType::new_from_atom(attr.value().as_atom());
2072 self.is_textual_or_password
2073 .set(self.input_type().is_textual_or_password());
2074
2075 let element = self.upcast::<Element>();
2076 if self.input_type().is_textual() {
2077 let read_write = !(self.ReadOnly() || element.disabled_state());
2078 element.set_read_write_state(read_write);
2079 } else {
2080 element.set_read_write_state(false);
2081 }
2082
2083 let new_value_mode = self.value_mode();
2084 match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
2085 (&ValueMode::Value, false, ValueMode::Default) |
2087 (&ValueMode::Value, false, ValueMode::DefaultOn) => {
2088 self.SetValue(cx, old_idl_value)
2089 .expect("Failed to set input value on type change to a default ValueMode.");
2090 },
2091
2092 (_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
2094 self.SetValue(
2095 cx,
2096 self.upcast::<Element>()
2097 .get_attribute_string_value(&local_name!("value"))
2098 .unwrap_or_default()
2099 .into(),
2100 )
2101 .expect(
2102 "Failed to set input value on type change to ValueMode::Value.",
2103 );
2104 self.value_dirty.set(false);
2105 },
2106
2107 (_, _, ValueMode::Filename)
2109 if old_value_mode != ValueMode::Filename =>
2110 {
2111 self.SetValue(cx, DOMString::from(""))
2112 .expect("Failed to set input value on type change to ValueMode::Filename.");
2113 },
2114 _ => {},
2115 }
2116
2117 self.input_type().as_specific().signal_type_change(cx, self);
2119
2120 let mut textinput = self.textinput.borrow_mut();
2122 let mut value = textinput.get_content();
2123 self.sanitize_value(&mut value);
2124 textinput.set_content(value);
2125 self.upcast::<Node>().dirty(NodeDamage::Other);
2126
2127 if self.does_minmaxlength_apply() {
2129 textinput.set_min_length(
2130 self.MinLength().to_usize().map(Utf16CodeUnitLength),
2131 );
2132 textinput.set_max_length(
2133 self.MaxLength().to_usize().map(Utf16CodeUnitLength),
2134 );
2135 } else {
2136 textinput.set_min_length(None);
2137 textinput.set_max_length(None);
2138 }
2139
2140 if !previously_selectable && self.selection_api_applies() {
2142 textinput.clear_selection_to_start();
2143 }
2144 },
2145 AttributeMutation::Removed => {
2146 self.input_type().as_specific().signal_type_change(cx, self);
2147 *self.input_type.borrow_mut() = InputType::new_text();
2148 self.is_textual_or_password
2149 .set(self.input_type().is_textual_or_password());
2150
2151 let element = self.upcast::<Element>();
2152 let read_write = !(self.ReadOnly() || element.disabled_state());
2153 element.set_read_write_state(read_write);
2154 },
2155 }
2156
2157 self.update_placeholder_shown_state();
2158 self.input_type()
2159 .as_specific()
2160 .update_placeholder_contents(cx, self);
2161 },
2162 local_name!("value") if !self.value_dirty.get() => {
2163 let value = mutation.new_value(attr).map(|value| (**value).to_owned());
2167 let mut value = value.map_or(DOMString::new(), DOMString::from);
2168
2169 self.sanitize_value(&mut value);
2170 self.textinput.borrow_mut().set_content(value);
2171 self.update_placeholder_shown_state();
2172 },
2173 local_name!("maxlength") if self.does_minmaxlength_apply() => match *attr.value() {
2174 AttrValue::Int(_, value) => {
2175 let mut textinput = self.textinput.borrow_mut();
2176
2177 if value < 0 {
2178 textinput.set_max_length(None);
2179 } else {
2180 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
2181 }
2182 },
2183 _ => panic!("Expected an AttrValue::Int"),
2184 },
2185 local_name!("minlength") if self.does_minmaxlength_apply() => match *attr.value() {
2186 AttrValue::Int(_, value) => {
2187 let mut textinput = self.textinput.borrow_mut();
2188
2189 if value < 0 {
2190 textinput.set_min_length(None);
2191 } else {
2192 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
2193 }
2194 },
2195 _ => panic!("Expected an AttrValue::Int"),
2196 },
2197 local_name!("placeholder") => {
2198 {
2199 let mut placeholder = self.placeholder.borrow_mut();
2200 placeholder.clear();
2201 if let AttributeMutation::Set(..) = mutation {
2202 placeholder
2203 .extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
2204 }
2205 }
2206 self.update_placeholder_shown_state();
2207 self.input_type()
2208 .as_specific()
2209 .update_placeholder_contents(cx, self);
2210 },
2211 local_name!("readonly") => {
2212 if self.input_type().is_textual() {
2213 let el = self.upcast::<Element>();
2214 match mutation {
2215 AttributeMutation::Set(..) => {
2216 el.set_read_write_state(false);
2217 },
2218 AttributeMutation::Removed => {
2219 el.set_read_write_state(!el.disabled_state());
2220 },
2221 }
2222 }
2223 },
2224 local_name!("form") => {
2225 self.form_attribute_mutated(cx, mutation);
2226 },
2227 _ => {
2228 self.input_type()
2229 .as_specific()
2230 .attribute_mutated(cx, self, attr, mutation);
2231 },
2232 }
2233
2234 self.value_changed(cx);
2235
2236 if could_have_had_embedder_control && !self.may_have_embedder_control() {
2237 self.owner_document()
2238 .embedder_controls()
2239 .hide_embedder_control(self.upcast());
2240 }
2241 }
2242
2243 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2244 match *name {
2245 local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
2246 local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
2247 local_name!("type") => AttrValue::from_atomic(value.into()),
2248 local_name!("maxlength") => {
2249 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
2250 },
2251 local_name!("minlength") => {
2252 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
2253 },
2254 _ => self
2255 .super_type()
2256 .unwrap()
2257 .parse_plain_attribute(name, value),
2258 }
2259 }
2260
2261 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2262 if let Some(s) = self.super_type() {
2263 s.bind_to_tree(cx, context);
2264 }
2265 self.upcast::<Element>()
2266 .check_ancestors_disabled_state_for_form_control();
2267
2268 self.input_type()
2269 .as_specific()
2270 .bind_to_tree(cx, self, context);
2271
2272 self.value_changed(cx);
2273 }
2274
2275 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
2276 let form_owner = self.form_owner();
2277 self.super_type().unwrap().unbind_from_tree(cx, context);
2278
2279 let node = self.upcast::<Node>();
2280 let el = self.upcast::<Element>();
2281 if node
2282 .ancestors()
2283 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
2284 {
2285 el.check_ancestors_disabled_state_for_form_control();
2286 } else {
2287 el.check_disabled_attribute();
2288 }
2289
2290 self.input_type()
2291 .as_specific()
2292 .unbind_from_tree(cx, self, form_owner, context);
2293
2294 self.validity_state(cx)
2295 .perform_validation_and_update(cx, ValidationFlags::all());
2296 }
2297
2298 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
2304 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
2305 self.handle_mouse_event(mouse_event);
2306 event.mark_as_handled();
2307 } else if event.type_() == atom!("keydown") &&
2308 !event.DefaultPrevented() &&
2309 self.input_type().is_textual_or_password()
2310 {
2311 if let Some(keyevent) = event.downcast::<KeyboardEvent>() {
2312 let action = self.textinput.borrow_mut().handle_keydown(keyevent);
2315 self.handle_key_reaction(cx, action, event);
2316 }
2317 } else if (event.type_() == atom!("compositionstart") ||
2318 event.type_() == atom!("compositionupdate") ||
2319 event.type_() == atom!("compositionend")) &&
2320 self.input_type().is_textual_or_password()
2321 {
2322 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
2323 if event.type_() == atom!("compositionend") {
2324 let action = self
2325 .textinput
2326 .borrow_mut()
2327 .handle_compositionend(compositionevent);
2328 self.handle_key_reaction(cx, action, event);
2329 self.upcast::<Node>().dirty(NodeDamage::Other);
2330 self.update_placeholder_shown_state();
2331 } else if event.type_() == atom!("compositionupdate") {
2332 let action = self
2333 .textinput
2334 .borrow_mut()
2335 .handle_compositionupdate(compositionevent);
2336 self.handle_key_reaction(cx, action, event);
2337 self.upcast::<Node>().dirty(NodeDamage::Other);
2338 self.update_placeholder_shown_state();
2339 } else if event.type_() == atom!("compositionstart") {
2340 self.update_placeholder_shown_state();
2342 }
2343 event.mark_as_handled();
2344 }
2345 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
2346 let reaction = self
2347 .textinput
2348 .borrow_mut()
2349 .handle_clipboard_event(clipboard_event);
2350 let flags = reaction.flags;
2351 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
2352 self.owner_document().event_handler().fire_clipboard_event(
2353 cx,
2354 None,
2355 ClipboardEventType::Change,
2356 );
2357 }
2358 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
2359 self.textinput.borrow().queue_input_event(
2360 self.upcast(),
2361 reaction.text,
2362 IsComposing::NotComposing,
2363 reaction.input_type,
2364 );
2365 }
2366 if !flags.is_empty() {
2367 event.mark_as_handled();
2368 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
2369 }
2370 } else if let Some(event) = event.downcast::<FocusEvent>() {
2371 self.handle_focus_event(event)
2372 }
2373
2374 self.value_changed(cx);
2375
2376 if let Some(super_type) = self.super_type() {
2377 super_type.handle_event(cx, event);
2378 }
2379 }
2380
2381 fn cloning_steps(
2383 &self,
2384 cx: &mut JSContext,
2385 copy: &Node,
2386 maybe_doc: Option<&Document>,
2387 clone_children: CloneChildrenFlag,
2388 ) {
2389 if let Some(s) = self.super_type() {
2390 s.cloning_steps(cx, copy, maybe_doc, clone_children);
2391 }
2392 let elem = copy.downcast::<HTMLInputElement>().unwrap();
2393 elem.value_dirty.set(self.value_dirty.get());
2394 elem.checked_changed.set(self.checked_changed.get());
2395 elem.upcast::<Element>()
2396 .set_state(ElementState::CHECKED, self.Checked());
2397 elem.upcast::<Element>()
2400 .set_state(ElementState::INDETERMINATE, self.Indeterminate());
2401 elem.textinput
2402 .borrow_mut()
2403 .set_content(self.textinput.borrow().get_content());
2404 self.value_changed(cx);
2405 }
2406}
2407
2408impl FormControl for HTMLInputElement {
2409 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2410 self.form_owner.get()
2411 }
2412
2413 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2414 self.form_owner.set(form);
2415 }
2416
2417 fn to_html_element(&self) -> &HTMLElement {
2418 self.upcast::<HTMLElement>()
2419 }
2420}
2421
2422impl Validatable for HTMLInputElement {
2423 fn as_element(&self) -> &Element {
2424 self.upcast()
2425 }
2426
2427 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
2428 self.validity_state
2429 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
2430 }
2431
2432 fn is_instance_validatable(&self) -> bool {
2433 match *self.input_type() {
2440 InputType::Hidden(_) | InputType::Button(_) | InputType::Reset(_) => false,
2441 _ => {
2442 !(self.upcast::<Element>().disabled_state() ||
2443 self.ReadOnly() ||
2444 is_barred_by_datalist_ancestor(self.upcast()))
2445 },
2446 }
2447 }
2448
2449 fn perform_validation(
2450 &self,
2451 cx: &mut JSContext,
2452 validate_flags: ValidationFlags,
2453 ) -> ValidationFlags {
2454 let mut failed_flags = ValidationFlags::empty();
2455 let value = self.Value();
2456
2457 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
2458 self.suffers_from_being_missing(&value)
2459 {
2460 failed_flags.insert(ValidationFlags::VALUE_MISSING);
2461 }
2462
2463 if validate_flags.contains(ValidationFlags::TYPE_MISMATCH) &&
2464 self.suffers_from_type_mismatch(&value)
2465 {
2466 failed_flags.insert(ValidationFlags::TYPE_MISMATCH);
2467 }
2468
2469 if validate_flags.contains(ValidationFlags::PATTERN_MISMATCH) &&
2470 self.suffers_from_pattern_mismatch(cx, &value)
2471 {
2472 failed_flags.insert(ValidationFlags::PATTERN_MISMATCH);
2473 }
2474
2475 if validate_flags.contains(ValidationFlags::BAD_INPUT) &&
2476 self.suffers_from_bad_input(&value)
2477 {
2478 failed_flags.insert(ValidationFlags::BAD_INPUT);
2479 }
2480
2481 if validate_flags.intersects(ValidationFlags::TOO_LONG | ValidationFlags::TOO_SHORT) {
2482 failed_flags |= self.suffers_from_length_issues(&value);
2483 }
2484
2485 if validate_flags.intersects(
2486 ValidationFlags::RANGE_UNDERFLOW |
2487 ValidationFlags::RANGE_OVERFLOW |
2488 ValidationFlags::STEP_MISMATCH,
2489 ) {
2490 failed_flags |= self.suffers_from_range_issues(&value);
2491 }
2492
2493 failed_flags & validate_flags
2494 }
2495}
2496
2497impl Activatable for HTMLInputElement {
2498 fn as_element(&self) -> &Element {
2499 self.upcast()
2500 }
2501
2502 fn is_instance_activatable(&self) -> bool {
2503 match *self.input_type() {
2504 InputType::Submit(_) |
2511 InputType::Reset(_) |
2512 InputType::File(_) |
2513 InputType::Image(_) |
2514 InputType::Button(_) => self.is_mutable(),
2515 InputType::Checkbox(_) | InputType::Radio(_) | InputType::Color(_) => true,
2519 _ => false,
2520 }
2521 }
2522
2523 fn legacy_pre_activation_behavior(&self, cx: &mut JSContext) -> Option<InputActivationState> {
2525 let activation_state = self
2526 .input_type()
2527 .as_specific()
2528 .legacy_pre_activation_behavior(cx, self);
2529
2530 if activation_state.is_some() {
2531 self.value_changed(cx);
2532 }
2533
2534 activation_state
2535 }
2536
2537 fn legacy_canceled_activation_behavior(
2539 &self,
2540 cx: &mut JSContext,
2541 cache: Option<InputActivationState>,
2542 ) {
2543 let ty = self.input_type();
2545 let cache = match cache {
2546 Some(cache) => {
2547 if (cache.was_radio && !matches!(*ty, InputType::Radio(_))) ||
2548 (cache.was_checkbox && !matches!(*ty, InputType::Checkbox(_)))
2549 {
2550 return;
2553 }
2554 cache
2555 },
2556 None => {
2557 return;
2558 },
2559 };
2560
2561 ty.as_specific()
2563 .legacy_canceled_activation_behavior(cx, self, cache);
2564
2565 self.value_changed(cx);
2566 }
2567
2568 fn activation_behavior(&self, cx: &mut JSContext, event: &Event, target: &EventTarget) {
2570 let input_activation_type = {
2571 let input_type = self.input_type();
2572 InputActivationType::new_from_input_type(&input_type)
2573 };
2574
2575 if let Some(input_activation_type) = input_activation_type {
2576 input_activation_type
2577 .as_specific()
2578 .activation_behavior(cx, self, event, target);
2579 }
2580 }
2581}
2582
2583fn compile_pattern(cx: &mut JSContext, pattern_str: &str, out_regex: MutableHandleObject) -> bool {
2587 if check_js_regex_syntax(cx, pattern_str) {
2589 let pattern_str = format!("^(?:{})$", pattern_str);
2591 let flags = RegExpFlags {
2592 flags_: RegExpFlag_UnicodeSets,
2593 };
2594 new_js_regex(cx, &pattern_str, flags, out_regex)
2595 } else {
2596 false
2597 }
2598}
2599
2600#[expect(unsafe_code)]
2601fn check_js_regex_syntax(cx: &mut JSContext, pattern: &str) -> bool {
2604 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2605 rooted!(&in(cx) let mut exception = UndefinedValue());
2606
2607 let valid = unsafe {
2608 CheckRegExpSyntax(
2609 cx,
2610 pattern.as_ptr(),
2611 pattern.len(),
2612 RegExpFlags {
2613 flags_: RegExpFlag_UnicodeSets,
2614 },
2615 exception.handle_mut(),
2616 )
2617 };
2618
2619 if !valid {
2620 unsafe { JS_ClearPendingException(cx) };
2621 return false;
2622 }
2623
2624 exception.is_undefined()
2627}
2628
2629#[expect(unsafe_code)]
2630fn new_js_regex(
2631 cx: &mut JSContext,
2632 pattern: &str,
2633 flags: RegExpFlags,
2634 mut out_regex: MutableHandleObject,
2635) -> bool {
2636 let pattern: Vec<u16> = pattern.encode_utf16().collect();
2637 out_regex.set(unsafe { NewUCRegExpObject(cx, pattern.as_ptr(), pattern.len(), flags) });
2638
2639 if out_regex.is_null() {
2640 unsafe { JS_ClearPendingException(cx) };
2641 return false;
2642 }
2643 true
2644}
2645
2646#[expect(unsafe_code)]
2647fn matches_js_regex(cx: &mut JSContext, regex_obj: HandleObject, value: &str) -> Result<bool, ()> {
2648 let mut value: Vec<u16> = value.encode_utf16().collect();
2649
2650 let mut is_regex = false;
2651 assert!(unsafe { ObjectIsRegExp(cx, regex_obj, &mut is_regex) });
2652 assert!(is_regex);
2653
2654 rooted!(&in(cx) let mut rval = UndefinedValue());
2655 let mut index = 0;
2656
2657 let ok = unsafe {
2658 ExecuteRegExpNoStatics(
2659 cx,
2660 regex_obj,
2661 value.as_mut_ptr(),
2662 value.len(),
2663 &mut index,
2664 true,
2665 rval.handle_mut(),
2666 )
2667 };
2668
2669 if ok {
2670 Ok(!rval.is_null())
2671 } else {
2672 unsafe { JS_ClearPendingException(cx) };
2673 Err(())
2674 }
2675}
2676
2677#[derive(MallocSizeOf)]
2681pub(crate) struct PendingWebDriverResponse {
2682 response_sender: GenericSender<Result<bool, ErrorStatus>>,
2684 expected_file_count: usize,
2686}
2687
2688impl PendingWebDriverResponse {
2689 pub(crate) fn finish(self, number_files_selected: usize) {
2690 if number_files_selected == self.expected_file_count {
2691 let _ = self.response_sender.send(Ok(false));
2692 } else {
2693 let _ = self.response_sender.send(Err(ErrorStatus::InvalidArgument));
2696 }
2697 }
2698}