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 servo_base::text::Utf16CodeUnitLength;
16use style::attr::AttrValue;
17use stylo_dom::ElementState;
18
19use crate::clipboard_provider::EmbedderClipboardProvider;
20use crate::dom::attr::Attr;
21use crate::dom::bindings::cell::DomRefCell;
22use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
23use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
24use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
25use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
26use crate::dom::bindings::error::ErrorResult;
27use crate::dom::bindings::inheritance::Castable;
28use crate::dom::bindings::refcounted::Trusted;
29use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
30use crate::dom::bindings::str::DOMString;
31use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
32use crate::dom::compositionevent::CompositionEvent;
33use crate::dom::document::Document;
34use crate::dom::document_embedder_controls::ControlElement;
35use crate::dom::element::{AttributeMutation, Element};
36use crate::dom::event::Event;
37use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
38use crate::dom::eventtarget::EventTarget;
39use crate::dom::html::htmlelement::HTMLElement;
40use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
41use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
42use crate::dom::html::input_element::HTMLInputElement;
43use crate::dom::htmlinputelement::text_input_widget::TextInputWidget;
44use crate::dom::keyboardevent::KeyboardEvent;
45use crate::dom::node::{
46 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
47};
48use crate::dom::nodelist::NodeList;
49use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
50use crate::dom::types::{FocusEvent, MouseEvent};
51use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
52use crate::dom::validitystate::{ValidationFlags, ValidityState};
53use crate::dom::virtualmethods::VirtualMethods;
54use crate::script_runtime::CanGc;
55use crate::textinput::{ClipboardEventFlags, IsComposing, KeyReaction, Lines, TextInput};
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 js::context::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 = self.Value().to_string();
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(DomRoot::from_ref(self.upcast())),
184 EmbedderControlRequest::InputMethod(InputMethodRequest {
185 input_method_type: InputMethodType::Text,
186 text: self.Value().to_string(),
187 insertion_point: self.GetSelectionEnd(),
188 multiline: false,
189 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
191 }),
192 None,
193 );
194 } else {
195 unreachable!("Got unexpected FocusEvent {event_type:?}");
196 }
197
198 self.maybe_update_shared_selection();
200 }
201
202 #[expect(unsafe_code)]
203 fn handle_text_content_changed(&self, _can_gc: CanGc) {
204 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
206 let cx = &mut cx;
207
208 self.validity_state(CanGc::from_cx(cx))
209 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
210
211 let placeholder_shown =
212 self.textinput.borrow().is_empty() && !self.placeholder.borrow().is_empty();
213 self.upcast::<Element>()
214 .set_placeholder_shown_state(placeholder_shown);
215
216 self.text_input_widget.borrow().update_shadow_tree(cx, self);
217 self.text_input_widget
218 .borrow()
219 .update_placeholder_contents(cx, self);
220 self.maybe_update_shared_selection();
221 }
222
223 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
224 if mouse_event.upcast::<Event>().DefaultPrevented() {
225 return;
226 }
227
228 if self.textinput.borrow().is_empty() {
231 return;
232 }
233 let node = self.upcast();
234 if self
235 .textinput
236 .borrow_mut()
237 .handle_mouse_event(node, mouse_event)
238 {
239 self.maybe_update_shared_selection();
240 }
241 }
242
243 fn schedule_a_selection_change_event(&self) {
245 if self.has_scheduled_selectionchange_event.get() {
247 return;
248 }
249 self.has_scheduled_selectionchange_event.set(true);
251 let this = Trusted::new(self);
253 self.owner_global()
254 .task_manager()
255 .user_interaction_task_source()
256 .queue(
257 task!(selectionchange_task_steps: move |cx| {
259 let this = this.root();
260 this.has_scheduled_selectionchange_event.set(false);
262 this.upcast::<EventTarget>().fire_event_with_params(cx,
264 atom!("selectionchange"),
265 EventBubbles::Bubbles,
266 EventCancelable::NotCancelable,
267 EventComposed::Composed,
268 );
269 }),
274 );
275 }
276}
277
278impl TextControlElement for HTMLTextAreaElement {
279 fn selection_api_applies(&self) -> bool {
280 true
281 }
282
283 fn has_selectable_text(&self) -> bool {
284 !self.textinput.borrow().get_content().is_empty()
285 }
286
287 fn has_uncollapsed_selection(&self) -> bool {
288 self.textinput.borrow().has_uncollapsed_selection()
289 }
290
291 fn set_dirty_value_flag(&self, value: bool) {
292 self.value_dirty.set(value)
293 }
294
295 fn select_all(&self) {
296 self.textinput.borrow_mut().select_all();
297 self.maybe_update_shared_selection();
298 }
299
300 fn maybe_update_shared_selection(&self) {
301 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
302 let (start, end) = (offsets.start.0, offsets.end.0);
303 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
304 let enabled = self.upcast::<Element>().focus_state();
305
306 let mut shared_selection = self.shared_selection.borrow_mut();
307 let range_remained_equal = range == shared_selection.range;
308 if range_remained_equal && enabled == shared_selection.enabled {
309 return;
310 }
311
312 if !range_remained_equal {
313 self.schedule_a_selection_change_event();
318 }
319
320 *shared_selection = ScriptSelection {
321 range,
322 character_range: self
323 .textinput
324 .borrow()
325 .sorted_selection_character_offsets_range(),
326 enabled,
327 };
328 self.owner_window().layout().set_needs_new_display_list();
329 }
330
331 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
332 self.placeholder.borrow()
333 }
334
335 fn value_text(&self) -> DOMString {
336 self.Value()
337 }
338}
339
340impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
341 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
346
347 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
349
350 make_getter!(DirName, "dirname");
352
353 make_setter!(SetDirName, "dirname");
355
356 make_bool_getter!(Disabled, "disabled");
358
359 make_bool_setter!(SetDisabled, "disabled");
361
362 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
364 self.form_owner()
365 }
366
367 make_getter!(Name, "name");
369
370 make_atomic_setter!(SetName, "name");
372
373 make_getter!(Placeholder, "placeholder");
375
376 make_setter!(SetPlaceholder, "placeholder");
378
379 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
381
382 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
384
385 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
387
388 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
390
391 make_bool_getter!(ReadOnly, "readonly");
393
394 make_bool_setter!(SetReadOnly, "readonly");
396
397 make_bool_getter!(Required, "required");
399
400 make_bool_setter!(SetRequired, "required");
402
403 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
405
406 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
408
409 make_getter!(Wrap, "wrap");
411
412 make_setter!(SetWrap, "wrap");
414
415 fn Type(&self) -> DOMString {
417 DOMString::from("textarea")
418 }
419
420 fn DefaultValue(&self) -> DOMString {
422 self.upcast::<Node>().GetTextContent().unwrap()
423 }
424
425 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
427 self.upcast::<Node>()
428 .set_text_content_for_element(cx, Some(value));
429
430 if !self.value_dirty.get() {
433 self.reset(CanGc::from_cx(cx));
434 }
435 }
436
437 fn Value(&self) -> DOMString {
439 self.textinput.borrow().get_content()
440 }
441
442 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
444 let old_api_value = self.Value();
446
447 self.textinput.borrow_mut().set_content(value);
449
450 self.value_dirty.set(true);
452
453 if old_api_value != self.Value() {
458 self.textinput.borrow_mut().clear_selection_to_end();
459 self.handle_text_content_changed(CanGc::from_cx(cx));
460 }
461 }
462
463 fn TextLength(&self) -> u32 {
465 self.textinput.borrow().len_utf16().0 as u32
466 }
467
468 make_labels_getter!(Labels, labels_node_list);
470
471 fn Select(&self) {
473 self.selection().dom_select();
474 }
475
476 fn GetSelectionStart(&self) -> Option<u32> {
478 self.selection().dom_start().map(|start| start.0 as u32)
479 }
480
481 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
483 self.selection()
484 .set_dom_start(start.map(Utf16CodeUnitLength::from))
485 }
486
487 fn GetSelectionEnd(&self) -> Option<u32> {
489 self.selection().dom_end().map(|end| end.0 as u32)
490 }
491
492 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
494 self.selection()
495 .set_dom_end(end.map(Utf16CodeUnitLength::from))
496 }
497
498 fn GetSelectionDirection(&self) -> Option<DOMString> {
500 self.selection().dom_direction()
501 }
502
503 fn SetSelectionDirection(
505 &self,
506 _cx: &mut JSContext,
507 direction: Option<DOMString>,
508 ) -> ErrorResult {
509 self.selection().set_dom_direction(direction)
510 }
511
512 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
514 self.selection().set_dom_range(
515 Utf16CodeUnitLength::from(start),
516 Utf16CodeUnitLength::from(end),
517 direction,
518 )
519 }
520
521 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
523 self.selection()
524 .set_dom_range_text(replacement, None, None, Default::default())
525 }
526
527 fn SetRangeText_(
529 &self,
530 replacement: DOMString,
531 start: u32,
532 end: u32,
533 selection_mode: SelectionMode,
534 ) -> ErrorResult {
535 self.selection().set_dom_range_text(
536 replacement,
537 Some(Utf16CodeUnitLength::from(start)),
538 Some(Utf16CodeUnitLength::from(end)),
539 selection_mode,
540 )
541 }
542
543 fn WillValidate(&self) -> bool {
545 self.is_instance_validatable()
546 }
547
548 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
550 self.validity_state(can_gc)
551 }
552
553 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
555 self.check_validity(cx)
556 }
557
558 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
560 self.report_validity(cx)
561 }
562
563 fn ValidationMessage(&self) -> DOMString {
565 self.validation_message()
566 }
567
568 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
570 self.validity_state(can_gc).set_custom_error_message(error);
571 }
572}
573
574impl HTMLTextAreaElement {
575 pub(crate) fn clear(&self) {
578 self.value_dirty.set(false);
579 self.textinput.borrow_mut().set_content(DOMString::from(""));
580 }
581
582 pub(crate) fn reset(&self, can_gc: CanGc) {
583 self.value_dirty.set(false);
585 self.textinput.borrow_mut().set_content(self.DefaultValue());
586 self.handle_text_content_changed(can_gc);
587 }
588
589 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
590 fn selection(&self) -> TextControlSelection<'_, Self> {
591 TextControlSelection::new(self, &self.textinput)
592 }
593
594 fn handle_key_reaction(&self, action: KeyReaction, event: &Event, can_gc: CanGc) {
595 match action {
596 KeyReaction::TriggerDefaultAction => (),
597 KeyReaction::DispatchInput(text, is_composing, input_type) => {
598 if event.IsTrusted() {
599 self.textinput.borrow().queue_input_event(
600 self.upcast(),
601 text,
602 is_composing,
603 input_type,
604 );
605 }
606 self.value_dirty.set(true);
607 self.handle_text_content_changed(can_gc);
608 event.mark_as_handled();
609 },
610 KeyReaction::RedrawSelection => {
611 self.maybe_update_shared_selection();
612 event.mark_as_handled();
613 },
614 KeyReaction::Nothing => (),
615 }
616 }
617}
618
619impl VirtualMethods for HTMLTextAreaElement {
620 fn super_type(&self) -> Option<&dyn VirtualMethods> {
621 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
622 }
623
624 fn attribute_mutated(
625 &self,
626 cx: &mut js::context::JSContext,
627 attr: &Attr,
628 mutation: AttributeMutation,
629 ) {
630 self.super_type()
631 .unwrap()
632 .attribute_mutated(cx, attr, mutation);
633 match *attr.local_name() {
634 local_name!("disabled") => {
635 let el = self.upcast::<Element>();
636 match mutation {
637 AttributeMutation::Set(..) => {
638 el.set_disabled_state(true);
639 el.set_enabled_state(false);
640
641 el.set_read_write_state(false);
642 },
643 AttributeMutation::Removed => {
644 el.set_disabled_state(false);
645 el.set_enabled_state(true);
646 el.check_ancestors_disabled_state_for_form_control();
647
648 if !el.disabled_state() && !el.read_write_state() {
649 el.set_read_write_state(true);
650 }
651 },
652 }
653 },
654 local_name!("maxlength") => match *attr.value() {
655 AttrValue::Int(_, value) => {
656 let mut textinput = self.textinput.borrow_mut();
657
658 if value < 0 {
659 textinput.set_max_length(None);
660 } else {
661 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
662 }
663 },
664 _ => panic!("Expected an AttrValue::Int"),
665 },
666 local_name!("minlength") => match *attr.value() {
667 AttrValue::Int(_, value) => {
668 let mut textinput = self.textinput.borrow_mut();
669
670 if value < 0 {
671 textinput.set_min_length(None);
672 } else {
673 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
674 }
675 },
676 _ => panic!("Expected an AttrValue::Int"),
677 },
678 local_name!("placeholder") => {
679 {
680 let mut placeholder = self.placeholder.borrow_mut();
681 match mutation {
682 AttributeMutation::Set(..) => {
683 let value = attr.value();
684 let value_str: &str = value.as_ref();
685 *placeholder =
686 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
687 },
688 AttributeMutation::Removed => placeholder.clear(),
689 }
690 }
691 self.handle_text_content_changed(CanGc::from_cx(cx));
692 },
693 local_name!("readonly") => {
694 let el = self.upcast::<Element>();
695 match mutation {
696 AttributeMutation::Set(..) => {
697 el.set_read_write_state(false);
698 },
699 AttributeMutation::Removed => {
700 el.set_read_write_state(!el.disabled_state());
701 },
702 }
703 },
704 local_name!("form") => {
705 self.form_attribute_mutated(mutation, CanGc::from_cx(cx));
706 },
707 _ => {},
708 }
709
710 self.validity_state(CanGc::from_cx(cx))
711 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
712 }
713
714 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
715 if let Some(s) = self.super_type() {
716 s.bind_to_tree(cx, context);
717 }
718
719 self.upcast::<Element>()
720 .check_ancestors_disabled_state_for_form_control();
721
722 self.handle_text_content_changed(CanGc::from_cx(cx));
723 }
724
725 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
726 match *name {
727 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
728 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
729 local_name!("maxlength") => {
730 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
731 },
732 local_name!("minlength") => {
733 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
734 },
735 _ => self
736 .super_type()
737 .unwrap()
738 .parse_plain_attribute(name, value),
739 }
740 }
741
742 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
743 self.super_type().unwrap().unbind_from_tree(cx, context);
744
745 let node = self.upcast::<Node>();
746 let el = self.upcast::<Element>();
747 if node
748 .ancestors()
749 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
750 {
751 el.check_ancestors_disabled_state_for_form_control();
752 } else {
753 el.check_disabled_attribute();
754 }
755
756 self.validity_state(CanGc::from_cx(cx))
757 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
758 }
759
760 fn cloning_steps(
763 &self,
764 cx: &mut JSContext,
765 copy: &Node,
766 maybe_doc: Option<&Document>,
767 clone_children: CloneChildrenFlag,
768 ) {
769 if let Some(s) = self.super_type() {
770 s.cloning_steps(cx, copy, maybe_doc, clone_children);
771 }
772 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
773 el.value_dirty.set(self.value_dirty.get());
774 {
775 let mut textinput = el.textinput.borrow_mut();
776 textinput.set_content(self.textinput.borrow().get_content());
777 }
778 el.validity_state(CanGc::from_cx(cx))
779 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
780 }
781
782 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
783 if let Some(s) = self.super_type() {
784 s.children_changed(cx, mutation);
785 }
786 if !self.value_dirty.get() {
787 self.reset(CanGc::from_cx(cx));
788 }
789 }
790
791 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
793 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
794 self.handle_mouse_event(mouse_event);
795 event.mark_as_handled();
796 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
797 if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
798 let action = self.textinput.borrow_mut().handle_keydown(keyboard_event);
801 self.handle_key_reaction(action, event, CanGc::from_cx(cx));
802 }
803 } else if event.type_() == atom!("compositionstart") ||
804 event.type_() == atom!("compositionupdate") ||
805 event.type_() == atom!("compositionend")
806 {
807 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
808 if event.type_() == atom!("compositionend") {
809 let action = self
810 .textinput
811 .borrow_mut()
812 .handle_compositionend(compositionevent);
813 self.handle_key_reaction(action, event, CanGc::from_cx(cx));
814 self.upcast::<Node>().dirty(NodeDamage::Other);
815 } else if event.type_() == atom!("compositionupdate") {
816 let action = self
817 .textinput
818 .borrow_mut()
819 .handle_compositionupdate(compositionevent);
820 self.handle_key_reaction(action, event, CanGc::from_cx(cx));
821 self.upcast::<Node>().dirty(NodeDamage::Other);
822 }
823 self.maybe_update_shared_selection();
824 event.mark_as_handled();
825 }
826 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
827 let reaction = self
828 .textinput
829 .borrow_mut()
830 .handle_clipboard_event(clipboard_event);
831
832 let flags = reaction.flags;
833 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
834 self.owner_document().event_handler().fire_clipboard_event(
835 None,
836 ClipboardEventType::Change,
837 CanGc::from_cx(cx),
838 );
839 }
840 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
841 self.textinput.borrow().queue_input_event(
842 self.upcast(),
843 reaction.text,
844 IsComposing::NotComposing,
845 reaction.input_type,
846 );
847 }
848 if !flags.is_empty() {
849 event.mark_as_handled();
850 self.handle_text_content_changed(CanGc::from_cx(cx));
851 }
852 } else if let Some(event) = event.downcast::<FocusEvent>() {
853 self.handle_focus_event(event);
854 }
855
856 self.validity_state(CanGc::from_cx(cx))
857 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
858
859 if let Some(super_type) = self.super_type() {
860 super_type.handle_event(cx, event);
861 }
862 }
863
864 fn pop(&self) {
865 self.super_type().unwrap().pop();
866
867 self.reset(CanGc::deprecated_note());
869 }
870}
871
872impl FormControl for HTMLTextAreaElement {
873 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
874 self.form_owner.get()
875 }
876
877 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
878 self.form_owner.set(form);
879 }
880
881 fn to_element(&self) -> &Element {
882 self.upcast::<Element>()
883 }
884}
885
886impl Validatable for HTMLTextAreaElement {
887 fn as_element(&self) -> &Element {
888 self.upcast()
889 }
890
891 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
892 self.validity_state
893 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
894 }
895
896 fn is_instance_validatable(&self) -> bool {
897 !self.upcast::<Element>().disabled_state() &&
901 !self.ReadOnly() &&
902 !is_barred_by_datalist_ancestor(self.upcast())
903 }
904
905 fn perform_validation(
906 &self,
907 validate_flags: ValidationFlags,
908 _can_gc: CanGc,
909 ) -> ValidationFlags {
910 let mut failed_flags = ValidationFlags::empty();
911
912 let textinput = self.textinput.borrow();
913 let Utf16CodeUnitLength(value_len) = textinput.len_utf16();
914 let last_edit_by_user = !textinput.was_last_change_by_set_content();
915 let value_dirty = self.value_dirty.get();
916
917 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
920 self.Required() &&
921 self.is_mutable() &&
922 value_len == 0
923 {
924 failed_flags.insert(ValidationFlags::VALUE_MISSING);
925 }
926
927 if value_dirty && last_edit_by_user && value_len > 0 {
928 if validate_flags.contains(ValidationFlags::TOO_LONG) {
931 let max_length = self.MaxLength();
932 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
933 failed_flags.insert(ValidationFlags::TOO_LONG);
934 }
935 }
936
937 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
940 let min_length = self.MinLength();
941 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
942 failed_flags.insert(ValidationFlags::TOO_SHORT);
943 }
944 }
945 }
946
947 failed_flags
948 }
949}