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