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_event(&self, event: &FocusEvent) {
216 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
220
221 let event_type = event.upcast::<Event>().type_();
222 if *event_type == *"blur" {
223 self.owner_document()
224 .embedder_controls()
225 .hide_embedder_control(self.upcast());
226 } else if *event_type == *"focus" {
227 self.owner_document()
228 .embedder_controls()
229 .show_embedder_control(
230 ControlElement::Ime(DomRoot::from_ref(self.upcast())),
231 EmbedderControlRequest::InputMethod(InputMethodRequest {
232 input_method_type: InputMethodType::Text,
233 text: self.Value().to_string(),
234 insertion_point: self.GetSelectionEnd(),
235 multiline: false,
236 }),
237 None,
238 );
239 } else {
240 unreachable!("Got unexpected FocusEvent {event_type:?}");
241 }
242 }
243}
244
245impl TextControlElement for HTMLTextAreaElement {
246 fn selection_api_applies(&self) -> bool {
247 true
248 }
249
250 fn has_selectable_text(&self) -> bool {
251 !self.textinput.borrow().get_content().is_empty()
252 }
253
254 fn has_selection(&self) -> bool {
255 self.textinput.borrow().has_selection()
256 }
257
258 fn set_dirty_value_flag(&self, value: bool) {
259 self.value_dirty.set(value)
260 }
261
262 fn select_all(&self) {
263 self.textinput.borrow_mut().select_all();
264 self.upcast::<Node>().dirty(NodeDamage::Other);
265 }
266}
267
268impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
269 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
274
275 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
277
278 make_getter!(DirName, "dirname");
280
281 make_setter!(SetDirName, "dirname");
283
284 make_bool_getter!(Disabled, "disabled");
286
287 make_bool_setter!(SetDisabled, "disabled");
289
290 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
292 self.form_owner()
293 }
294
295 make_getter!(Name, "name");
297
298 make_atomic_setter!(SetName, "name");
300
301 make_getter!(Placeholder, "placeholder");
303
304 make_setter!(SetPlaceholder, "placeholder");
306
307 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
309
310 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
312
313 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
315
316 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
318
319 make_bool_getter!(ReadOnly, "readonly");
321
322 make_bool_setter!(SetReadOnly, "readonly");
324
325 make_bool_getter!(Required, "required");
327
328 make_bool_setter!(SetRequired, "required");
330
331 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
333
334 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
336
337 make_getter!(Wrap, "wrap");
339
340 make_setter!(SetWrap, "wrap");
342
343 fn Type(&self) -> DOMString {
345 DOMString::from("textarea")
346 }
347
348 fn DefaultValue(&self) -> DOMString {
350 self.upcast::<Node>().GetTextContent().unwrap()
351 }
352
353 fn SetDefaultValue(&self, value: DOMString, can_gc: CanGc) {
355 self.upcast::<Node>()
356 .set_text_content_for_element(Some(value), can_gc);
357
358 if !self.value_dirty.get() {
361 self.reset();
362 }
363 }
364
365 fn Value(&self) -> DOMString {
367 self.textinput.borrow().get_content()
368 }
369
370 fn SetValue(&self, value: DOMString, can_gc: CanGc) {
372 {
373 let mut textinput = self.textinput.borrow_mut();
374
375 let old_value = textinput.get_content();
377
378 textinput.set_content(value);
380
381 self.value_dirty.set(true);
383
384 if old_value != textinput.get_content() {
385 textinput.clear_selection_to_limit(Direction::Forward);
387 }
388 }
389
390 self.validity_state(can_gc)
391 .perform_validation_and_update(ValidationFlags::all(), can_gc);
392 self.upcast::<Node>().dirty(NodeDamage::Other);
393 }
394
395 fn TextLength(&self) -> u32 {
397 let UTF16CodeUnits(num_units) = self.textinput.borrow().utf16_len();
398 num_units as u32
399 }
400
401 make_labels_getter!(Labels, labels_node_list);
403
404 fn Select(&self) {
406 self.selection().dom_select();
407 }
408
409 fn GetSelectionStart(&self) -> Option<u32> {
411 self.selection().dom_start()
412 }
413
414 fn SetSelectionStart(&self, start: Option<u32>) -> ErrorResult {
416 self.selection().set_dom_start(start)
417 }
418
419 fn GetSelectionEnd(&self) -> Option<u32> {
421 self.selection().dom_end()
422 }
423
424 fn SetSelectionEnd(&self, end: Option<u32>) -> ErrorResult {
426 self.selection().set_dom_end(end)
427 }
428
429 fn GetSelectionDirection(&self) -> Option<DOMString> {
431 self.selection().dom_direction()
432 }
433
434 fn SetSelectionDirection(&self, direction: Option<DOMString>) -> ErrorResult {
436 self.selection().set_dom_direction(direction)
437 }
438
439 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
441 self.selection().set_dom_range(start, end, direction)
442 }
443
444 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
446 self.selection()
447 .set_dom_range_text(replacement, None, None, Default::default())
448 }
449
450 fn SetRangeText_(
452 &self,
453 replacement: DOMString,
454 start: u32,
455 end: u32,
456 selection_mode: SelectionMode,
457 ) -> ErrorResult {
458 self.selection()
459 .set_dom_range_text(replacement, Some(start), Some(end), selection_mode)
460 }
461
462 fn WillValidate(&self) -> bool {
464 self.is_instance_validatable()
465 }
466
467 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
469 self.validity_state(can_gc)
470 }
471
472 fn CheckValidity(&self, can_gc: CanGc) -> bool {
474 self.check_validity(can_gc)
475 }
476
477 fn ReportValidity(&self, can_gc: CanGc) -> bool {
479 self.report_validity(can_gc)
480 }
481
482 fn ValidationMessage(&self) -> DOMString {
484 self.validation_message()
485 }
486
487 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
489 self.validity_state(can_gc).set_custom_error_message(error);
490 }
491}
492
493impl HTMLTextAreaElement {
494 pub(crate) fn clear(&self) {
497 self.value_dirty.set(false);
498 self.textinput.borrow_mut().set_content(DOMString::from(""));
499 }
500
501 pub(crate) fn reset(&self) {
502 let mut textinput = self.textinput.borrow_mut();
504 textinput.set_content(self.DefaultValue());
505 self.value_dirty.set(false);
506 }
507
508 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
509 fn selection(&self) -> TextControlSelection<'_, Self> {
510 TextControlSelection::new(self, &self.textinput)
511 }
512
513 fn handle_key_reaction(&self, action: KeyReaction, event: &Event) {
514 match action {
515 KeyReaction::TriggerDefaultAction => (),
516 KeyReaction::DispatchInput(text, is_composing, input_type) => {
517 if event.IsTrusted() {
518 self.textinput.borrow().queue_input_event(
519 self.upcast(),
520 text,
521 is_composing,
522 input_type,
523 );
524 }
525 self.value_dirty.set(true);
526 self.update_placeholder_shown_state();
527 self.upcast::<Node>().dirty(NodeDamage::Other);
528 event.mark_as_handled();
529 },
530 KeyReaction::RedrawSelection => {
531 self.upcast::<Node>().dirty(NodeDamage::Other);
532 event.mark_as_handled();
533 },
534 KeyReaction::Nothing => (),
535 }
536 }
537}
538
539impl VirtualMethods for HTMLTextAreaElement {
540 fn super_type(&self) -> Option<&dyn VirtualMethods> {
541 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
542 }
543
544 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
545 self.super_type()
546 .unwrap()
547 .attribute_mutated(attr, mutation, can_gc);
548 match *attr.local_name() {
549 local_name!("disabled") => {
550 let el = self.upcast::<Element>();
551 match mutation {
552 AttributeMutation::Set(..) => {
553 el.set_disabled_state(true);
554 el.set_enabled_state(false);
555
556 el.set_read_write_state(false);
557 },
558 AttributeMutation::Removed => {
559 el.set_disabled_state(false);
560 el.set_enabled_state(true);
561 el.check_ancestors_disabled_state_for_form_control();
562
563 if !el.disabled_state() && !el.read_write_state() {
564 el.set_read_write_state(true);
565 }
566 },
567 }
568 el.update_sequentially_focusable_status(CanGc::note());
569 },
570 local_name!("maxlength") => match *attr.value() {
571 AttrValue::Int(_, value) => {
572 let mut textinput = self.textinput.borrow_mut();
573
574 if value < 0 {
575 textinput.set_max_length(None);
576 } else {
577 textinput.set_max_length(Some(UTF16CodeUnits(value as usize)))
578 }
579 },
580 _ => panic!("Expected an AttrValue::Int"),
581 },
582 local_name!("minlength") => match *attr.value() {
583 AttrValue::Int(_, value) => {
584 let mut textinput = self.textinput.borrow_mut();
585
586 if value < 0 {
587 textinput.set_min_length(None);
588 } else {
589 textinput.set_min_length(Some(UTF16CodeUnits(value as usize)))
590 }
591 },
592 _ => panic!("Expected an AttrValue::Int"),
593 },
594 local_name!("placeholder") => {
595 {
596 let mut placeholder = self.placeholder.borrow_mut();
597 placeholder.clear();
598 if let AttributeMutation::Set(..) = mutation {
599 placeholder.push_str(attr.value().as_ref());
600 }
601 }
602 self.update_placeholder_shown_state();
603 },
604 local_name!("readonly") => {
605 let el = self.upcast::<Element>();
606 match mutation {
607 AttributeMutation::Set(..) => {
608 el.set_read_write_state(false);
609 },
610 AttributeMutation::Removed => {
611 el.set_read_write_state(!el.disabled_state());
612 },
613 }
614 },
615 local_name!("form") => {
616 self.form_attribute_mutated(mutation, can_gc);
617 },
618 _ => {},
619 }
620
621 self.validity_state(can_gc)
622 .perform_validation_and_update(ValidationFlags::all(), can_gc);
623 }
624
625 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
626 if let Some(s) = self.super_type() {
627 s.bind_to_tree(context, can_gc);
628 }
629
630 self.upcast::<Element>()
631 .check_ancestors_disabled_state_for_form_control();
632
633 self.validity_state(can_gc)
634 .perform_validation_and_update(ValidationFlags::all(), can_gc);
635 }
636
637 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
638 match *name {
639 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
640 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
641 local_name!("maxlength") => {
642 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
643 },
644 local_name!("minlength") => {
645 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
646 },
647 _ => self
648 .super_type()
649 .unwrap()
650 .parse_plain_attribute(name, value),
651 }
652 }
653
654 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
655 self.super_type().unwrap().unbind_from_tree(context, can_gc);
656
657 let node = self.upcast::<Node>();
658 let el = self.upcast::<Element>();
659 if node
660 .ancestors()
661 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
662 {
663 el.check_ancestors_disabled_state_for_form_control();
664 } else {
665 el.check_disabled_attribute();
666 }
667
668 self.validity_state(can_gc)
669 .perform_validation_and_update(ValidationFlags::all(), can_gc);
670 }
671
672 fn cloning_steps(
675 &self,
676 copy: &Node,
677 maybe_doc: Option<&Document>,
678 clone_children: CloneChildrenFlag,
679 can_gc: CanGc,
680 ) {
681 if let Some(s) = self.super_type() {
682 s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
683 }
684 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
685 el.value_dirty.set(self.value_dirty.get());
686 {
687 let mut textinput = el.textinput.borrow_mut();
688 textinput.set_content(self.textinput.borrow().get_content());
689 }
690 el.validity_state(can_gc)
691 .perform_validation_and_update(ValidationFlags::all(), can_gc);
692 }
693
694 fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
695 if let Some(s) = self.super_type() {
696 s.children_changed(mutation, can_gc);
697 }
698 if !self.value_dirty.get() {
699 self.reset();
700 }
701 }
702
703 fn handle_event(&self, event: &Event, can_gc: CanGc) {
705 if let Some(s) = self.super_type() {
706 s.handle_event(event, can_gc);
707 }
708
709 if event.type_() == atom!("click") && !event.DefaultPrevented() {
710 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
712 if let Some(kevent) = event.downcast::<KeyboardEvent>() {
713 let action = self.textinput.borrow_mut().handle_keydown(kevent);
716 self.handle_key_reaction(action, event);
717 }
718 } else if event.type_() == atom!("keypress") && !event.DefaultPrevented() {
719 } else if event.type_() == atom!("compositionstart") ||
723 event.type_() == atom!("compositionupdate") ||
724 event.type_() == atom!("compositionend")
725 {
726 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
727 if event.type_() == atom!("compositionend") {
728 let action = self
729 .textinput
730 .borrow_mut()
731 .handle_compositionend(compositionevent);
732 self.handle_key_reaction(action, event);
733 self.upcast::<Node>().dirty(NodeDamage::Other);
734 } else if event.type_() == atom!("compositionupdate") {
735 let action = self
736 .textinput
737 .borrow_mut()
738 .handle_compositionupdate(compositionevent);
739 self.handle_key_reaction(action, event);
740 self.upcast::<Node>().dirty(NodeDamage::Other);
741 }
742 event.mark_as_handled();
743 }
744 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
745 let reaction = self
746 .textinput
747 .borrow_mut()
748 .handle_clipboard_event(clipboard_event);
749
750 let flags = reaction.flags;
751 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
752 self.owner_document().event_handler().fire_clipboard_event(
753 None,
754 ClipboardEventType::Change,
755 can_gc,
756 );
757 }
758 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
759 self.textinput.borrow().queue_input_event(
760 self.upcast(),
761 reaction.text,
762 IsComposing::NotComposing,
763 reaction.input_type,
764 );
765 }
766 if !flags.is_empty() {
767 self.upcast::<Node>().dirty(NodeDamage::ContentOrHeritage);
768 }
769 } else if let Some(event) = event.downcast::<FocusEvent>() {
770 self.handle_focus_event(event);
771 }
772
773 self.validity_state(can_gc)
774 .perform_validation_and_update(ValidationFlags::all(), can_gc);
775 }
776
777 fn pop(&self) {
778 self.super_type().unwrap().pop();
779
780 self.reset();
782 }
783}
784
785impl FormControl for HTMLTextAreaElement {
786 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
787 self.form_owner.get()
788 }
789
790 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
791 self.form_owner.set(form);
792 }
793
794 fn to_element(&self) -> &Element {
795 self.upcast::<Element>()
796 }
797}
798
799impl Validatable for HTMLTextAreaElement {
800 fn as_element(&self) -> &Element {
801 self.upcast()
802 }
803
804 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
805 self.validity_state
806 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
807 }
808
809 fn is_instance_validatable(&self) -> bool {
810 !self.upcast::<Element>().disabled_state() &&
814 !self.ReadOnly() &&
815 !is_barred_by_datalist_ancestor(self.upcast())
816 }
817
818 fn perform_validation(
819 &self,
820 validate_flags: ValidationFlags,
821 _can_gc: CanGc,
822 ) -> ValidationFlags {
823 let mut failed_flags = ValidationFlags::empty();
824
825 let textinput = self.textinput.borrow();
826 let UTF16CodeUnits(value_len) = textinput.utf16_len();
827 let last_edit_by_user = !textinput.was_last_change_by_set_content();
828 let value_dirty = self.value_dirty.get();
829
830 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
833 self.Required() &&
834 self.is_mutable() &&
835 value_len == 0
836 {
837 failed_flags.insert(ValidationFlags::VALUE_MISSING);
838 }
839
840 if value_dirty && last_edit_by_user && value_len > 0 {
841 if validate_flags.contains(ValidationFlags::TOO_LONG) {
844 let max_length = self.MaxLength();
845 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
846 failed_flags.insert(ValidationFlags::TOO_LONG);
847 }
848 }
849
850 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
853 let min_length = self.MinLength();
854 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
855 failed_flags.insert(ValidationFlags::TOO_SHORT);
856 }
857 }
858 }
859
860 failed_flags
861 }
862}