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 script_bindings::domstring::TracedDOMString;
16use servo_base::text::{RangeAny, Utf16CodeUnits, Utf32CodeUnits};
17use style::attr::AttrValue;
18use stylo_dom::ElementState;
19
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::refcounted::Trusted;
26use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
27use crate::dom::bindings::str::DOMString;
28use crate::dom::document::Document;
29use crate::dom::document_embedder_controls::ControlElement;
30use crate::dom::element::attributes::storage::AttrRef;
31use crate::dom::element::{AttributeMutation, Element};
32use crate::dom::event::Event;
33use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
34use crate::dom::eventtarget::EventTarget;
35use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
36use crate::dom::html::form_controls::input_type::text_input_widget::TextInputWidget;
37use crate::dom::html::form_controls::text_control::TextControlElement;
38use crate::dom::html::form_controls::text_input::{KeyReaction, Lines, TextInput};
39use crate::dom::html::htmlelement::HTMLElement;
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<TracedDOMString>,
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 cx,
182 ControlElement::Ime(Dom::from_ref(self.upcast())),
183 EmbedderControlRequest::InputMethod(InputMethodRequest {
184 input_method_type: InputMethodType::Text,
185 text: String::from(self.Value()),
186 insertion_point: self.GetSelectionEnd(),
187 multiline: false,
188 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
190 }),
191 None,
192 );
193 }
194
195 self.maybe_update_shared_selection();
197 }
198
199 fn schedule_a_selection_change_event(&self) {
201 if self.has_scheduled_selectionchange_event.get() {
203 return;
204 }
205 self.has_scheduled_selectionchange_event.set(true);
207 let this = Trusted::new(self);
209 self.owner_global()
210 .task_manager()
211 .user_interaction_task_source()
212 .queue(
213 task!(selectionchange_task_steps: move |cx| {
215 let this = this.root();
216 this.has_scheduled_selectionchange_event.set(false);
218 this.upcast::<EventTarget>().fire_event_with_params(
220 cx,
221 atom!("selectionchange"),
222 EventBubbles::Bubbles,
223 EventCancelable::NotCancelable,
224 EventComposed::Composed,
225 );
226 }),
231 );
232 }
233}
234
235impl TextControlElement for HTMLTextAreaElement {
236 fn as_element(&self) -> &Element {
237 self.upcast()
238 }
239
240 fn text_input(&self) -> Ref<'_, TextInput<EmbedderClipboardProvider>> {
241 self.text_input.borrow()
242 }
243
244 fn text_input_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
245 self.text_input.borrow_mut()
246 }
247
248 fn selection_api_applies(&self) -> bool {
249 true
250 }
251
252 fn has_selectable_text(&self) -> bool {
253 !self.text_input.borrow().get_content().is_empty()
254 }
255
256 fn has_uncollapsed_selection(&self) -> bool {
257 self.text_input.borrow().has_uncollapsed_selection()
258 }
259
260 fn set_dirty_value_flag(&self, value: bool) {
261 self.value_dirty.set(value)
262 }
263
264 fn select_all(&self) {
265 self.text_input.borrow_mut().select_all();
266 self.maybe_update_shared_selection();
267 }
268
269 fn maybe_update_shared_selection(&self) {
270 let selection = {
271 let mut text_input = self.text_input.borrow_mut();
272 let selection_range = text_input.selection_start()..text_input.selection_end();
273 let enabled = self.upcast::<Element>().focus_state();
274
275 let range_remained_equal = selection_range == text_input.previous_selection_range;
276 if range_remained_equal && enabled == text_input.selection_for_layout.is_some() {
277 return;
278 }
279
280 if !range_remained_equal {
281 self.schedule_a_selection_change_event();
286 }
287
288 let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range());
289 text_input.previous_selection_range = selection_range;
290 text_input.selection_for_layout = selection;
291 selection
292 };
293
294 if self
295 .text_input_widget
296 .borrow()
297 .set_text_run_selection(selection)
298 {
299 self.owner_window().layout().set_needs_new_display_list();
301 } else {
302 }
304 }
305
306 fn placeholder_text<'a>(&'a self) -> Ref<'a, TracedDOMString> {
307 self.placeholder.borrow()
308 }
309
310 fn value_text(&self) -> DOMString {
311 self.Value()
312 }
313
314 fn read_only_or_disabled(&self) -> bool {
315 self.ReadOnly() || self.Disabled()
316 }
317
318 fn handle_text_content_changed(&self, cx: &mut JSContext) {
319 self.validity_state(cx)
320 .perform_validation_and_update(cx, ValidationFlags::all());
321
322 let placeholder_shown =
323 self.text_input.borrow().is_empty() && !self.placeholder.borrow().is_empty();
324 self.upcast::<Element>()
325 .set_placeholder_shown_state(cx.no_gc(), placeholder_shown);
326
327 self.text_input_widget.borrow().update_shadow_tree(cx, self);
328 self.text_input_widget
329 .borrow()
330 .update_placeholder_contents(cx, self);
331 self.maybe_update_shared_selection();
332 }
333
334 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction) {
335 match action {
336 KeyReaction::TriggerDefaultAction => (),
337 KeyReaction::DispatchInput(text, input_type) => {
338 self.queue_input_event(text, input_type);
339 self.value_dirty.set(true);
340 self.handle_text_content_changed(cx);
341 self.maybe_update_shared_selection();
342 },
343 KeyReaction::RedrawSelection => {
344 self.maybe_update_shared_selection();
345 },
346 KeyReaction::Nothing => (),
347 }
348 }
349}
350
351impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
352 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
357
358 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
360
361 make_getter!(DirName, "dirname");
363
364 make_setter!(SetDirName, "dirname");
366
367 make_bool_getter!(Disabled, "disabled");
369
370 make_bool_setter!(SetDisabled, "disabled");
372
373 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
375 self.form_owner()
376 }
377
378 make_getter!(Name, "name");
380
381 make_atomic_setter!(SetName, "name");
383
384 make_getter!(Placeholder, "placeholder");
386
387 make_setter!(SetPlaceholder, "placeholder");
389
390 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
392
393 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
395
396 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
398
399 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
401
402 make_bool_getter!(ReadOnly, "readonly");
404
405 make_bool_setter!(SetReadOnly, "readonly");
407
408 make_bool_getter!(Required, "required");
410
411 make_bool_setter!(SetRequired, "required");
413
414 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
416
417 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
419
420 make_getter!(Wrap, "wrap");
422
423 make_setter!(SetWrap, "wrap");
425
426 fn Type(&self) -> DOMString {
428 DOMString::from_static("textarea")
429 }
430
431 fn DefaultValue(&self) -> DOMString {
433 self.upcast::<Node>().GetTextContent().unwrap()
434 }
435
436 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
438 self.upcast::<Node>()
439 .set_text_content_for_element(cx, Some(value));
440
441 if !self.value_dirty.get() {
444 self.reset(cx);
445 }
446 }
447
448 fn Value(&self) -> DOMString {
450 self.text_input.borrow().get_content()
451 }
452
453 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
455 let old_api_value = self.Value();
457
458 self.text_input
460 .safe_borrow_mut(cx.no_gc())
461 .set_content(value);
462
463 self.value_dirty.set(true);
465
466 if old_api_value != self.Value() {
471 self.text_input
472 .safe_borrow_mut(cx.no_gc())
473 .clear_selection_to_end();
474 self.handle_text_content_changed(cx);
475 }
476 }
477
478 fn TextLength(&self) -> u32 {
480 self.text_input.borrow().len_utf16().0
481 }
482
483 make_labels_getter!(Labels, labels_node_list);
485
486 fn Select(&self, cx: &mut JSContext) {
488 self.dom_select(cx);
489 }
490
491 fn GetSelectionStart(&self) -> Option<u32> {
493 self.dom_start().map(|start| start.0)
494 }
495
496 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
498 self.set_dom_start(start.map(Utf16CodeUnits::from))
499 }
500
501 fn GetSelectionEnd(&self) -> Option<u32> {
503 self.dom_end().map(|end| end.0)
504 }
505
506 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
508 self.set_dom_end(end.map(Utf16CodeUnits::from))
509 }
510
511 fn GetSelectionDirection(&self) -> Option<DOMString> {
513 self.dom_direction()
514 }
515
516 fn SetSelectionDirection(
518 &self,
519 _cx: &mut JSContext,
520 direction: Option<DOMString>,
521 ) -> ErrorResult {
522 self.set_dom_direction(direction)
523 }
524
525 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
527 self.set_dom_range(
528 Utf16CodeUnits::from(start),
529 Utf16CodeUnits::from(end),
530 direction,
531 )
532 }
533
534 fn SetRangeText(&self, cx: &mut JSContext, replacement: DOMString) -> ErrorResult {
536 self.set_dom_range_text(cx, replacement, None, None, Default::default())
537 }
538
539 fn SetRangeText_(
541 &self,
542 cx: &mut JSContext,
543 replacement: DOMString,
544 start: u32,
545 end: u32,
546 selection_mode: SelectionMode,
547 ) -> ErrorResult {
548 self.set_dom_range_text(
549 cx,
550 replacement,
551 Some(Utf16CodeUnits::from(start)),
552 Some(Utf16CodeUnits::from(end)),
553 selection_mode,
554 )
555 }
556
557 fn WillValidate(&self, no_gc: &NoGC) -> bool {
559 self.is_instance_validatable(no_gc)
560 }
561
562 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
564 self.validity_state(cx)
565 }
566
567 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
569 self.check_validity(cx)
570 }
571
572 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
574 self.report_validity(cx)
575 }
576
577 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
579 self.validation_message(cx)
580 }
581
582 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
584 self.validity_state(cx).set_custom_error_message(cx, error);
585 }
586}
587
588impl HTMLTextAreaElement {
589 pub(crate) fn clear(&self) {
592 self.value_dirty.set(false);
593 self.text_input.borrow_mut().set_content(DOMString::new());
594 }
595
596 pub(crate) fn reset(&self, cx: &mut JSContext) {
597 self.value_dirty.set(false);
599 self.text_input
600 .safe_borrow_mut(cx.no_gc())
601 .set_content(self.DefaultValue());
602 self.handle_text_content_changed(cx);
603 }
604}
605
606impl VirtualMethods for HTMLTextAreaElement {
607 fn super_type(&self) -> Option<&dyn VirtualMethods> {
608 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
609 }
610
611 fn attribute_mutated(
612 &self,
613 cx: &mut JSContext,
614 attr: AttrRef<'_>,
615 mutation: AttributeMutation,
616 ) {
617 self.super_type()
618 .unwrap()
619 .attribute_mutated(cx, attr, mutation);
620 match *attr.local_name() {
621 local_name!("disabled") => {
622 let el = self.upcast::<Element>();
623 match mutation {
624 AttributeMutation::Set(_) => {
625 el.set_read_write_state(cx.no_gc(), false);
626 },
627 AttributeMutation::Removed => {
628 if !el.disabled_state() && !el.read_write_state() {
629 el.set_read_write_state(cx.no_gc(), true);
630 }
631 },
632 }
633 },
634 local_name!("maxlength") => match *attr.value() {
635 AttrValue::Int(_, value) => {
636 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
637
638 if value < 0 {
639 text_input.set_max_length(None);
640 } else {
641 text_input.set_max_length(Some(Utf16CodeUnits(value as u32)))
642 }
643 },
644 _ => panic!("Expected an AttrValue::Int"),
645 },
646 local_name!("minlength") => match *attr.value() {
647 AttrValue::Int(_, value) => {
648 let mut text_input = self.text_input.safe_borrow_mut(cx.no_gc());
649
650 if value < 0 {
651 text_input.set_min_length(None);
652 } else {
653 text_input.set_min_length(Some(Utf16CodeUnits(value as u32)))
654 }
655 },
656 _ => panic!("Expected an AttrValue::Int"),
657 },
658 local_name!("placeholder") => {
659 {
660 let mut placeholder = self.placeholder.borrow_mut();
661 match mutation {
662 AttributeMutation::Set(_) => {
663 let value = attr.value();
664 let value_str: &str = value.as_ref();
665 *placeholder =
666 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
667 },
668 AttributeMutation::Removed => placeholder.clear(),
669 }
670 }
671 self.handle_text_content_changed(cx);
672 },
673 local_name!("readonly") => {
674 let el = self.upcast::<Element>();
675 match mutation {
676 AttributeMutation::Set(_) => {
677 el.set_read_write_state(cx.no_gc(), false);
678 },
679 AttributeMutation::Removed => {
680 el.set_read_write_state(cx.no_gc(), !el.disabled_state());
681 },
682 }
683 },
684 local_name!("form") => {
685 self.form_attribute_mutated(cx, mutation);
686 },
687 _ => {},
688 }
689
690 self.validity_state(cx)
691 .perform_validation_and_update(cx, ValidationFlags::all());
692 }
693
694 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
695 if let Some(s) = self.super_type() {
696 s.bind_to_tree(cx, context);
697 }
698 self.handle_text_content_changed(cx);
699 }
700
701 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
702 match *name {
703 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
704 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
705 local_name!("maxlength") => {
706 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
707 },
708 local_name!("minlength") => {
709 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
710 },
711 _ => self
712 .super_type()
713 .unwrap()
714 .parse_plain_attribute(name, value),
715 }
716 }
717
718 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
719 self.owner_document()
721 .embedder_controls()
722 .hide_embedder_control(self.upcast());
723
724 self.super_type().unwrap().unbind_from_tree(cx, context);
725 self.validity_state(cx)
726 .perform_validation_and_update(cx, ValidationFlags::all());
727 }
728
729 fn cloning_steps(
732 &self,
733 cx: &mut JSContext,
734 copy: &Node,
735 maybe_doc: Option<&Document>,
736 clone_children: CloneChildrenFlag,
737 ) {
738 if let Some(s) = self.super_type() {
739 s.cloning_steps(cx, copy, maybe_doc, clone_children);
740 }
741 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
742 el.value_dirty.set(self.value_dirty.get());
743 {
744 let mut text_input = el.text_input.safe_borrow_mut(cx.no_gc());
745 text_input.set_content(self.text_input.borrow().get_content());
746 }
747 el.validity_state(cx)
748 .perform_validation_and_update(cx, ValidationFlags::all());
749 }
750
751 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
752 if let Some(s) = self.super_type() {
753 s.children_changed(cx, mutation);
754 }
755 if !self.value_dirty.get() {
756 self.reset(cx);
757 }
758 }
759
760 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
762 if let Some(event) = event.downcast::<FocusEvent>() {
763 self.handle_focus_event(cx, event);
764 }
765
766 self.validity_state(cx)
767 .perform_validation_and_update(cx, ValidationFlags::all());
768
769 if let Some(super_type) = self.super_type() {
770 super_type.handle_event(cx, event);
771 }
772 }
773
774 fn pop(&self, cx: &mut JSContext) {
775 self.super_type().unwrap().pop(cx);
776
777 self.reset(cx);
779 }
780}
781
782impl FormControl for HTMLTextAreaElement {
783 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
784 self.form_owner.get()
785 }
786
787 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
788 self.form_owner.set(form);
789 }
790
791 fn to_html_element(&self) -> &HTMLElement {
792 self.upcast::<HTMLElement>()
793 }
794}
795
796impl Validatable for HTMLTextAreaElement {
797 fn as_element(&self) -> &Element {
798 self.upcast()
799 }
800
801 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
802 self.validity_state
803 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
804 }
805
806 fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
807 !self.upcast::<Element>().disabled_state() &&
811 !self.ReadOnly() &&
812 !is_barred_by_datalist_ancestor(no_gc, self.upcast())
813 }
814
815 fn perform_validation(
816 &self,
817 _cx: &mut JSContext,
818 validate_flags: ValidationFlags,
819 ) -> ValidationFlags {
820 let mut failed_flags = ValidationFlags::empty();
821
822 let text_input = self.text_input.borrow();
823 let Utf16CodeUnits(value_len) = text_input.len_utf16();
824 let last_edit_by_user = !text_input.was_last_change_by_set_content();
825 let value_dirty = self.value_dirty.get();
826
827 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
830 self.Required() &&
831 self.is_mutable() &&
832 value_len == 0
833 {
834 failed_flags.insert(ValidationFlags::VALUE_MISSING);
835 }
836
837 if value_dirty && last_edit_by_user && value_len > 0 {
838 if validate_flags.contains(ValidationFlags::TOO_LONG) {
841 let max_length = self.MaxLength();
842 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as u32) {
843 failed_flags.insert(ValidationFlags::TOO_LONG);
844 }
845 }
846
847 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
850 let min_length = self.MinLength();
851 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as u32) {
852 failed_flags.insert(ValidationFlags::TOO_SHORT);
853 }
854 }
855 }
856
857 failed_flags
858 }
859}