1use std::cell::{Cell, Ref, RefCell};
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use embedder_traits::{EmbedderControlRequest, InputMethodRequest, InputMethodType};
10use fonts::{ByteIndex, TextByteRange};
11use html5ever::{LocalName, Prefix, local_name, ns};
12use js::context::JSContext;
13use js::rust::HandleObject;
14use layout_api::{ScriptSelection, SharedSelection};
15use script_bindings::cell::DomRefCell;
16use servo_base::text::Utf16CodeUnits;
17use style::attr::AttrValue;
18use stylo_dom::ElementState;
19
20use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
21use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
22use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
23use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
24use crate::dom::bindings::error::ErrorResult;
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::refcounted::Trusted;
27use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
28use crate::dom::bindings::str::DOMString;
29use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
30use crate::dom::compositionevent::CompositionEvent;
31use crate::dom::document::Document;
32use crate::dom::document_embedder_controls::ControlElement;
33use crate::dom::element::attributes::storage::AttrRef;
34use crate::dom::element::{AttributeMutation, Element};
35use crate::dom::event::Event;
36use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
37use crate::dom::eventtarget::EventTarget;
38use crate::dom::html::form_controls::htmlinputelement::HTMLInputElement;
39use crate::dom::html::form_controls::input_type::text_input_widget::TextInputWidget;
40use crate::dom::html::form_controls::text_control::{TextControlElement, TextControlSelection};
41use crate::dom::html::form_controls::text_input::{
42 ClipboardEventFlags, EmbedderClipboardProvider, IsComposing, KeyReaction, Lines, TextInput,
43};
44use crate::dom::html::htmlelement::HTMLElement;
45use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
46use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
47use crate::dom::keyboardevent::KeyboardEvent;
48use crate::dom::node::virtualmethods::VirtualMethods;
49use crate::dom::node::{
50 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
51};
52use crate::dom::nodelist::NodeList;
53use crate::dom::types::{FocusEvent, MouseEvent};
54use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
55use crate::dom::validitystate::{ValidationFlags, ValidityState};
56
57#[dom_struct]
58pub(crate) struct HTMLTextAreaElement {
59 htmlelement: HTMLElement,
60 #[no_trace]
61 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
62 placeholder: RefCell<DOMString>,
63 value_dirty: Cell<bool>,
65 form_owner: MutNullableDom<HTMLFormElement>,
66 labels_node_list: MutNullableDom<NodeList>,
67 validity_state: MutNullableDom<ValidityState>,
68 text_input_widget: DomRefCell<TextInputWidget>,
70 #[no_trace]
73 #[conditional_malloc_size_of]
74 shared_selection: SharedSelection,
75
76 has_scheduled_selectionchange_event: Cell<bool>,
78}
79
80impl LayoutDom<'_, HTMLTextAreaElement> {
81 pub(crate) fn selection_for_layout(self) -> SharedSelection {
82 self.unsafe_get().shared_selection.clone()
83 }
84
85 pub(crate) fn get_cols(self) -> u32 {
86 self.upcast::<Element>()
87 .get_attr_for_layout(&ns!(), &local_name!("cols"))
88 .map_or(DEFAULT_COLS, AttrValue::as_uint)
89 }
90
91 pub(crate) fn get_rows(self) -> u32 {
92 self.upcast::<Element>()
93 .get_attr_for_layout(&ns!(), &local_name!("rows"))
94 .map_or(DEFAULT_ROWS, AttrValue::as_uint)
95 }
96}
97
98const DEFAULT_COLS: u32 = 20;
100
101const DEFAULT_ROWS: u32 = 2;
103
104const DEFAULT_MAX_LENGTH: i32 = -1;
105const DEFAULT_MIN_LENGTH: i32 = -1;
106
107impl HTMLTextAreaElement {
108 fn new_inherited(
109 local_name: LocalName,
110 prefix: Option<Prefix>,
111 document: &Document,
112 ) -> HTMLTextAreaElement {
113 let embedder_sender = document
114 .window()
115 .as_global_scope()
116 .script_to_embedder_chan()
117 .clone();
118 HTMLTextAreaElement {
119 htmlelement: HTMLElement::new_inherited_with_state(
120 ElementState::ENABLED | ElementState::READWRITE,
121 local_name,
122 prefix,
123 document,
124 ),
125 placeholder: Default::default(),
126 textinput: DomRefCell::new(TextInput::new(
127 Lines::Multiple,
128 DOMString::new(),
129 EmbedderClipboardProvider {
130 embedder_sender,
131 webview_id: document.webview_id(),
132 },
133 )),
134 value_dirty: Cell::new(false),
135 form_owner: Default::default(),
136 labels_node_list: Default::default(),
137 validity_state: Default::default(),
138 text_input_widget: Default::default(),
139 shared_selection: Default::default(),
140 has_scheduled_selectionchange_event: Default::default(),
141 }
142 }
143
144 pub(crate) fn new(
145 cx: &mut JSContext,
146 local_name: LocalName,
147 prefix: Option<Prefix>,
148 document: &Document,
149 proto: Option<HandleObject>,
150 ) -> DomRoot<HTMLTextAreaElement> {
151 Node::reflect_node_with_proto(
152 cx,
153 Box::new(HTMLTextAreaElement::new_inherited(
154 local_name, prefix, document,
155 )),
156 document,
157 proto,
158 )
159 }
160
161 pub(crate) fn auto_directionality(&self) -> String {
162 let value: String = String::from(self.Value());
163 HTMLInputElement::directionality_from_value(&value)
164 }
165
166 pub(crate) fn is_mutable(&self) -> bool {
168 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
171 }
172
173 fn handle_focus_event(&self, event: &FocusEvent) {
174 let event_type = event.upcast::<Event>().type_();
175 if *event_type == *"blur" {
176 self.owner_document()
177 .embedder_controls()
178 .hide_embedder_control(self.upcast());
179 } else if *event_type == *"focus" {
180 self.owner_document()
181 .embedder_controls()
182 .show_embedder_control(
183 ControlElement::Ime(Dom::from_ref(self.upcast())),
184 EmbedderControlRequest::InputMethod(InputMethodRequest {
185 input_method_type: InputMethodType::Text,
186 text: String::from(self.Value()),
187 insertion_point: self.GetSelectionEnd(),
188 multiline: false,
189 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
191 }),
192 None,
193 );
194 }
195
196 self.maybe_update_shared_selection();
198 }
199
200 fn handle_text_content_changed(&self, cx: &mut JSContext) {
201 self.validity_state(cx)
202 .perform_validation_and_update(cx, ValidationFlags::all());
203
204 let placeholder_shown =
205 self.textinput.borrow().is_empty() && !self.placeholder.borrow().is_empty();
206 self.upcast::<Element>()
207 .set_placeholder_shown_state(placeholder_shown);
208
209 self.text_input_widget.borrow().update_shadow_tree(cx, self);
210 self.text_input_widget
211 .borrow()
212 .update_placeholder_contents(cx, self);
213 self.maybe_update_shared_selection();
214 }
215
216 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
217 if mouse_event.upcast::<Event>().DefaultPrevented() {
218 return;
219 }
220
221 if self.textinput.borrow().is_empty() {
224 return;
225 }
226 if self.textinput.borrow_mut().handle_mouse_event(mouse_event) {
227 self.maybe_update_shared_selection();
228 }
229 }
230
231 fn schedule_a_selection_change_event(&self) {
233 if self.has_scheduled_selectionchange_event.get() {
235 return;
236 }
237 self.has_scheduled_selectionchange_event.set(true);
239 let this = Trusted::new(self);
241 self.owner_global()
242 .task_manager()
243 .user_interaction_task_source()
244 .queue(
245 task!(selectionchange_task_steps: move |cx| {
247 let this = this.root();
248 this.has_scheduled_selectionchange_event.set(false);
250 this.upcast::<EventTarget>().fire_event_with_params(
252 cx,
253 atom!("selectionchange"),
254 EventBubbles::Bubbles,
255 EventCancelable::NotCancelable,
256 EventComposed::Composed,
257 );
258 }),
263 );
264 }
265}
266
267impl TextControlElement for HTMLTextAreaElement {
268 fn selection_api_applies(&self) -> bool {
269 true
270 }
271
272 fn has_selectable_text(&self) -> bool {
273 !self.textinput.borrow().get_content().is_empty()
274 }
275
276 fn has_uncollapsed_selection(&self) -> bool {
277 self.textinput.borrow().has_uncollapsed_selection()
278 }
279
280 fn set_dirty_value_flag(&self, value: bool) {
281 self.value_dirty.set(value)
282 }
283
284 fn select_all(&self) {
285 self.textinput.borrow_mut().select_all();
286 self.maybe_update_shared_selection();
287 }
288
289 fn maybe_update_shared_selection(&self) {
290 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
291 let (start, end) = (offsets.start.0, offsets.end.0);
292 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
293 let enabled = self.upcast::<Element>().focus_state();
294
295 let mut shared_selection = self.shared_selection.borrow_mut();
296 let range_remained_equal = range == shared_selection.range;
297 if range_remained_equal && enabled == shared_selection.enabled {
298 return;
299 }
300
301 if !range_remained_equal {
302 self.schedule_a_selection_change_event();
307 }
308
309 *shared_selection = ScriptSelection {
310 range,
311 character_range: self
312 .textinput
313 .borrow()
314 .sorted_selection_character_offsets_range(),
315 enabled,
316 };
317 self.owner_window().layout().set_needs_new_display_list();
318 }
319
320 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
321 self.placeholder.borrow()
322 }
323
324 fn value_text(&self) -> DOMString {
325 self.Value()
326 }
327}
328
329impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
330 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
335
336 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
338
339 make_getter!(DirName, "dirname");
341
342 make_setter!(SetDirName, "dirname");
344
345 make_bool_getter!(Disabled, "disabled");
347
348 make_bool_setter!(SetDisabled, "disabled");
350
351 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
353 self.form_owner()
354 }
355
356 make_getter!(Name, "name");
358
359 make_atomic_setter!(SetName, "name");
361
362 make_getter!(Placeholder, "placeholder");
364
365 make_setter!(SetPlaceholder, "placeholder");
367
368 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
370
371 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
373
374 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
376
377 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
379
380 make_bool_getter!(ReadOnly, "readonly");
382
383 make_bool_setter!(SetReadOnly, "readonly");
385
386 make_bool_getter!(Required, "required");
388
389 make_bool_setter!(SetRequired, "required");
391
392 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
394
395 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
397
398 make_getter!(Wrap, "wrap");
400
401 make_setter!(SetWrap, "wrap");
403
404 fn Type(&self) -> DOMString {
406 DOMString::from("textarea")
407 }
408
409 fn DefaultValue(&self) -> DOMString {
411 self.upcast::<Node>().GetTextContent().unwrap()
412 }
413
414 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
416 self.upcast::<Node>()
417 .set_text_content_for_element(cx, Some(value));
418
419 if !self.value_dirty.get() {
422 self.reset(cx);
423 }
424 }
425
426 fn Value(&self) -> DOMString {
428 self.textinput.borrow().get_content()
429 }
430
431 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
433 let old_api_value = self.Value();
435
436 self.textinput.borrow_mut().set_content(value);
438
439 self.value_dirty.set(true);
441
442 if old_api_value != self.Value() {
447 self.textinput.borrow_mut().clear_selection_to_end();
448 self.handle_text_content_changed(cx);
449 }
450 }
451
452 fn TextLength(&self) -> u32 {
454 self.textinput.borrow().len_utf16().0 as u32
455 }
456
457 make_labels_getter!(Labels, labels_node_list);
459
460 fn Select(&self) {
462 self.selection().dom_select();
463 }
464
465 fn GetSelectionStart(&self) -> Option<u32> {
467 self.selection().dom_start().map(|start| start.0 as u32)
468 }
469
470 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
472 self.selection()
473 .set_dom_start(start.map(Utf16CodeUnits::from))
474 }
475
476 fn GetSelectionEnd(&self) -> Option<u32> {
478 self.selection().dom_end().map(|end| end.0 as u32)
479 }
480
481 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
483 self.selection().set_dom_end(end.map(Utf16CodeUnits::from))
484 }
485
486 fn GetSelectionDirection(&self) -> Option<DOMString> {
488 self.selection().dom_direction()
489 }
490
491 fn SetSelectionDirection(
493 &self,
494 _cx: &mut JSContext,
495 direction: Option<DOMString>,
496 ) -> ErrorResult {
497 self.selection().set_dom_direction(direction)
498 }
499
500 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
502 self.selection().set_dom_range(
503 Utf16CodeUnits::from(start),
504 Utf16CodeUnits::from(end),
505 direction,
506 )
507 }
508
509 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
511 self.selection()
512 .set_dom_range_text(replacement, None, None, Default::default())
513 }
514
515 fn SetRangeText_(
517 &self,
518 replacement: DOMString,
519 start: u32,
520 end: u32,
521 selection_mode: SelectionMode,
522 ) -> ErrorResult {
523 self.selection().set_dom_range_text(
524 replacement,
525 Some(Utf16CodeUnits::from(start)),
526 Some(Utf16CodeUnits::from(end)),
527 selection_mode,
528 )
529 }
530
531 fn WillValidate(&self) -> bool {
533 self.is_instance_validatable()
534 }
535
536 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
538 self.validity_state(cx)
539 }
540
541 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
543 self.check_validity(cx)
544 }
545
546 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
548 self.report_validity(cx)
549 }
550
551 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
553 self.validation_message(cx)
554 }
555
556 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
558 self.validity_state(cx).set_custom_error_message(cx, error);
559 }
560}
561
562impl HTMLTextAreaElement {
563 pub(crate) fn clear(&self) {
566 self.value_dirty.set(false);
567 self.textinput.borrow_mut().set_content(DOMString::from(""));
568 }
569
570 pub(crate) fn reset(&self, cx: &mut JSContext) {
571 self.value_dirty.set(false);
573 self.textinput.borrow_mut().set_content(self.DefaultValue());
574 self.handle_text_content_changed(cx);
575 }
576
577 fn selection(&self) -> TextControlSelection<'_, Self> {
578 TextControlSelection::new(self, &self.textinput)
579 }
580
581 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction, event: &Event) {
582 match action {
583 KeyReaction::TriggerDefaultAction => (),
584 KeyReaction::DispatchInput(text, is_composing, input_type) => {
585 if event.IsTrusted() {
586 self.textinput.borrow().queue_input_event(
587 self.upcast(),
588 text,
589 is_composing,
590 input_type,
591 );
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 textinput = self.textinput.borrow_mut();
644
645 if value < 0 {
646 textinput.set_max_length(None);
647 } else {
648 textinput.set_max_length(Some(Utf16CodeUnits(value as usize)))
649 }
650 },
651 _ => panic!("Expected an AttrValue::Int"),
652 },
653 local_name!("minlength") => match *attr.value() {
654 AttrValue::Int(_, value) => {
655 let mut textinput = self.textinput.borrow_mut();
656
657 if value < 0 {
658 textinput.set_min_length(None);
659 } else {
660 textinput.set_min_length(Some(Utf16CodeUnits(value as usize)))
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 textinput = el.textinput.borrow_mut();
768 textinput.set_content(self.textinput.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 let Some(mouse_event) = event.downcast::<MouseEvent>() {
786 self.handle_mouse_event(mouse_event);
787 event.mark_as_handled();
788 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
789 if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
790 let action = self.textinput.borrow_mut().handle_keydown(keyboard_event);
793 self.handle_key_reaction(cx, action, event);
794 }
795 } else if event.type_() == atom!("compositionstart") ||
796 event.type_() == atom!("compositionupdate") ||
797 event.type_() == atom!("compositionend")
798 {
799 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
800 if event.type_() == atom!("compositionend") {
801 let action = self
802 .textinput
803 .borrow_mut()
804 .handle_compositionend(compositionevent);
805 self.handle_key_reaction(cx, action, event);
806 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
807 } else if event.type_() == atom!("compositionupdate") {
808 let action = self
809 .textinput
810 .borrow_mut()
811 .handle_compositionupdate(compositionevent);
812 self.handle_key_reaction(cx, action, event);
813 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
814 }
815 self.maybe_update_shared_selection();
816 event.mark_as_handled();
817 }
818 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
819 let reaction = self
820 .textinput
821 .borrow_mut()
822 .handle_clipboard_event(clipboard_event);
823
824 let flags = reaction.flags;
825 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
826 self.owner_document().event_handler().fire_clipboard_event(
827 cx,
828 None,
829 ClipboardEventType::Change,
830 );
831 }
832 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
833 self.textinput.borrow().queue_input_event(
834 self.upcast(),
835 reaction.text,
836 IsComposing::NotComposing,
837 reaction.input_type,
838 );
839 }
840 if !flags.is_empty() {
841 event.mark_as_handled();
842 self.handle_text_content_changed(cx);
843 }
844 } else if let Some(event) = event.downcast::<FocusEvent>() {
845 self.handle_focus_event(event);
846 }
847
848 self.validity_state(cx)
849 .perform_validation_and_update(cx, ValidationFlags::all());
850
851 if let Some(super_type) = self.super_type() {
852 super_type.handle_event(cx, event);
853 }
854 }
855
856 fn pop(&self, cx: &mut JSContext) {
857 self.super_type().unwrap().pop(cx);
858
859 self.reset(cx);
861 }
862}
863
864impl FormControl for HTMLTextAreaElement {
865 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
866 self.form_owner.get()
867 }
868
869 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
870 self.form_owner.set(form);
871 }
872
873 fn to_html_element(&self) -> &HTMLElement {
874 self.upcast::<HTMLElement>()
875 }
876}
877
878impl Validatable for HTMLTextAreaElement {
879 fn as_element(&self) -> &Element {
880 self.upcast()
881 }
882
883 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
884 self.validity_state
885 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
886 }
887
888 fn is_instance_validatable(&self) -> bool {
889 !self.upcast::<Element>().disabled_state() &&
893 !self.ReadOnly() &&
894 !is_barred_by_datalist_ancestor(self.upcast())
895 }
896
897 fn perform_validation(
898 &self,
899 _cx: &mut JSContext,
900 validate_flags: ValidationFlags,
901 ) -> ValidationFlags {
902 let mut failed_flags = ValidationFlags::empty();
903
904 let textinput = self.textinput.borrow();
905 let Utf16CodeUnits(value_len) = textinput.len_utf16();
906 let last_edit_by_user = !textinput.was_last_change_by_set_content();
907 let value_dirty = self.value_dirty.get();
908
909 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
912 self.Required() &&
913 self.is_mutable() &&
914 value_len == 0
915 {
916 failed_flags.insert(ValidationFlags::VALUE_MISSING);
917 }
918
919 if value_dirty && last_edit_by_user && value_len > 0 {
920 if validate_flags.contains(ValidationFlags::TOO_LONG) {
923 let max_length = self.MaxLength();
924 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
925 failed_flags.insert(ValidationFlags::TOO_LONG);
926 }
927 }
928
929 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
932 let min_length = self.MinLength();
933 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
934 failed_flags.insert(ValidationFlags::TOO_SHORT);
935 }
936 }
937 }
938
939 failed_flags
940 }
941}