1use std::cell::{Cell, RefCell};
6use std::default::Default;
7
8use base::text::Utf16CodeUnitLength;
9use dom_struct::dom_struct;
10use embedder_traits::{EmbedderControlRequest, InputMethodRequest, InputMethodType};
11use fonts::{ByteIndex, TextByteRange};
12use html5ever::{LocalName, Prefix, local_name, ns};
13use js::context::JSContext;
14use js::rust::HandleObject;
15use layout_api::wrapper_traits::{ScriptSelection, SharedSelection};
16use script_bindings::codegen::GenericBindings::CharacterDataBinding::CharacterDataMethods;
17use script_bindings::root::Dom;
18use style::attr::AttrValue;
19use stylo_dom::ElementState;
20
21use crate::clipboard_provider::EmbedderClipboardProvider;
22use crate::dom::attr::Attr;
23use crate::dom::bindings::cell::DomRefCell;
24use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
25use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
26use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
27use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
28use crate::dom::bindings::error::ErrorResult;
29use crate::dom::bindings::inheritance::Castable;
30use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
31use crate::dom::bindings::str::DOMString;
32use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
33use crate::dom::compositionevent::CompositionEvent;
34use crate::dom::document::Document;
35use crate::dom::document_embedder_controls::ControlElement;
36use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
37use crate::dom::event::Event;
38use crate::dom::html::htmlelement::HTMLElement;
39use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
40use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
41use crate::dom::html::htmlinputelement::HTMLInputElement;
42use crate::dom::keyboardevent::KeyboardEvent;
43use crate::dom::node::{
44 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
45};
46use crate::dom::nodelist::NodeList;
47use crate::dom::text::Text;
48use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
49use crate::dom::types::{CharacterData, FocusEvent, MouseEvent};
50use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
51use crate::dom::validitystate::{ValidationFlags, ValidityState};
52use crate::dom::virtualmethods::VirtualMethods;
53use crate::script_runtime::CanGc;
54use crate::textinput::{ClipboardEventFlags, IsComposing, KeyReaction, Lines, TextInput};
55
56#[dom_struct]
57pub(crate) struct HTMLTextAreaElement {
58 htmlelement: HTMLElement,
59 #[no_trace]
60 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
61 placeholder: RefCell<String>,
62 value_dirty: Cell<bool>,
64 form_owner: MutNullableDom<HTMLFormElement>,
65 labels_node_list: MutNullableDom<NodeList>,
66 validity_state: MutNullableDom<ValidityState>,
67 shadow_node: DomRefCell<Option<Dom<Text>>>,
70 #[no_trace]
73 #[conditional_malloc_size_of]
74 shared_selection: SharedSelection,
75}
76
77pub(crate) trait LayoutHTMLTextAreaElementHelpers {
78 fn selection_for_layout(self) -> SharedSelection;
79 fn get_cols(self) -> u32;
80 fn get_rows(self) -> u32;
81}
82
83impl LayoutHTMLTextAreaElementHelpers for LayoutDom<'_, HTMLTextAreaElement> {
84 fn selection_for_layout(self) -> SharedSelection {
85 self.unsafe_get().shared_selection.clone()
86 }
87
88 fn get_cols(self) -> u32 {
89 self.upcast::<Element>()
90 .get_attr_for_layout(&ns!(), &local_name!("cols"))
91 .map_or(DEFAULT_COLS, AttrValue::as_uint)
92 }
93
94 fn get_rows(self) -> u32 {
95 self.upcast::<Element>()
96 .get_attr_for_layout(&ns!(), &local_name!("rows"))
97 .map_or(DEFAULT_ROWS, AttrValue::as_uint)
98 }
99}
100
101const DEFAULT_COLS: u32 = 20;
103
104const DEFAULT_ROWS: u32 = 2;
106
107const DEFAULT_MAX_LENGTH: i32 = -1;
108const DEFAULT_MIN_LENGTH: i32 = -1;
109
110impl HTMLTextAreaElement {
111 fn new_inherited(
112 local_name: LocalName,
113 prefix: Option<Prefix>,
114 document: &Document,
115 ) -> HTMLTextAreaElement {
116 let embedder_sender = document
117 .window()
118 .as_global_scope()
119 .script_to_embedder_chan()
120 .clone();
121 HTMLTextAreaElement {
122 htmlelement: HTMLElement::new_inherited_with_state(
123 ElementState::ENABLED | ElementState::READWRITE,
124 local_name,
125 prefix,
126 document,
127 ),
128 placeholder: Default::default(),
129 textinput: DomRefCell::new(TextInput::new(
130 Lines::Multiple,
131 DOMString::new(),
132 EmbedderClipboardProvider {
133 embedder_sender,
134 webview_id: document.webview_id(),
135 },
136 )),
137 value_dirty: Cell::new(false),
138 form_owner: Default::default(),
139 labels_node_list: Default::default(),
140 validity_state: Default::default(),
141 shadow_node: Default::default(),
142 shared_selection: Default::default(),
143 }
144 }
145
146 pub(crate) fn new(
147 local_name: LocalName,
148 prefix: Option<Prefix>,
149 document: &Document,
150 proto: Option<HandleObject>,
151 can_gc: CanGc,
152 ) -> DomRoot<HTMLTextAreaElement> {
153 Node::reflect_node_with_proto(
154 Box::new(HTMLTextAreaElement::new_inherited(
155 local_name, prefix, document,
156 )),
157 document,
158 proto,
159 can_gc,
160 )
161 }
162
163 pub(crate) fn auto_directionality(&self) -> String {
164 let value: String = self.Value().to_string();
165 HTMLInputElement::directionality_from_value(&value)
166 }
167
168 pub(crate) fn is_mutable(&self) -> bool {
170 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
173 }
174
175 fn handle_focus_event(&self, event: &FocusEvent) {
176 let event_type = event.upcast::<Event>().type_();
177 if *event_type == *"blur" {
178 self.owner_document()
179 .embedder_controls()
180 .hide_embedder_control(self.upcast());
181 } else if *event_type == *"focus" {
182 self.owner_document()
183 .embedder_controls()
184 .show_embedder_control(
185 ControlElement::Ime(DomRoot::from_ref(self.upcast())),
186 EmbedderControlRequest::InputMethod(InputMethodRequest {
187 input_method_type: InputMethodType::Text,
188 text: self.Value().to_string(),
189 insertion_point: self.GetSelectionEnd(),
190 multiline: false,
191 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
193 }),
194 None,
195 );
196 } else {
197 unreachable!("Got unexpected FocusEvent {event_type:?}");
198 }
199
200 self.maybe_update_shared_selection();
202 }
203
204 fn handle_text_content_changed(&self, can_gc: CanGc) {
205 self.validity_state(can_gc)
206 .perform_validation_and_update(ValidationFlags::all(), can_gc);
207
208 let textinput_content = self.textinput.borrow().get_content();
209 let element = self.upcast::<Element>();
210 let placeholder_shown =
211 textinput_content.is_empty() && !self.placeholder.borrow().is_empty();
212 element.set_placeholder_shown_state(placeholder_shown);
213
214 let shadow_root = element
215 .shadow_root()
216 .unwrap_or_else(|| element.attach_ua_shadow_root(true, can_gc));
217 if self.shadow_node.borrow().is_none() {
218 let shadow_node = Text::new(Default::default(), &shadow_root.owner_document(), can_gc);
219 Node::replace_all(Some(shadow_node.upcast()), shadow_root.upcast(), can_gc);
220 self.shadow_node
221 .borrow_mut()
222 .replace(shadow_node.as_traced());
223 }
224
225 let content = if placeholder_shown {
226 self.placeholder
229 .borrow()
230 .replace("\r\n", "\n")
231 .replace('\r', "\n")
232 .into()
233 } else if textinput_content.is_empty() {
234 "\u{200B}".into()
239 } else {
240 textinput_content
241 };
242
243 let shadow_node = self.shadow_node.borrow_mut();
244 let character_data = shadow_node
245 .as_ref()
246 .expect("Should have always created a node at this point.")
247 .upcast::<CharacterData>();
248 if character_data.Data() != content {
249 character_data.SetData(content);
250 self.maybe_update_shared_selection();
251 }
252 }
253
254 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
255 if mouse_event.upcast::<Event>().DefaultPrevented() {
256 return;
257 }
258
259 if self.textinput.borrow().is_empty() {
262 return;
263 }
264 let node = self.upcast();
265 if self
266 .textinput
267 .borrow_mut()
268 .handle_mouse_event(node, mouse_event)
269 {
270 self.maybe_update_shared_selection();
271 }
272 }
273}
274
275impl TextControlElement for HTMLTextAreaElement {
276 fn selection_api_applies(&self) -> bool {
277 true
278 }
279
280 fn has_selectable_text(&self) -> bool {
281 !self.textinput.borrow().get_content().is_empty()
282 }
283
284 fn has_uncollapsed_selection(&self) -> bool {
285 self.textinput.borrow().has_uncollapsed_selection()
286 }
287
288 fn set_dirty_value_flag(&self, value: bool) {
289 self.value_dirty.set(value)
290 }
291
292 fn select_all(&self) {
293 self.textinput.borrow_mut().select_all();
294 self.maybe_update_shared_selection();
295 }
296
297 fn maybe_update_shared_selection(&self) {
298 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
299 let (start, end) = (offsets.start.0, offsets.end.0);
300 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
301 let enabled = self.upcast::<Element>().focus_state();
302
303 let mut shared_selection = self.shared_selection.borrow_mut();
304 if range == shared_selection.range && enabled == shared_selection.enabled {
305 return;
306 }
307 *shared_selection = ScriptSelection {
308 range,
309 character_range: self
310 .textinput
311 .borrow()
312 .sorted_selection_character_offsets_range(),
313 enabled,
314 };
315 self.owner_window().layout().set_needs_new_display_list();
316 }
317}
318
319impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
320 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
325
326 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
328
329 make_getter!(DirName, "dirname");
331
332 make_setter!(SetDirName, "dirname");
334
335 make_bool_getter!(Disabled, "disabled");
337
338 make_bool_setter!(SetDisabled, "disabled");
340
341 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
343 self.form_owner()
344 }
345
346 make_getter!(Name, "name");
348
349 make_atomic_setter!(SetName, "name");
351
352 make_getter!(Placeholder, "placeholder");
354
355 make_setter!(SetPlaceholder, "placeholder");
357
358 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
360
361 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
363
364 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
366
367 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
369
370 make_bool_getter!(ReadOnly, "readonly");
372
373 make_bool_setter!(SetReadOnly, "readonly");
375
376 make_bool_getter!(Required, "required");
378
379 make_bool_setter!(SetRequired, "required");
381
382 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
384
385 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
387
388 make_getter!(Wrap, "wrap");
390
391 make_setter!(SetWrap, "wrap");
393
394 fn Type(&self) -> DOMString {
396 DOMString::from("textarea")
397 }
398
399 fn DefaultValue(&self) -> DOMString {
401 self.upcast::<Node>().GetTextContent().unwrap()
402 }
403
404 fn SetDefaultValue(&self, value: DOMString, can_gc: CanGc) {
406 self.upcast::<Node>()
407 .set_text_content_for_element(Some(value), can_gc);
408
409 if !self.value_dirty.get() {
412 self.reset(can_gc);
413 }
414 }
415
416 fn Value(&self) -> DOMString {
418 self.textinput.borrow().get_content()
419 }
420
421 fn SetValue(&self, value: DOMString, can_gc: CanGc) {
423 let old_api_value = self.Value();
425
426 self.textinput.borrow_mut().set_content(value);
428
429 self.value_dirty.set(true);
431
432 if old_api_value != self.Value() {
437 self.textinput.borrow_mut().clear_selection_to_end();
438 self.handle_text_content_changed(can_gc);
439 }
440 }
441
442 fn TextLength(&self) -> u32 {
444 self.textinput.borrow().len_utf16().0 as u32
445 }
446
447 make_labels_getter!(Labels, labels_node_list);
449
450 fn Select(&self) {
452 self.selection().dom_select();
453 }
454
455 fn GetSelectionStart(&self) -> Option<u32> {
457 self.selection().dom_start().map(|start| start.0 as u32)
458 }
459
460 fn SetSelectionStart(&self, start: Option<u32>) -> ErrorResult {
462 self.selection()
463 .set_dom_start(start.map(Utf16CodeUnitLength::from))
464 }
465
466 fn GetSelectionEnd(&self) -> Option<u32> {
468 self.selection().dom_end().map(|end| end.0 as u32)
469 }
470
471 fn SetSelectionEnd(&self, end: Option<u32>) -> ErrorResult {
473 self.selection()
474 .set_dom_end(end.map(Utf16CodeUnitLength::from))
475 }
476
477 fn GetSelectionDirection(&self) -> Option<DOMString> {
479 self.selection().dom_direction()
480 }
481
482 fn SetSelectionDirection(&self, direction: Option<DOMString>) -> ErrorResult {
484 self.selection().set_dom_direction(direction)
485 }
486
487 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
489 self.selection().set_dom_range(
490 Utf16CodeUnitLength::from(start),
491 Utf16CodeUnitLength::from(end),
492 direction,
493 )
494 }
495
496 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
498 self.selection()
499 .set_dom_range_text(replacement, None, None, Default::default())
500 }
501
502 fn SetRangeText_(
504 &self,
505 replacement: DOMString,
506 start: u32,
507 end: u32,
508 selection_mode: SelectionMode,
509 ) -> ErrorResult {
510 self.selection().set_dom_range_text(
511 replacement,
512 Some(Utf16CodeUnitLength::from(start)),
513 Some(Utf16CodeUnitLength::from(end)),
514 selection_mode,
515 )
516 }
517
518 fn WillValidate(&self) -> bool {
520 self.is_instance_validatable()
521 }
522
523 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
525 self.validity_state(can_gc)
526 }
527
528 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
530 self.check_validity(cx)
531 }
532
533 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
535 self.report_validity(cx)
536 }
537
538 fn ValidationMessage(&self) -> DOMString {
540 self.validation_message()
541 }
542
543 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
545 self.validity_state(can_gc).set_custom_error_message(error);
546 }
547}
548
549impl HTMLTextAreaElement {
550 pub(crate) fn clear(&self) {
553 self.value_dirty.set(false);
554 self.textinput.borrow_mut().set_content(DOMString::from(""));
555 }
556
557 pub(crate) fn reset(&self, can_gc: CanGc) {
558 self.value_dirty.set(false);
560 self.textinput.borrow_mut().set_content(self.DefaultValue());
561 self.handle_text_content_changed(can_gc);
562 }
563
564 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
565 fn selection(&self) -> TextControlSelection<'_, Self> {
566 TextControlSelection::new(self, &self.textinput)
567 }
568
569 fn handle_key_reaction(&self, action: KeyReaction, event: &Event, can_gc: CanGc) {
570 match action {
571 KeyReaction::TriggerDefaultAction => (),
572 KeyReaction::DispatchInput(text, is_composing, input_type) => {
573 if event.IsTrusted() {
574 self.textinput.borrow().queue_input_event(
575 self.upcast(),
576 text,
577 is_composing,
578 input_type,
579 );
580 }
581 self.value_dirty.set(true);
582 self.handle_text_content_changed(can_gc);
583 event.mark_as_handled();
584 },
585 KeyReaction::RedrawSelection => {
586 self.maybe_update_shared_selection();
587 event.mark_as_handled();
588 },
589 KeyReaction::Nothing => (),
590 }
591 }
592}
593
594impl VirtualMethods for HTMLTextAreaElement {
595 fn super_type(&self) -> Option<&dyn VirtualMethods> {
596 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
597 }
598
599 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
600 self.super_type()
601 .unwrap()
602 .attribute_mutated(attr, mutation, can_gc);
603 match *attr.local_name() {
604 local_name!("disabled") => {
605 let el = self.upcast::<Element>();
606 match mutation {
607 AttributeMutation::Set(..) => {
608 el.set_disabled_state(true);
609 el.set_enabled_state(false);
610
611 el.set_read_write_state(false);
612 },
613 AttributeMutation::Removed => {
614 el.set_disabled_state(false);
615 el.set_enabled_state(true);
616 el.check_ancestors_disabled_state_for_form_control();
617
618 if !el.disabled_state() && !el.read_write_state() {
619 el.set_read_write_state(true);
620 }
621 },
622 }
623 },
624 local_name!("maxlength") => match *attr.value() {
625 AttrValue::Int(_, value) => {
626 let mut textinput = self.textinput.borrow_mut();
627
628 if value < 0 {
629 textinput.set_max_length(None);
630 } else {
631 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
632 }
633 },
634 _ => panic!("Expected an AttrValue::Int"),
635 },
636 local_name!("minlength") => match *attr.value() {
637 AttrValue::Int(_, value) => {
638 let mut textinput = self.textinput.borrow_mut();
639
640 if value < 0 {
641 textinput.set_min_length(None);
642 } else {
643 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
644 }
645 },
646 _ => panic!("Expected an AttrValue::Int"),
647 },
648 local_name!("placeholder") => {
649 {
650 let mut placeholder = self.placeholder.borrow_mut();
651 placeholder.clear();
652 if let AttributeMutation::Set(..) = mutation {
653 placeholder.push_str(attr.value().as_ref());
654 }
655 }
656 self.handle_text_content_changed(can_gc);
657 },
658 local_name!("readonly") => {
659 let el = self.upcast::<Element>();
660 match mutation {
661 AttributeMutation::Set(..) => {
662 el.set_read_write_state(false);
663 },
664 AttributeMutation::Removed => {
665 el.set_read_write_state(!el.disabled_state());
666 },
667 }
668 },
669 local_name!("form") => {
670 self.form_attribute_mutated(mutation, can_gc);
671 },
672 _ => {},
673 }
674
675 self.validity_state(can_gc)
676 .perform_validation_and_update(ValidationFlags::all(), can_gc);
677 }
678
679 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
680 if let Some(s) = self.super_type() {
681 s.bind_to_tree(context, can_gc);
682 }
683
684 self.upcast::<Element>()
685 .check_ancestors_disabled_state_for_form_control();
686
687 self.handle_text_content_changed(can_gc);
688 }
689
690 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
691 match *name {
692 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
693 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
694 local_name!("maxlength") => {
695 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
696 },
697 local_name!("minlength") => {
698 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
699 },
700 _ => self
701 .super_type()
702 .unwrap()
703 .parse_plain_attribute(name, value),
704 }
705 }
706
707 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
708 self.super_type().unwrap().unbind_from_tree(context, can_gc);
709
710 let node = self.upcast::<Node>();
711 let el = self.upcast::<Element>();
712 if node
713 .ancestors()
714 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
715 {
716 el.check_ancestors_disabled_state_for_form_control();
717 } else {
718 el.check_disabled_attribute();
719 }
720
721 self.validity_state(can_gc)
722 .perform_validation_and_update(ValidationFlags::all(), can_gc);
723 }
724
725 fn cloning_steps(
728 &self,
729 cx: &mut JSContext,
730 copy: &Node,
731 maybe_doc: Option<&Document>,
732 clone_children: CloneChildrenFlag,
733 ) {
734 if let Some(s) = self.super_type() {
735 s.cloning_steps(cx, copy, maybe_doc, clone_children);
736 }
737 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
738 el.value_dirty.set(self.value_dirty.get());
739 {
740 let mut textinput = el.textinput.borrow_mut();
741 textinput.set_content(self.textinput.borrow().get_content());
742 }
743 el.validity_state(CanGc::from_cx(cx))
744 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
745 }
746
747 fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
748 if let Some(s) = self.super_type() {
749 s.children_changed(mutation, can_gc);
750 }
751 if !self.value_dirty.get() {
752 self.reset(can_gc);
753 }
754 }
755
756 fn handle_event(&self, event: &Event, can_gc: CanGc) {
758 if let Some(s) = self.super_type() {
759 s.handle_event(event, can_gc);
760 }
761
762 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
763 self.handle_mouse_event(mouse_event);
764 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
765 if let Some(kevent) = event.downcast::<KeyboardEvent>() {
766 let action = self.textinput.borrow_mut().handle_keydown(kevent);
769 self.handle_key_reaction(action, event, can_gc);
770 }
771 } else if event.type_() == atom!("keypress") && !event.DefaultPrevented() {
772 } else if event.type_() == atom!("compositionstart") ||
776 event.type_() == atom!("compositionupdate") ||
777 event.type_() == atom!("compositionend")
778 {
779 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
780 if event.type_() == atom!("compositionend") {
781 let action = self
782 .textinput
783 .borrow_mut()
784 .handle_compositionend(compositionevent);
785 self.handle_key_reaction(action, event, can_gc);
786 self.upcast::<Node>().dirty(NodeDamage::Other);
787 } else if event.type_() == atom!("compositionupdate") {
788 let action = self
789 .textinput
790 .borrow_mut()
791 .handle_compositionupdate(compositionevent);
792 self.handle_key_reaction(action, event, can_gc);
793 self.upcast::<Node>().dirty(NodeDamage::Other);
794 }
795 self.maybe_update_shared_selection();
796 event.mark_as_handled();
797 }
798 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
799 let reaction = self
800 .textinput
801 .borrow_mut()
802 .handle_clipboard_event(clipboard_event);
803
804 let flags = reaction.flags;
805 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
806 self.owner_document().event_handler().fire_clipboard_event(
807 None,
808 ClipboardEventType::Change,
809 can_gc,
810 );
811 }
812 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
813 self.textinput.borrow().queue_input_event(
814 self.upcast(),
815 reaction.text,
816 IsComposing::NotComposing,
817 reaction.input_type,
818 );
819 }
820 if !flags.is_empty() {
821 self.handle_text_content_changed(can_gc);
822 }
823 } else if let Some(event) = event.downcast::<FocusEvent>() {
824 self.handle_focus_event(event);
825 }
826
827 self.validity_state(can_gc)
828 .perform_validation_and_update(ValidationFlags::all(), can_gc);
829 }
830
831 fn pop(&self) {
832 self.super_type().unwrap().pop();
833
834 self.reset(CanGc::note());
836 }
837}
838
839impl FormControl for HTMLTextAreaElement {
840 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
841 self.form_owner.get()
842 }
843
844 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
845 self.form_owner.set(form);
846 }
847
848 fn to_element(&self) -> &Element {
849 self.upcast::<Element>()
850 }
851}
852
853impl Validatable for HTMLTextAreaElement {
854 fn as_element(&self) -> &Element {
855 self.upcast()
856 }
857
858 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
859 self.validity_state
860 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
861 }
862
863 fn is_instance_validatable(&self) -> bool {
864 !self.upcast::<Element>().disabled_state() &&
868 !self.ReadOnly() &&
869 !is_barred_by_datalist_ancestor(self.upcast())
870 }
871
872 fn perform_validation(
873 &self,
874 validate_flags: ValidationFlags,
875 _can_gc: CanGc,
876 ) -> ValidationFlags {
877 let mut failed_flags = ValidationFlags::empty();
878
879 let textinput = self.textinput.borrow();
880 let Utf16CodeUnitLength(value_len) = textinput.len_utf16();
881 let last_edit_by_user = !textinput.was_last_change_by_set_content();
882 let value_dirty = self.value_dirty.get();
883
884 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
887 self.Required() &&
888 self.is_mutable() &&
889 value_len == 0
890 {
891 failed_flags.insert(ValidationFlags::VALUE_MISSING);
892 }
893
894 if value_dirty && last_edit_by_user && value_len > 0 {
895 if validate_flags.contains(ValidationFlags::TOO_LONG) {
898 let max_length = self.MaxLength();
899 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
900 failed_flags.insert(ValidationFlags::TOO_LONG);
901 }
902 }
903
904 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
907 let min_length = self.MinLength();
908 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
909 failed_flags.insert(ValidationFlags::TOO_SHORT);
910 }
911 }
912 }
913
914 failed_flags
915 }
916}