1use std::cell::Cell;
6use std::default::Default;
7use std::ops::Range;
8
9use dom_struct::dom_struct;
10use embedder_traits::{EmbedderControlRequest, InputMethodRequest, InputMethodType};
11use html5ever::{LocalName, Prefix, local_name, ns};
12use js::rust::HandleObject;
13use style::attr::AttrValue;
14use stylo_dom::ElementState;
15
16use crate::clipboard_provider::EmbedderClipboardProvider;
17use crate::dom::attr::Attr;
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
20use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
21use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
22use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
23use crate::dom::bindings::error::ErrorResult;
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
26use crate::dom::bindings::str::DOMString;
27use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
28use crate::dom::compositionevent::CompositionEvent;
29use crate::dom::document::Document;
30use crate::dom::document_embedder_controls::ControlElement;
31use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
32use crate::dom::event::Event;
33use crate::dom::html::htmlelement::HTMLElement;
34use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
35use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
36use crate::dom::html::htmlinputelement::HTMLInputElement;
37use crate::dom::keyboardevent::KeyboardEvent;
38use crate::dom::node::{
39 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
40};
41use crate::dom::nodelist::NodeList;
42use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
43use crate::dom::types::FocusEvent;
44use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
45use crate::dom::validitystate::{ValidationFlags, ValidityState};
46use crate::dom::virtualmethods::VirtualMethods;
47use crate::script_runtime::CanGc;
48use crate::textinput::{
49 ClipboardEventFlags, Direction, IsComposing, KeyReaction, Lines, SelectionDirection, TextInput,
50 UTF8Bytes, UTF16CodeUnits,
51};
52
53#[dom_struct]
54pub(crate) struct HTMLTextAreaElement {
55 htmlelement: HTMLElement,
56 #[no_trace]
57 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
58 placeholder: DomRefCell<String>,
59 value_dirty: Cell<bool>,
61 form_owner: MutNullableDom<HTMLFormElement>,
62 labels_node_list: MutNullableDom<NodeList>,
63 validity_state: MutNullableDom<ValidityState>,
64}
65
66pub(crate) trait LayoutHTMLTextAreaElementHelpers {
67 fn value_for_layout(self) -> String;
68 fn selection_for_layout(self) -> Option<Range<usize>>;
69 fn get_cols(self) -> u32;
70 fn get_rows(self) -> u32;
71}
72
73#[expect(unsafe_code)]
74impl<'dom> LayoutDom<'dom, HTMLTextAreaElement> {
75 fn textinput_content(self) -> DOMString {
76 unsafe {
77 self.unsafe_get()
78 .textinput
79 .borrow_for_layout()
80 .get_content()
81 }
82 }
83
84 fn textinput_sorted_selection_offsets_range(self) -> Range<UTF8Bytes> {
85 unsafe {
86 self.unsafe_get()
87 .textinput
88 .borrow_for_layout()
89 .sorted_selection_offsets_range()
90 }
91 }
92
93 fn placeholder(self) -> &'dom str {
94 unsafe { self.unsafe_get().placeholder.borrow_for_layout() }
95 }
96}
97
98impl LayoutHTMLTextAreaElementHelpers for LayoutDom<'_, HTMLTextAreaElement> {
99 fn value_for_layout(self) -> String {
100 let text = self.textinput_content();
101 if text.is_empty() {
102 self.placeholder().replace("\r\n", "\n").replace('\r', "\n")
105 } else {
106 text.into()
107 }
108 }
109
110 fn selection_for_layout(self) -> Option<Range<usize>> {
111 if !self.upcast::<Element>().focus_state() {
112 return None;
113 }
114 Some(UTF8Bytes::unwrap_range(
115 self.textinput_sorted_selection_offsets_range(),
116 ))
117 }
118
119 fn get_cols(self) -> u32 {
120 self.upcast::<Element>()
121 .get_attr_for_layout(&ns!(), &local_name!("cols"))
122 .map_or(DEFAULT_COLS, AttrValue::as_uint)
123 }
124
125 fn get_rows(self) -> u32 {
126 self.upcast::<Element>()
127 .get_attr_for_layout(&ns!(), &local_name!("rows"))
128 .map_or(DEFAULT_ROWS, AttrValue::as_uint)
129 }
130}
131
132const DEFAULT_COLS: u32 = 20;
134
135const DEFAULT_ROWS: u32 = 2;
137
138const DEFAULT_MAX_LENGTH: i32 = -1;
139const DEFAULT_MIN_LENGTH: i32 = -1;
140
141impl HTMLTextAreaElement {
142 fn new_inherited(
143 local_name: LocalName,
144 prefix: Option<Prefix>,
145 document: &Document,
146 ) -> HTMLTextAreaElement {
147 let embedder_sender = document
148 .window()
149 .as_global_scope()
150 .script_to_embedder_chan()
151 .clone();
152 HTMLTextAreaElement {
153 htmlelement: HTMLElement::new_inherited_with_state(
154 ElementState::ENABLED | ElementState::READWRITE,
155 local_name,
156 prefix,
157 document,
158 ),
159 placeholder: DomRefCell::new(String::new()),
160 textinput: DomRefCell::new(TextInput::new(
161 Lines::Multiple,
162 DOMString::new(),
163 EmbedderClipboardProvider {
164 embedder_sender,
165 webview_id: document.webview_id(),
166 },
167 None,
168 None,
169 SelectionDirection::None,
170 )),
171 value_dirty: Cell::new(false),
172 form_owner: Default::default(),
173 labels_node_list: Default::default(),
174 validity_state: Default::default(),
175 }
176 }
177
178 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
179 pub(crate) fn new(
180 local_name: LocalName,
181 prefix: Option<Prefix>,
182 document: &Document,
183 proto: Option<HandleObject>,
184 can_gc: CanGc,
185 ) -> DomRoot<HTMLTextAreaElement> {
186 Node::reflect_node_with_proto(
187 Box::new(HTMLTextAreaElement::new_inherited(
188 local_name, prefix, document,
189 )),
190 document,
191 proto,
192 can_gc,
193 )
194 }
195
196 pub(crate) fn auto_directionality(&self) -> String {
197 let value: String = self.Value().to_string();
198 HTMLInputElement::directionality_from_value(&value)
199 }
200
201 fn update_placeholder_shown_state(&self) {
202 let has_placeholder = !self.placeholder.borrow().is_empty();
203 let has_value = !self.textinput.borrow().is_empty();
204 let el = self.upcast::<Element>();
205 el.set_placeholder_shown_state(has_placeholder && !has_value);
206 }
207
208 pub(crate) fn is_mutable(&self) -> bool {
210 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
213 }
214
215 fn handle_focus(&self) {
216 self.owner_document()
217 .embedder_controls()
218 .show_embedder_control(
219 ControlElement::Ime(DomRoot::from_ref(self.upcast())),
220 EmbedderControlRequest::InputMethod(InputMethodRequest {
221 input_method_type: InputMethodType::Text,
222 text: self.Value().to_string(),
223 insertion_point: self.GetSelectionEnd(),
224 multiline: false,
225 }),
226 None,
227 );
228 }
229}
230
231impl TextControlElement for HTMLTextAreaElement {
232 fn selection_api_applies(&self) -> bool {
233 true
234 }
235
236 fn has_selectable_text(&self) -> bool {
237 !self.textinput.borrow().get_content().is_empty()
238 }
239
240 fn has_selection(&self) -> bool {
241 self.textinput.borrow().has_selection()
242 }
243
244 fn set_dirty_value_flag(&self, value: bool) {
245 self.value_dirty.set(value)
246 }
247
248 fn select_all(&self) {
249 self.textinput.borrow_mut().select_all();
250 self.upcast::<Node>().dirty(NodeDamage::Other);
251 }
252}
253
254impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
255 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
260
261 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
263
264 make_getter!(DirName, "dirname");
266
267 make_setter!(SetDirName, "dirname");
269
270 make_bool_getter!(Disabled, "disabled");
272
273 make_bool_setter!(SetDisabled, "disabled");
275
276 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
278 self.form_owner()
279 }
280
281 make_getter!(Name, "name");
283
284 make_atomic_setter!(SetName, "name");
286
287 make_getter!(Placeholder, "placeholder");
289
290 make_setter!(SetPlaceholder, "placeholder");
292
293 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
295
296 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
298
299 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
301
302 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
304
305 make_bool_getter!(ReadOnly, "readonly");
307
308 make_bool_setter!(SetReadOnly, "readonly");
310
311 make_bool_getter!(Required, "required");
313
314 make_bool_setter!(SetRequired, "required");
316
317 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
319
320 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
322
323 make_getter!(Wrap, "wrap");
325
326 make_setter!(SetWrap, "wrap");
328
329 fn Type(&self) -> DOMString {
331 DOMString::from("textarea")
332 }
333
334 fn DefaultValue(&self) -> DOMString {
336 self.upcast::<Node>().GetTextContent().unwrap()
337 }
338
339 fn SetDefaultValue(&self, value: DOMString, can_gc: CanGc) {
341 self.upcast::<Node>()
342 .set_text_content_for_element(Some(value), can_gc);
343
344 if !self.value_dirty.get() {
347 self.reset();
348 }
349 }
350
351 fn Value(&self) -> DOMString {
353 self.textinput.borrow().get_content()
354 }
355
356 fn SetValue(&self, value: DOMString, can_gc: CanGc) {
358 {
359 let mut textinput = self.textinput.borrow_mut();
360
361 let old_value = textinput.get_content();
363
364 textinput.set_content(value);
366
367 self.value_dirty.set(true);
369
370 if old_value != textinput.get_content() {
371 textinput.clear_selection_to_limit(Direction::Forward);
373 }
374 }
375
376 self.validity_state(can_gc)
377 .perform_validation_and_update(ValidationFlags::all(), can_gc);
378 self.upcast::<Node>().dirty(NodeDamage::Other);
379 }
380
381 fn TextLength(&self) -> u32 {
383 let UTF16CodeUnits(num_units) = self.textinput.borrow().utf16_len();
384 num_units as u32
385 }
386
387 make_labels_getter!(Labels, labels_node_list);
389
390 fn Select(&self) {
392 self.selection().dom_select();
393 }
394
395 fn GetSelectionStart(&self) -> Option<u32> {
397 self.selection().dom_start()
398 }
399
400 fn SetSelectionStart(&self, start: Option<u32>) -> ErrorResult {
402 self.selection().set_dom_start(start)
403 }
404
405 fn GetSelectionEnd(&self) -> Option<u32> {
407 self.selection().dom_end()
408 }
409
410 fn SetSelectionEnd(&self, end: Option<u32>) -> ErrorResult {
412 self.selection().set_dom_end(end)
413 }
414
415 fn GetSelectionDirection(&self) -> Option<DOMString> {
417 self.selection().dom_direction()
418 }
419
420 fn SetSelectionDirection(&self, direction: Option<DOMString>) -> ErrorResult {
422 self.selection().set_dom_direction(direction)
423 }
424
425 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
427 self.selection().set_dom_range(start, end, direction)
428 }
429
430 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
432 self.selection()
433 .set_dom_range_text(replacement, None, None, Default::default())
434 }
435
436 fn SetRangeText_(
438 &self,
439 replacement: DOMString,
440 start: u32,
441 end: u32,
442 selection_mode: SelectionMode,
443 ) -> ErrorResult {
444 self.selection()
445 .set_dom_range_text(replacement, Some(start), Some(end), selection_mode)
446 }
447
448 fn WillValidate(&self) -> bool {
450 self.is_instance_validatable()
451 }
452
453 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
455 self.validity_state(can_gc)
456 }
457
458 fn CheckValidity(&self, can_gc: CanGc) -> bool {
460 self.check_validity(can_gc)
461 }
462
463 fn ReportValidity(&self, can_gc: CanGc) -> bool {
465 self.report_validity(can_gc)
466 }
467
468 fn ValidationMessage(&self) -> DOMString {
470 self.validation_message()
471 }
472
473 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
475 self.validity_state(can_gc).set_custom_error_message(error);
476 }
477}
478
479impl HTMLTextAreaElement {
480 pub(crate) fn clear(&self) {
483 self.value_dirty.set(false);
484 self.textinput.borrow_mut().set_content(DOMString::from(""));
485 }
486
487 pub(crate) fn reset(&self) {
488 let mut textinput = self.textinput.borrow_mut();
490 textinput.set_content(self.DefaultValue());
491 self.value_dirty.set(false);
492 }
493
494 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
495 fn selection(&self) -> TextControlSelection<'_, Self> {
496 TextControlSelection::new(self, &self.textinput)
497 }
498
499 fn handle_key_reaction(&self, action: KeyReaction, event: &Event) {
500 match action {
501 KeyReaction::TriggerDefaultAction => (),
502 KeyReaction::DispatchInput(text, is_composing, input_type) => {
503 if event.IsTrusted() {
504 self.textinput.borrow().queue_input_event(
505 self.upcast(),
506 text,
507 is_composing,
508 input_type,
509 );
510 }
511 self.value_dirty.set(true);
512 self.update_placeholder_shown_state();
513 self.upcast::<Node>().dirty(NodeDamage::Other);
514 event.mark_as_handled();
515 },
516 KeyReaction::RedrawSelection => {
517 self.upcast::<Node>().dirty(NodeDamage::Other);
518 event.mark_as_handled();
519 },
520 KeyReaction::Nothing => (),
521 }
522 }
523}
524
525impl VirtualMethods for HTMLTextAreaElement {
526 fn super_type(&self) -> Option<&dyn VirtualMethods> {
527 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
528 }
529
530 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
531 self.super_type()
532 .unwrap()
533 .attribute_mutated(attr, mutation, can_gc);
534 match *attr.local_name() {
535 local_name!("disabled") => {
536 let el = self.upcast::<Element>();
537 match mutation {
538 AttributeMutation::Set(..) => {
539 el.set_disabled_state(true);
540 el.set_enabled_state(false);
541
542 el.set_read_write_state(false);
543 },
544 AttributeMutation::Removed => {
545 el.set_disabled_state(false);
546 el.set_enabled_state(true);
547 el.check_ancestors_disabled_state_for_form_control();
548
549 if !el.disabled_state() && !el.read_write_state() {
550 el.set_read_write_state(true);
551 }
552 },
553 }
554 el.update_sequentially_focusable_status(CanGc::note());
555 },
556 local_name!("maxlength") => match *attr.value() {
557 AttrValue::Int(_, value) => {
558 let mut textinput = self.textinput.borrow_mut();
559
560 if value < 0 {
561 textinput.set_max_length(None);
562 } else {
563 textinput.set_max_length(Some(UTF16CodeUnits(value as usize)))
564 }
565 },
566 _ => panic!("Expected an AttrValue::Int"),
567 },
568 local_name!("minlength") => match *attr.value() {
569 AttrValue::Int(_, value) => {
570 let mut textinput = self.textinput.borrow_mut();
571
572 if value < 0 {
573 textinput.set_min_length(None);
574 } else {
575 textinput.set_min_length(Some(UTF16CodeUnits(value as usize)))
576 }
577 },
578 _ => panic!("Expected an AttrValue::Int"),
579 },
580 local_name!("placeholder") => {
581 {
582 let mut placeholder = self.placeholder.borrow_mut();
583 placeholder.clear();
584 if let AttributeMutation::Set(..) = mutation {
585 placeholder.push_str(attr.value().as_ref());
586 }
587 }
588 self.update_placeholder_shown_state();
589 },
590 local_name!("readonly") => {
591 let el = self.upcast::<Element>();
592 match mutation {
593 AttributeMutation::Set(..) => {
594 el.set_read_write_state(false);
595 },
596 AttributeMutation::Removed => {
597 el.set_read_write_state(!el.disabled_state());
598 },
599 }
600 },
601 local_name!("form") => {
602 self.form_attribute_mutated(mutation, can_gc);
603 },
604 _ => {},
605 }
606
607 self.validity_state(can_gc)
608 .perform_validation_and_update(ValidationFlags::all(), can_gc);
609 }
610
611 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
612 if let Some(s) = self.super_type() {
613 s.bind_to_tree(context, can_gc);
614 }
615
616 self.upcast::<Element>()
617 .check_ancestors_disabled_state_for_form_control();
618
619 self.validity_state(can_gc)
620 .perform_validation_and_update(ValidationFlags::all(), can_gc);
621 }
622
623 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
624 match *name {
625 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
626 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
627 local_name!("maxlength") => {
628 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
629 },
630 local_name!("minlength") => {
631 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
632 },
633 _ => self
634 .super_type()
635 .unwrap()
636 .parse_plain_attribute(name, value),
637 }
638 }
639
640 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
641 self.super_type().unwrap().unbind_from_tree(context, can_gc);
642
643 let node = self.upcast::<Node>();
644 let el = self.upcast::<Element>();
645 if node
646 .ancestors()
647 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
648 {
649 el.check_ancestors_disabled_state_for_form_control();
650 } else {
651 el.check_disabled_attribute();
652 }
653
654 self.validity_state(can_gc)
655 .perform_validation_and_update(ValidationFlags::all(), can_gc);
656 }
657
658 fn cloning_steps(
661 &self,
662 copy: &Node,
663 maybe_doc: Option<&Document>,
664 clone_children: CloneChildrenFlag,
665 can_gc: CanGc,
666 ) {
667 if let Some(s) = self.super_type() {
668 s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
669 }
670 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
671 el.value_dirty.set(self.value_dirty.get());
672 {
673 let mut textinput = el.textinput.borrow_mut();
674 textinput.set_content(self.textinput.borrow().get_content());
675 }
676 el.validity_state(can_gc)
677 .perform_validation_and_update(ValidationFlags::all(), can_gc);
678 }
679
680 fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
681 if let Some(s) = self.super_type() {
682 s.children_changed(mutation, can_gc);
683 }
684 if !self.value_dirty.get() {
685 self.reset();
686 }
687 }
688
689 fn handle_event(&self, event: &Event, can_gc: CanGc) {
691 if let Some(s) = self.super_type() {
692 s.handle_event(event, can_gc);
693 }
694
695 if event.type_() == atom!("click") && !event.DefaultPrevented() {
696 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
698 if let Some(kevent) = event.downcast::<KeyboardEvent>() {
699 let action = self.textinput.borrow_mut().handle_keydown(kevent);
702 self.handle_key_reaction(action, event);
703 }
704 } else if event.type_() == atom!("keypress") && !event.DefaultPrevented() {
705 } else if event.type_() == atom!("compositionstart") ||
709 event.type_() == atom!("compositionupdate") ||
710 event.type_() == atom!("compositionend")
711 {
712 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
713 if event.type_() == atom!("compositionend") {
714 let action = self
715 .textinput
716 .borrow_mut()
717 .handle_compositionend(compositionevent);
718 self.handle_key_reaction(action, event);
719 self.upcast::<Node>().dirty(NodeDamage::Other);
720 } else if event.type_() == atom!("compositionupdate") {
721 let action = self
722 .textinput
723 .borrow_mut()
724 .handle_compositionupdate(compositionevent);
725 self.handle_key_reaction(action, event);
726 self.upcast::<Node>().dirty(NodeDamage::Other);
727 }
728 event.mark_as_handled();
729 }
730 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
731 let reaction = self
732 .textinput
733 .borrow_mut()
734 .handle_clipboard_event(clipboard_event);
735
736 let flags = reaction.flags;
737 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
738 self.owner_document().event_handler().fire_clipboard_event(
739 None,
740 ClipboardEventType::Change,
741 can_gc,
742 );
743 }
744 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
745 self.textinput.borrow().queue_input_event(
746 self.upcast(),
747 reaction.text,
748 IsComposing::NotComposing,
749 reaction.input_type,
750 );
751 }
752 if !flags.is_empty() {
753 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
754 }
755 } else if let Some(event) = event.downcast::<FocusEvent>() {
756 if *event.upcast::<Event>().type_() == *"blur" {
757 self.owner_document()
758 .embedder_controls()
759 .hide_embedder_control(self.upcast());
760 }
761 if *event.upcast::<Event>().type_() == *"focus" {
762 self.handle_focus();
763 }
764 }
765
766 self.validity_state(can_gc)
767 .perform_validation_and_update(ValidationFlags::all(), can_gc);
768 }
769
770 fn pop(&self) {
771 self.super_type().unwrap().pop();
772
773 self.reset();
775 }
776}
777
778impl FormControl for HTMLTextAreaElement {
779 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
780 self.form_owner.get()
781 }
782
783 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
784 self.form_owner.set(form);
785 }
786
787 fn to_element(&self) -> &Element {
788 self.upcast::<Element>()
789 }
790}
791
792impl Validatable for HTMLTextAreaElement {
793 fn as_element(&self) -> &Element {
794 self.upcast()
795 }
796
797 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
798 self.validity_state
799 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
800 }
801
802 fn is_instance_validatable(&self) -> bool {
803 !self.upcast::<Element>().disabled_state() &&
807 !self.ReadOnly() &&
808 !is_barred_by_datalist_ancestor(self.upcast())
809 }
810
811 fn perform_validation(
812 &self,
813 validate_flags: ValidationFlags,
814 _can_gc: CanGc,
815 ) -> ValidationFlags {
816 let mut failed_flags = ValidationFlags::empty();
817
818 let textinput = self.textinput.borrow();
819 let UTF16CodeUnits(value_len) = textinput.utf16_len();
820 let last_edit_by_user = !textinput.was_last_change_by_set_content();
821 let value_dirty = self.value_dirty.get();
822
823 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
826 self.Required() &&
827 self.is_mutable() &&
828 value_len == 0
829 {
830 failed_flags.insert(ValidationFlags::VALUE_MISSING);
831 }
832
833 if value_dirty && last_edit_by_user && value_len > 0 {
834 if validate_flags.contains(ValidationFlags::TOO_LONG) {
837 let max_length = self.MaxLength();
838 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
839 failed_flags.insert(ValidationFlags::TOO_LONG);
840 }
841 }
842
843 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
846 let min_length = self.MinLength();
847 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
848 failed_flags.insert(ValidationFlags::TOO_SHORT);
849 }
850 }
851 }
852
853 failed_flags
854 }
855}