1use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use embedder_traits::{EmbedderControlRequest, InputMethodRequest, InputMethodType};
10use html5ever::{LocalName, Prefix, local_name, ns};
11use js::context::{JSContext, NoGC};
12use js::rust::HandleObject;
13use script_bindings::cell::DomRefCell;
14use script_bindings::codegen::GenericBindings::SelectionBinding::SelectionMethods;
15use servo_base::text::{RangeAny, Utf16CodeUnits, Utf32CodeUnits};
16use style::attr::AttrValue;
17use stylo_dom::ElementState;
18
19use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
20use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
21use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
22use crate::dom::bindings::error::ErrorResult;
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::refcounted::Trusted;
25use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
26use crate::dom::bindings::str::DOMString;
27use crate::dom::document::Document;
28use crate::dom::document_embedder_controls::ControlElement;
29use crate::dom::element::attributes::storage::AttrRef;
30use crate::dom::element::{AttributeMutation, Element};
31use crate::dom::event::Event;
32use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
33use crate::dom::eventtarget::EventTarget;
34use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
35use crate::dom::html::form_controls::input_type::text_input_widget::TextInputWidget;
36use crate::dom::html::form_controls::text_control::TextControlElement;
37use crate::dom::html::form_controls::text_input::{KeyReaction, Lines, TextInput};
38use crate::dom::html::htmlelement::HTMLElement;
39use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
40use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
41use crate::dom::node::virtualmethods::VirtualMethods;
42use crate::dom::node::{
43 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeTraits, UnbindContext,
44};
45use crate::dom::nodelist::NodeList;
46use crate::dom::text_input::EmbedderClipboardProvider;
47use crate::dom::types::FocusEvent;
48use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
49use crate::dom::validitystate::{ValidationFlags, ValidityState};
50
51#[dom_struct]
52pub(crate) struct HTMLTextAreaElement {
53 htmlelement: HTMLElement,
54 #[no_trace]
55 text_input: DomRefCell<TextInput<EmbedderClipboardProvider>>,
56 placeholder: RefCell<DOMString>,
57 value_dirty: Cell<bool>,
59 form_owner: MutNullableDom<HTMLFormElement>,
60 labels_node_list: MutNullableDom<NodeList>,
61 validity_state: MutNullableDom<ValidityState>,
62 text_input_widget: DomRefCell<TextInputWidget>,
64
65 has_scheduled_selectionchange_event: Cell<bool>,
67}
68
69impl LayoutDom<'_, HTMLTextAreaElement> {
70 pub(crate) fn selection_for_layout(self) -> Option<RangeAny<Utf32CodeUnits>> {
71 let element = self.unsafe_get();
72 #[expect(unsafe_code)]
73 let text_input = unsafe { element.text_input.borrow_for_layout() };
74 text_input.selection_for_layout
75 }
76
77 pub(crate) fn get_cols(self) -> u32 {
78 self.upcast::<Element>()
79 .get_attr_for_layout(&ns!(), &local_name!("cols"))
80 .map_or(DEFAULT_COLS, AttrValue::as_uint)
81 }
82
83 pub(crate) fn get_rows(self) -> u32 {
84 self.upcast::<Element>()
85 .get_attr_for_layout(&ns!(), &local_name!("rows"))
86 .map_or(DEFAULT_ROWS, AttrValue::as_uint)
87 }
88}
89
90const DEFAULT_COLS: u32 = 20;
92
93const DEFAULT_ROWS: u32 = 2;
95
96const DEFAULT_MAX_LENGTH: i32 = -1;
97const DEFAULT_MIN_LENGTH: i32 = -1;
98
99impl HTMLTextAreaElement {
100 fn new_inherited(
101 local_name: LocalName,
102 prefix: Option<Prefix>,
103 document: &Document,
104 ) -> HTMLTextAreaElement {
105 let embedder_sender = document
106 .window()
107 .as_global_scope()
108 .script_to_embedder_chan()
109 .clone();
110 HTMLTextAreaElement {
111 htmlelement: HTMLElement::new_inherited_with_state(
112 ElementState::ENABLED | ElementState::READWRITE,
113 local_name,
114 prefix,
115 document,
116 ),
117 placeholder: Default::default(),
118 text_input: DomRefCell::new(TextInput::new(
119 Lines::Multiple,
120 DOMString::new(),
121 EmbedderClipboardProvider {
122 embedder_sender,
123 webview_id: document.webview_id(),
124 },
125 )),
126 value_dirty: Cell::new(false),
127 form_owner: Default::default(),
128 labels_node_list: Default::default(),
129 validity_state: Default::default(),
130 text_input_widget: Default::default(),
131 has_scheduled_selectionchange_event: Default::default(),
132 }
133 }
134
135 pub(crate) fn new(
136 cx: &mut JSContext,
137 local_name: LocalName,
138 prefix: Option<Prefix>,
139 document: &Document,
140 proto: Option<HandleObject>,
141 ) -> DomRoot<HTMLTextAreaElement> {
142 Node::reflect_node_with_proto(
143 cx,
144 Box::new(HTMLTextAreaElement::new_inherited(
145 local_name, prefix, document,
146 )),
147 document,
148 proto,
149 )
150 }
151
152 pub(crate) fn auto_directionality(&self) -> String {
153 let value: String = String::from(self.Value());
154 HTMLInputElement::directionality_from_value(&value)
155 }
156
157 pub(crate) fn is_mutable(&self) -> bool {
159 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
162 }
163
164 fn handle_focus_event(&self, cx: &mut JSContext, event: &FocusEvent) {
165 let event_type = event.upcast::<Event>().type_();
166 let document = self.owner_document();
167 if *event_type == *"blur" {
168 document
169 .embedder_controls()
170 .hide_embedder_control(self.upcast());
171 } else if *event_type == *"focus" {
172 if let Some(selection) = document.selection() {
177 let _ = selection.Collapse(cx, None, 0);
178 }
179
180 document.embedder_controls().show_embedder_control(
181 ControlElement::Ime(Dom::from_ref(self.upcast())),
182 EmbedderControlRequest::InputMethod(InputMethodRequest {
183 input_method_type: InputMethodType::Text,
184 text: String::from(self.Value()),
185 insertion_point: self.GetSelectionEnd(),
186 multiline: false,
187 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
189 }),
190 None,
191 );
192 }
193
194 self.maybe_update_shared_selection();
196 }
197
198 fn schedule_a_selection_change_event(&self) {
200 if self.has_scheduled_selectionchange_event.get() {
202 return;
203 }
204 self.has_scheduled_selectionchange_event.set(true);
206 let this = Trusted::new(self);
208 self.owner_global()
209 .task_manager()
210 .user_interaction_task_source()
211 .queue(
212 task!(selectionchange_task_steps: move |cx| {
214 let this = this.root();
215 this.has_scheduled_selectionchange_event.set(false);
217 this.upcast::<EventTarget>().fire_event_with_params(
219 cx,
220 atom!("selectionchange"),
221 EventBubbles::Bubbles,
222 EventCancelable::NotCancelable,
223 EventComposed::Composed,
224 );
225 }),
230 );
231 }
232}
233
234impl TextControlElement for HTMLTextAreaElement {
235 fn as_element(&self) -> &Element {
236 self.upcast()
237 }
238
239 fn text_input(&self) -> Ref<'_, TextInput<EmbedderClipboardProvider>> {
240 self.text_input.borrow()
241 }
242
243 fn text_input_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
244 self.text_input.borrow_mut()
245 }
246
247 fn selection_api_applies(&self) -> bool {
248 true
249 }
250
251 fn has_selectable_text(&self) -> bool {
252 !self.text_input.borrow().get_content().is_empty()
253 }
254
255 fn has_uncollapsed_selection(&self) -> bool {
256 self.text_input.borrow().has_uncollapsed_selection()
257 }
258
259 fn set_dirty_value_flag(&self, value: bool) {
260 self.value_dirty.set(value)
261 }
262
263 fn select_all(&self) {
264 self.text_input.borrow_mut().select_all();
265 self.maybe_update_shared_selection();
266 }
267
268 fn maybe_update_shared_selection(&self) {
269 let selection = {
270 let mut text_input = self.text_input.borrow_mut();
271 let selection_range = text_input.selection_start()..text_input.selection_end();
272 let enabled = self.upcast::<Element>().focus_state();
273
274 let range_remained_equal = selection_range == text_input.previous_selection_range;
275 if range_remained_equal && enabled == text_input.selection_for_layout.is_some() {
276 return;
277 }
278
279 if !range_remained_equal {
280 self.schedule_a_selection_change_event();
285 }
286
287 let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range());
288 text_input.previous_selection_range = selection_range;
289 text_input.selection_for_layout = selection;
290 selection
291 };
292
293 if self
294 .text_input_widget
295 .borrow()
296 .set_text_run_selection(selection)
297 {
298 self.owner_window().layout().set_needs_new_display_list();
300 } else {
301 }
303 }
304
305 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
306 self.placeholder.borrow()
307 }
308
309 fn value_text(&self) -> DOMString {
310 self.Value()
311 }
312
313 fn read_only_or_disabled(&self) -> bool {
314 self.ReadOnly() || self.Disabled()
315 }
316
317 fn handle_text_content_changed(&self, cx: &mut JSContext) {
318 self.validity_state(cx)
319 .perform_validation_and_update(cx, ValidationFlags::all());
320
321 let placeholder_shown =
322 self.text_input.borrow().is_empty() && !self.placeholder.borrow().is_empty();
323 self.upcast::<Element>()
324 .set_placeholder_shown_state(placeholder_shown);
325
326 self.text_input_widget.borrow().update_shadow_tree(cx, self);
327 self.text_input_widget
328 .borrow()
329 .update_placeholder_contents(cx, self);
330 self.maybe_update_shared_selection();
331 }
332
333 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction) {
334 match action {
335 KeyReaction::TriggerDefaultAction => (),
336 KeyReaction::DispatchInput(text, is_composing, input_type) => {
337 self.queue_input_event(text, is_composing, input_type);
338 self.value_dirty.set(true);
339 self.handle_text_content_changed(cx);
340 },
341 KeyReaction::RedrawSelection => {
342 self.maybe_update_shared_selection();
343 },
344 KeyReaction::Nothing => (),
345 }
346 }
347}
348
349impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
350 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
355
356 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
358
359 make_getter!(DirName, "dirname");
361
362 make_setter!(SetDirName, "dirname");
364
365 make_bool_getter!(Disabled, "disabled");
367
368 make_bool_setter!(SetDisabled, "disabled");
370
371 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
373 self.form_owner()
374 }
375
376 make_getter!(Name, "name");
378
379 make_atomic_setter!(SetName, "name");
381
382 make_getter!(Placeholder, "placeholder");
384
385 make_setter!(SetPlaceholder, "placeholder");
387
388 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
390
391 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
393
394 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
396
397 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
399
400 make_bool_getter!(ReadOnly, "readonly");
402
403 make_bool_setter!(SetReadOnly, "readonly");
405
406 make_bool_getter!(Required, "required");
408
409 make_bool_setter!(SetRequired, "required");
411
412 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
414
415 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
417
418 make_getter!(Wrap, "wrap");
420
421 make_setter!(SetWrap, "wrap");
423
424 fn Type(&self) -> DOMString {
426 DOMString::from_static("textarea")
427 }
428
429 fn DefaultValue(&self) -> DOMString {
431 self.upcast::<Node>().GetTextContent().unwrap()
432 }
433
434 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
436 self.upcast::<Node>()
437 .set_text_content_for_element(cx, Some(value));
438
439 if !self.value_dirty.get() {
442 self.reset(cx);
443 }
444 }
445
446 fn Value(&self) -> DOMString {
448 self.text_input.borrow().get_content()
449 }
450
451 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
453 let old_api_value = self.Value();
455
456 self.text_input.borrow_mut().set_content(value);
458
459 self.value_dirty.set(true);
461
462 if old_api_value != self.Value() {
467 self.text_input.borrow_mut().clear_selection_to_end();
468 self.handle_text_content_changed(cx);
469 }
470 }
471
472 fn TextLength(&self) -> u32 {
474 self.text_input.borrow().len_utf16().0
475 }
476
477 make_labels_getter!(Labels, labels_node_list);
479
480 fn Select(&self, cx: &mut JSContext) {
482 self.dom_select(cx);
483 }
484
485 fn GetSelectionStart(&self) -> Option<u32> {
487 self.dom_start().map(|start| start.0)
488 }
489
490 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
492 self.set_dom_start(start.map(Utf16CodeUnits::from))
493 }
494
495 fn GetSelectionEnd(&self) -> Option<u32> {
497 self.dom_end().map(|end| end.0)
498 }
499
500 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
502 self.set_dom_end(end.map(Utf16CodeUnits::from))
503 }
504
505 fn GetSelectionDirection(&self) -> Option<DOMString> {
507 self.dom_direction()
508 }
509
510 fn SetSelectionDirection(
512 &self,
513 _cx: &mut JSContext,
514 direction: Option<DOMString>,
515 ) -> ErrorResult {
516 self.set_dom_direction(direction)
517 }
518
519 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
521 self.set_dom_range(
522 Utf16CodeUnits::from(start),
523 Utf16CodeUnits::from(end),
524 direction,
525 )
526 }
527
528 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
530 self.set_dom_range_text(replacement, None, None, Default::default())
531 }
532
533 fn SetRangeText_(
535 &self,
536 replacement: DOMString,
537 start: u32,
538 end: u32,
539 selection_mode: SelectionMode,
540 ) -> ErrorResult {
541 self.set_dom_range_text(
542 replacement,
543 Some(Utf16CodeUnits::from(start)),
544 Some(Utf16CodeUnits::from(end)),
545 selection_mode,
546 )
547 }
548
549 fn WillValidate(&self, no_gc: &NoGC) -> bool {
551 self.is_instance_validatable(no_gc)
552 }
553
554 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
556 self.validity_state(cx)
557 }
558
559 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
561 self.check_validity(cx)
562 }
563
564 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
566 self.report_validity(cx)
567 }
568
569 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
571 self.validation_message(cx)
572 }
573
574 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
576 self.validity_state(cx).set_custom_error_message(cx, error);
577 }
578}
579
580impl HTMLTextAreaElement {
581 pub(crate) fn clear(&self) {
584 self.value_dirty.set(false);
585 self.text_input.borrow_mut().set_content(DOMString::new());
586 }
587
588 pub(crate) fn reset(&self, cx: &mut JSContext) {
589 self.value_dirty.set(false);
591 self.text_input
592 .borrow_mut()
593 .set_content(self.DefaultValue());
594 self.handle_text_content_changed(cx);
595 }
596}
597
598impl VirtualMethods for HTMLTextAreaElement {
599 fn super_type(&self) -> Option<&dyn VirtualMethods> {
600 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
601 }
602
603 fn attribute_mutated(
604 &self,
605 cx: &mut JSContext,
606 attr: AttrRef<'_>,
607 mutation: AttributeMutation,
608 ) {
609 self.super_type()
610 .unwrap()
611 .attribute_mutated(cx, attr, mutation);
612 match *attr.local_name() {
613 local_name!("disabled") => {
614 let el = self.upcast::<Element>();
615 match mutation {
616 AttributeMutation::Set(_) => {
617 el.set_disabled_state(true);
618 el.set_enabled_state(false);
619
620 el.set_read_write_state(false);
621 },
622 AttributeMutation::Removed => {
623 el.set_disabled_state(false);
624 el.set_enabled_state(true);
625 el.check_ancestors_disabled_state_for_form_control(cx.no_gc());
626
627 if !el.disabled_state() && !el.read_write_state() {
628 el.set_read_write_state(true);
629 }
630 },
631 }
632 },
633 local_name!("maxlength") => match *attr.value() {
634 AttrValue::Int(_, value) => {
635 let mut text_input = self.text_input.borrow_mut();
636
637 if value < 0 {
638 text_input.set_max_length(None);
639 } else {
640 text_input.set_max_length(Some(Utf16CodeUnits(value as u32)))
641 }
642 },
643 _ => panic!("Expected an AttrValue::Int"),
644 },
645 local_name!("minlength") => match *attr.value() {
646 AttrValue::Int(_, value) => {
647 let mut text_input = self.text_input.borrow_mut();
648
649 if value < 0 {
650 text_input.set_min_length(None);
651 } else {
652 text_input.set_min_length(Some(Utf16CodeUnits(value as u32)))
653 }
654 },
655 _ => panic!("Expected an AttrValue::Int"),
656 },
657 local_name!("placeholder") => {
658 {
659 let mut placeholder = self.placeholder.borrow_mut();
660 match mutation {
661 AttributeMutation::Set(_) => {
662 let value = attr.value();
663 let value_str: &str = value.as_ref();
664 *placeholder =
665 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
666 },
667 AttributeMutation::Removed => placeholder.clear(),
668 }
669 }
670 self.handle_text_content_changed(cx);
671 },
672 local_name!("readonly") => {
673 let el = self.upcast::<Element>();
674 match mutation {
675 AttributeMutation::Set(_) => {
676 el.set_read_write_state(false);
677 },
678 AttributeMutation::Removed => {
679 el.set_read_write_state(!el.disabled_state());
680 },
681 }
682 },
683 local_name!("form") => {
684 self.form_attribute_mutated(cx, mutation);
685 },
686 _ => {},
687 }
688
689 self.validity_state(cx)
690 .perform_validation_and_update(cx, ValidationFlags::all());
691 }
692
693 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
694 if let Some(s) = self.super_type() {
695 s.bind_to_tree(cx, context);
696 }
697
698 self.upcast::<Element>()
699 .check_ancestors_disabled_state_for_form_control(cx.no_gc());
700
701 self.handle_text_content_changed(cx);
702 }
703
704 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
705 match *name {
706 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
707 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
708 local_name!("maxlength") => {
709 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
710 },
711 local_name!("minlength") => {
712 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
713 },
714 _ => self
715 .super_type()
716 .unwrap()
717 .parse_plain_attribute(name, value),
718 }
719 }
720
721 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
722 self.owner_document()
724 .embedder_controls()
725 .hide_embedder_control(self.upcast());
726
727 self.super_type().unwrap().unbind_from_tree(cx, context);
728
729 let node = self.upcast::<Node>();
730 let el = self.upcast::<Element>();
731 if node
732 .ancestors()
733 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
734 {
735 el.check_ancestors_disabled_state_for_form_control(cx.no_gc());
736 } else {
737 el.check_disabled_attribute();
738 }
739
740 self.validity_state(cx)
741 .perform_validation_and_update(cx, ValidationFlags::all());
742 }
743
744 fn cloning_steps(
747 &self,
748 cx: &mut JSContext,
749 copy: &Node,
750 maybe_doc: Option<&Document>,
751 clone_children: CloneChildrenFlag,
752 ) {
753 if let Some(s) = self.super_type() {
754 s.cloning_steps(cx, copy, maybe_doc, clone_children);
755 }
756 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
757 el.value_dirty.set(self.value_dirty.get());
758 {
759 let mut text_input = el.text_input.borrow_mut();
760 text_input.set_content(self.text_input.borrow().get_content());
761 }
762 el.validity_state(cx)
763 .perform_validation_and_update(cx, ValidationFlags::all());
764 }
765
766 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
767 if let Some(s) = self.super_type() {
768 s.children_changed(cx, mutation);
769 }
770 if !self.value_dirty.get() {
771 self.reset(cx);
772 }
773 }
774
775 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
777 if let Some(event) = event.downcast::<FocusEvent>() {
778 self.handle_focus_event(cx, event);
779 }
780
781 self.validity_state(cx)
782 .perform_validation_and_update(cx, ValidationFlags::all());
783
784 if let Some(super_type) = self.super_type() {
785 super_type.handle_event(cx, event);
786 }
787 }
788
789 fn pop(&self, cx: &mut JSContext) {
790 self.super_type().unwrap().pop(cx);
791
792 self.reset(cx);
794 }
795}
796
797impl FormControl for HTMLTextAreaElement {
798 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
799 self.form_owner.get()
800 }
801
802 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
803 self.form_owner.set(form);
804 }
805
806 fn to_html_element(&self) -> &HTMLElement {
807 self.upcast::<HTMLElement>()
808 }
809}
810
811impl Validatable for HTMLTextAreaElement {
812 fn as_element(&self) -> &Element {
813 self.upcast()
814 }
815
816 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
817 self.validity_state
818 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
819 }
820
821 fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
822 !self.upcast::<Element>().disabled_state() &&
826 !self.ReadOnly() &&
827 !is_barred_by_datalist_ancestor(no_gc, self.upcast())
828 }
829
830 fn perform_validation(
831 &self,
832 _cx: &mut JSContext,
833 validate_flags: ValidationFlags,
834 ) -> ValidationFlags {
835 let mut failed_flags = ValidationFlags::empty();
836
837 let text_input = self.text_input.borrow();
838 let Utf16CodeUnits(value_len) = text_input.len_utf16();
839 let last_edit_by_user = !text_input.was_last_change_by_set_content();
840 let value_dirty = self.value_dirty.get();
841
842 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
845 self.Required() &&
846 self.is_mutable() &&
847 value_len == 0
848 {
849 failed_flags.insert(ValidationFlags::VALUE_MISSING);
850 }
851
852 if value_dirty && last_edit_by_user && value_len > 0 {
853 if validate_flags.contains(ValidationFlags::TOO_LONG) {
856 let max_length = self.MaxLength();
857 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as u32) {
858 failed_flags.insert(ValidationFlags::TOO_LONG);
859 }
860 }
861
862 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
865 let min_length = self.MinLength();
866 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as u32) {
867 failed_flags.insert(ValidationFlags::TOO_SHORT);
868 }
869 }
870 }
871
872 failed_flags
873 }
874}