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;
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::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::refcounted::Trusted;
26use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
27use crate::dom::bindings::str::DOMString;
28use crate::dom::compositionevent::CompositionEvent;
29use crate::dom::document::Document;
30use crate::dom::document_embedder_controls::ControlElement;
31use crate::dom::element::attributes::storage::AttrRef;
32use crate::dom::element::{AttributeMutation, Element};
33use crate::dom::event::Event;
34use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
35use crate::dom::eventtarget::EventTarget;
36use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
37use crate::dom::html::form_controls::input_type::text_input_widget::TextInputWidget;
38use crate::dom::html::form_controls::text_control::TextControlElement;
39use crate::dom::html::form_controls::text_input::{KeyReaction, Lines, TextInput};
40use crate::dom::html::htmlelement::HTMLElement;
41use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
42use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
43use crate::dom::inputevent::HitTestResult;
44use crate::dom::keyboardevent::KeyboardEvent;
45use crate::dom::node::virtualmethods::VirtualMethods;
46use crate::dom::node::{
47 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
48};
49use crate::dom::nodelist::NodeList;
50use crate::dom::text_input::EmbedderClipboardProvider;
51use crate::dom::types::{FocusEvent, MouseEvent};
52use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
53use crate::dom::validitystate::{ValidationFlags, ValidityState};
54
55#[dom_struct]
56pub(crate) struct HTMLTextAreaElement {
57 htmlelement: HTMLElement,
58 #[no_trace]
59 text_input: DomRefCell<TextInput<EmbedderClipboardProvider>>,
60 placeholder: RefCell<DOMString>,
61 value_dirty: Cell<bool>,
63 form_owner: MutNullableDom<HTMLFormElement>,
64 labels_node_list: MutNullableDom<NodeList>,
65 validity_state: MutNullableDom<ValidityState>,
66 text_input_widget: DomRefCell<TextInputWidget>,
68
69 has_scheduled_selectionchange_event: Cell<bool>,
71}
72
73impl LayoutDom<'_, HTMLTextAreaElement> {
74 pub(crate) fn selection_for_layout(self) -> Option<RangeAny<Utf32CodeUnits>> {
75 let element = self.unsafe_get();
76 #[expect(unsafe_code)]
77 let text_input = unsafe { element.text_input.borrow_for_layout() };
78 text_input.selection_for_layout
79 }
80
81 pub(crate) fn get_cols(self) -> u32 {
82 self.upcast::<Element>()
83 .get_attr_for_layout(&ns!(), &local_name!("cols"))
84 .map_or(DEFAULT_COLS, AttrValue::as_uint)
85 }
86
87 pub(crate) fn get_rows(self) -> u32 {
88 self.upcast::<Element>()
89 .get_attr_for_layout(&ns!(), &local_name!("rows"))
90 .map_or(DEFAULT_ROWS, AttrValue::as_uint)
91 }
92}
93
94const DEFAULT_COLS: u32 = 20;
96
97const DEFAULT_ROWS: u32 = 2;
99
100const DEFAULT_MAX_LENGTH: i32 = -1;
101const DEFAULT_MIN_LENGTH: i32 = -1;
102
103impl HTMLTextAreaElement {
104 fn new_inherited(
105 local_name: LocalName,
106 prefix: Option<Prefix>,
107 document: &Document,
108 ) -> HTMLTextAreaElement {
109 let embedder_sender = document
110 .window()
111 .as_global_scope()
112 .script_to_embedder_chan()
113 .clone();
114 HTMLTextAreaElement {
115 htmlelement: HTMLElement::new_inherited_with_state(
116 ElementState::ENABLED | ElementState::READWRITE,
117 local_name,
118 prefix,
119 document,
120 ),
121 placeholder: Default::default(),
122 text_input: DomRefCell::new(TextInput::new(
123 Lines::Multiple,
124 DOMString::new(),
125 EmbedderClipboardProvider {
126 embedder_sender,
127 webview_id: document.webview_id(),
128 },
129 )),
130 value_dirty: Cell::new(false),
131 form_owner: Default::default(),
132 labels_node_list: Default::default(),
133 validity_state: Default::default(),
134 text_input_widget: Default::default(),
135 has_scheduled_selectionchange_event: Default::default(),
136 }
137 }
138
139 pub(crate) fn new(
140 cx: &mut JSContext,
141 local_name: LocalName,
142 prefix: Option<Prefix>,
143 document: &Document,
144 proto: Option<HandleObject>,
145 ) -> DomRoot<HTMLTextAreaElement> {
146 Node::reflect_node_with_proto(
147 cx,
148 Box::new(HTMLTextAreaElement::new_inherited(
149 local_name, prefix, document,
150 )),
151 document,
152 proto,
153 )
154 }
155
156 pub(crate) fn auto_directionality(&self) -> String {
157 let value: String = String::from(self.Value());
158 HTMLInputElement::directionality_from_value(&value)
159 }
160
161 pub(crate) fn is_mutable(&self) -> bool {
163 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
166 }
167
168 fn handle_focus_event(&self, cx: &mut JSContext, event: &FocusEvent) {
169 let event_type = event.upcast::<Event>().type_();
170 let document = self.owner_document();
171 if *event_type == *"blur" {
172 document
173 .embedder_controls()
174 .hide_embedder_control(self.upcast());
175 } else if *event_type == *"focus" {
176 if let Some(selection) = document.selection() {
181 let _ = selection.Collapse(cx, None, 0);
182 }
183
184 document.embedder_controls().show_embedder_control(
185 ControlElement::Ime(Dom::from_ref(self.upcast())),
186 EmbedderControlRequest::InputMethod(InputMethodRequest {
187 input_method_type: InputMethodType::Text,
188 text: String::from(self.Value()),
189 insertion_point: self.GetSelectionEnd(),
190 multiline: false,
191 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
193 }),
194 None,
195 );
196 }
197
198 self.maybe_update_shared_selection();
200 }
201
202 fn schedule_a_selection_change_event(&self) {
204 if self.has_scheduled_selectionchange_event.get() {
206 return;
207 }
208 self.has_scheduled_selectionchange_event.set(true);
210 let this = Trusted::new(self);
212 self.owner_global()
213 .task_manager()
214 .user_interaction_task_source()
215 .queue(
216 task!(selectionchange_task_steps: move |cx| {
218 let this = this.root();
219 this.has_scheduled_selectionchange_event.set(false);
221 this.upcast::<EventTarget>().fire_event_with_params(
223 cx,
224 atom!("selectionchange"),
225 EventBubbles::Bubbles,
226 EventCancelable::NotCancelable,
227 EventComposed::Composed,
228 );
229 }),
234 );
235 }
236}
237
238impl TextControlElement for HTMLTextAreaElement {
239 fn as_element(&self) -> &Element {
240 self.upcast()
241 }
242
243 fn text_input(&self) -> Ref<'_, TextInput<EmbedderClipboardProvider>> {
244 self.text_input.borrow()
245 }
246
247 fn text_input_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
248 self.text_input.borrow_mut()
249 }
250
251 fn selection_api_applies(&self) -> bool {
252 true
253 }
254
255 fn has_selectable_text(&self) -> bool {
256 !self.text_input.borrow().get_content().is_empty()
257 }
258
259 fn has_uncollapsed_selection(&self) -> bool {
260 self.text_input.borrow().has_uncollapsed_selection()
261 }
262
263 fn set_dirty_value_flag(&self, value: bool) {
264 self.value_dirty.set(value)
265 }
266
267 fn select_all(&self) {
268 self.text_input.borrow_mut().select_all();
269 self.maybe_update_shared_selection();
270 }
271
272 fn maybe_update_shared_selection(&self) {
273 let selection = {
274 let mut text_input = self.text_input.borrow_mut();
275 let selection_range = text_input.selection_start()..text_input.selection_end();
276 let enabled = self.upcast::<Element>().focus_state();
277
278 let range_remained_equal = selection_range == text_input.previous_selection_range;
279 if range_remained_equal && enabled == text_input.selection_for_layout.is_some() {
280 return;
281 }
282
283 if !range_remained_equal {
284 self.schedule_a_selection_change_event();
289 }
290
291 let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range());
292 text_input.previous_selection_range = selection_range;
293 text_input.selection_for_layout = selection;
294 selection
295 };
296
297 if self
298 .text_input_widget
299 .borrow()
300 .set_text_run_selection(selection)
301 {
302 self.owner_window().layout().set_needs_new_display_list();
304 } else {
305 }
307 }
308
309 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
310 self.placeholder.borrow()
311 }
312
313 fn value_text(&self) -> DOMString {
314 self.Value()
315 }
316
317 fn read_only_or_disabled(&self) -> bool {
318 self.ReadOnly() || self.Disabled()
319 }
320
321 fn handle_text_content_changed(&self, cx: &mut JSContext) {
322 self.validity_state(cx)
323 .perform_validation_and_update(cx, ValidationFlags::all());
324
325 let placeholder_shown =
326 self.text_input.borrow().is_empty() && !self.placeholder.borrow().is_empty();
327 self.upcast::<Element>()
328 .set_placeholder_shown_state(placeholder_shown);
329
330 self.text_input_widget.borrow().update_shadow_tree(cx, self);
331 self.text_input_widget
332 .borrow()
333 .update_placeholder_contents(cx, self);
334 self.maybe_update_shared_selection();
335 }
336}
337
338impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
339 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
344
345 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
347
348 make_getter!(DirName, "dirname");
350
351 make_setter!(SetDirName, "dirname");
353
354 make_bool_getter!(Disabled, "disabled");
356
357 make_bool_setter!(SetDisabled, "disabled");
359
360 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
362 self.form_owner()
363 }
364
365 make_getter!(Name, "name");
367
368 make_atomic_setter!(SetName, "name");
370
371 make_getter!(Placeholder, "placeholder");
373
374 make_setter!(SetPlaceholder, "placeholder");
376
377 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
379
380 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
382
383 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
385
386 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
388
389 make_bool_getter!(ReadOnly, "readonly");
391
392 make_bool_setter!(SetReadOnly, "readonly");
394
395 make_bool_getter!(Required, "required");
397
398 make_bool_setter!(SetRequired, "required");
400
401 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
403
404 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
406
407 make_getter!(Wrap, "wrap");
409
410 make_setter!(SetWrap, "wrap");
412
413 fn Type(&self) -> DOMString {
415 DOMString::from_static("textarea")
416 }
417
418 fn DefaultValue(&self) -> DOMString {
420 self.upcast::<Node>().GetTextContent().unwrap()
421 }
422
423 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
425 self.upcast::<Node>()
426 .set_text_content_for_element(cx, Some(value));
427
428 if !self.value_dirty.get() {
431 self.reset(cx);
432 }
433 }
434
435 fn Value(&self) -> DOMString {
437 self.text_input.borrow().get_content()
438 }
439
440 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
442 let old_api_value = self.Value();
444
445 self.text_input.borrow_mut().set_content(value);
447
448 self.value_dirty.set(true);
450
451 if old_api_value != self.Value() {
456 self.text_input.borrow_mut().clear_selection_to_end();
457 self.handle_text_content_changed(cx);
458 }
459 }
460
461 fn TextLength(&self) -> u32 {
463 self.text_input.borrow().len_utf16().0
464 }
465
466 make_labels_getter!(Labels, labels_node_list);
468
469 fn Select(&self) {
471 self.dom_select();
472 }
473
474 fn GetSelectionStart(&self) -> Option<u32> {
476 self.dom_start().map(|start| start.0)
477 }
478
479 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
481 self.set_dom_start(start.map(Utf16CodeUnits::from))
482 }
483
484 fn GetSelectionEnd(&self) -> Option<u32> {
486 self.dom_end().map(|end| end.0)
487 }
488
489 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
491 self.set_dom_end(end.map(Utf16CodeUnits::from))
492 }
493
494 fn GetSelectionDirection(&self) -> Option<DOMString> {
496 self.dom_direction()
497 }
498
499 fn SetSelectionDirection(
501 &self,
502 _cx: &mut JSContext,
503 direction: Option<DOMString>,
504 ) -> ErrorResult {
505 self.set_dom_direction(direction)
506 }
507
508 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
510 self.set_dom_range(
511 Utf16CodeUnits::from(start),
512 Utf16CodeUnits::from(end),
513 direction,
514 )
515 }
516
517 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
519 self.set_dom_range_text(replacement, None, None, Default::default())
520 }
521
522 fn SetRangeText_(
524 &self,
525 replacement: DOMString,
526 start: u32,
527 end: u32,
528 selection_mode: SelectionMode,
529 ) -> ErrorResult {
530 self.set_dom_range_text(
531 replacement,
532 Some(Utf16CodeUnits::from(start)),
533 Some(Utf16CodeUnits::from(end)),
534 selection_mode,
535 )
536 }
537
538 fn WillValidate(&self) -> bool {
540 self.is_instance_validatable()
541 }
542
543 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
545 self.validity_state(cx)
546 }
547
548 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
550 self.check_validity(cx)
551 }
552
553 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
555 self.report_validity(cx)
556 }
557
558 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
560 self.validation_message(cx)
561 }
562
563 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
565 self.validity_state(cx).set_custom_error_message(cx, error);
566 }
567}
568
569impl HTMLTextAreaElement {
570 pub(crate) fn clear(&self) {
573 self.value_dirty.set(false);
574 self.text_input.borrow_mut().set_content(DOMString::new());
575 }
576
577 pub(crate) fn reset(&self, cx: &mut JSContext) {
578 self.value_dirty.set(false);
580 self.text_input
581 .borrow_mut()
582 .set_content(self.DefaultValue());
583 self.handle_text_content_changed(cx);
584 }
585
586 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction, event: &Event) {
587 match action {
588 KeyReaction::TriggerDefaultAction => (),
589 KeyReaction::DispatchInput(text, is_composing, input_type) => {
590 if event.IsTrusted() {
591 self.queue_input_event(text, is_composing, input_type);
592 }
593 self.value_dirty.set(true);
594 self.handle_text_content_changed(cx);
595 event.mark_as_handled();
596 },
597 KeyReaction::RedrawSelection => {
598 self.maybe_update_shared_selection();
599 event.mark_as_handled();
600 },
601 KeyReaction::Nothing => (),
602 }
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_disabled_state(true);
626 el.set_enabled_state(false);
627
628 el.set_read_write_state(false);
629 },
630 AttributeMutation::Removed => {
631 el.set_disabled_state(false);
632 el.set_enabled_state(true);
633 el.check_ancestors_disabled_state_for_form_control();
634
635 if !el.disabled_state() && !el.read_write_state() {
636 el.set_read_write_state(true);
637 }
638 },
639 }
640 },
641 local_name!("maxlength") => match *attr.value() {
642 AttrValue::Int(_, value) => {
643 let mut text_input = self.text_input.borrow_mut();
644
645 if value < 0 {
646 text_input.set_max_length(None);
647 } else {
648 text_input.set_max_length(Some(Utf16CodeUnits(value as u32)))
649 }
650 },
651 _ => panic!("Expected an AttrValue::Int"),
652 },
653 local_name!("minlength") => match *attr.value() {
654 AttrValue::Int(_, value) => {
655 let mut text_input = self.text_input.borrow_mut();
656
657 if value < 0 {
658 text_input.set_min_length(None);
659 } else {
660 text_input.set_min_length(Some(Utf16CodeUnits(value as u32)))
661 }
662 },
663 _ => panic!("Expected an AttrValue::Int"),
664 },
665 local_name!("placeholder") => {
666 {
667 let mut placeholder = self.placeholder.borrow_mut();
668 match mutation {
669 AttributeMutation::Set(..) => {
670 let value = attr.value();
671 let value_str: &str = value.as_ref();
672 *placeholder =
673 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
674 },
675 AttributeMutation::Removed => placeholder.clear(),
676 }
677 }
678 self.handle_text_content_changed(cx);
679 },
680 local_name!("readonly") => {
681 let el = self.upcast::<Element>();
682 match mutation {
683 AttributeMutation::Set(..) => {
684 el.set_read_write_state(false);
685 },
686 AttributeMutation::Removed => {
687 el.set_read_write_state(!el.disabled_state());
688 },
689 }
690 },
691 local_name!("form") => {
692 self.form_attribute_mutated(cx, mutation);
693 },
694 _ => {},
695 }
696
697 self.validity_state(cx)
698 .perform_validation_and_update(cx, ValidationFlags::all());
699 }
700
701 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
702 if let Some(s) = self.super_type() {
703 s.bind_to_tree(cx, context);
704 }
705
706 self.upcast::<Element>()
707 .check_ancestors_disabled_state_for_form_control();
708
709 self.handle_text_content_changed(cx);
710 }
711
712 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
713 match *name {
714 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
715 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
716 local_name!("maxlength") => {
717 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
718 },
719 local_name!("minlength") => {
720 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
721 },
722 _ => self
723 .super_type()
724 .unwrap()
725 .parse_plain_attribute(name, value),
726 }
727 }
728
729 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
730 self.owner_document()
732 .embedder_controls()
733 .hide_embedder_control(self.upcast());
734
735 self.super_type().unwrap().unbind_from_tree(cx, context);
736
737 let node = self.upcast::<Node>();
738 let el = self.upcast::<Element>();
739 if node
740 .ancestors()
741 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
742 {
743 el.check_ancestors_disabled_state_for_form_control();
744 } else {
745 el.check_disabled_attribute();
746 }
747
748 self.validity_state(cx)
749 .perform_validation_and_update(cx, ValidationFlags::all());
750 }
751
752 fn cloning_steps(
755 &self,
756 cx: &mut JSContext,
757 copy: &Node,
758 maybe_doc: Option<&Document>,
759 clone_children: CloneChildrenFlag,
760 ) {
761 if let Some(s) = self.super_type() {
762 s.cloning_steps(cx, copy, maybe_doc, clone_children);
763 }
764 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
765 el.value_dirty.set(self.value_dirty.get());
766 {
767 let mut text_input = el.text_input.borrow_mut();
768 text_input.set_content(self.text_input.borrow().get_content());
769 }
770 el.validity_state(cx)
771 .perform_validation_and_update(cx, ValidationFlags::all());
772 }
773
774 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
775 if let Some(s) = self.super_type() {
776 s.children_changed(cx, mutation);
777 }
778 if !self.value_dirty.get() {
779 self.reset(cx);
780 }
781 }
782
783 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
785 if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
786 if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
787 let action = self.text_input.borrow_mut().handle_keydown(keyboard_event);
790 self.handle_key_reaction(cx, action, event);
791 }
792 } else if event.type_() == atom!("compositionstart") ||
793 event.type_() == atom!("compositionupdate") ||
794 event.type_() == atom!("compositionend")
795 {
796 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
797 if event.type_() == atom!("compositionend") {
798 let action = self
799 .text_input
800 .borrow_mut()
801 .handle_compositionend(compositionevent);
802 self.handle_key_reaction(cx, action, event);
803 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
804 } else if event.type_() == atom!("compositionupdate") {
805 let action = self
806 .text_input
807 .borrow_mut()
808 .handle_compositionupdate(compositionevent);
809 self.handle_key_reaction(cx, action, event);
810 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
811 }
812 self.maybe_update_shared_selection();
813 event.mark_as_handled();
814 }
815 } else if let Some(event) = event.downcast::<FocusEvent>() {
816 self.handle_focus_event(cx, event);
817 }
818
819 self.validity_state(cx)
820 .perform_validation_and_update(cx, ValidationFlags::all());
821
822 if let Some(super_type) = self.super_type() {
823 super_type.handle_event(cx, event);
824 }
825 }
826
827 fn handle_mousedown_event(
828 &self,
829 cx: &mut JSContext,
830 mouse_event: &MouseEvent,
831 hit_test_result: &HitTestResult,
832 ) {
833 if self.text_input.borrow().is_empty() {
835 if let Some(super_type) = self.super_type() {
836 super_type.handle_mousedown_event(cx, mouse_event, hit_test_result);
837 }
838 return;
839 }
840
841 if self.text_input.borrow_mut().handle_mousedown_event(
842 self.upcast(),
843 mouse_event,
844 hit_test_result,
845 ) {
846 self.maybe_update_shared_selection();
847 mouse_event.upcast::<Event>().mark_as_handled();
848 }
849 }
850
851 fn pop(&self, cx: &mut JSContext) {
852 self.super_type().unwrap().pop(cx);
853
854 self.reset(cx);
856 }
857}
858
859impl FormControl for HTMLTextAreaElement {
860 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
861 self.form_owner.get()
862 }
863
864 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
865 self.form_owner.set(form);
866 }
867
868 fn to_html_element(&self) -> &HTMLElement {
869 self.upcast::<HTMLElement>()
870 }
871}
872
873impl Validatable for HTMLTextAreaElement {
874 fn as_element(&self) -> &Element {
875 self.upcast()
876 }
877
878 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
879 self.validity_state
880 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
881 }
882
883 fn is_instance_validatable(&self) -> bool {
884 !self.upcast::<Element>().disabled_state() &&
888 !self.ReadOnly() &&
889 !is_barred_by_datalist_ancestor(self.upcast())
890 }
891
892 fn perform_validation(
893 &self,
894 _cx: &mut JSContext,
895 validate_flags: ValidationFlags,
896 ) -> ValidationFlags {
897 let mut failed_flags = ValidationFlags::empty();
898
899 let text_input = self.text_input.borrow();
900 let Utf16CodeUnits(value_len) = text_input.len_utf16();
901 let last_edit_by_user = !text_input.was_last_change_by_set_content();
902 let value_dirty = self.value_dirty.get();
903
904 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
907 self.Required() &&
908 self.is_mutable() &&
909 value_len == 0
910 {
911 failed_flags.insert(ValidationFlags::VALUE_MISSING);
912 }
913
914 if value_dirty && last_edit_by_user && value_len > 0 {
915 if validate_flags.contains(ValidationFlags::TOO_LONG) {
918 let max_length = self.MaxLength();
919 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as u32) {
920 failed_flags.insert(ValidationFlags::TOO_LONG);
921 }
922 }
923
924 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
927 let min_length = self.MinLength();
928 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as u32) {
929 failed_flags.insert(ValidationFlags::TOO_SHORT);
930 }
931 }
932 }
933
934 failed_flags
935 }
936}