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::wrapper_traits::{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::root::{DomRoot, LayoutDom, MutNullableDom};
29use crate::dom::bindings::str::DOMString;
30use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
31use crate::dom::compositionevent::CompositionEvent;
32use crate::dom::document::Document;
33use crate::dom::document_embedder_controls::ControlElement;
34use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
35use crate::dom::event::Event;
36use crate::dom::html::htmlelement::HTMLElement;
37use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
38use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
39use crate::dom::html::input_element::HTMLInputElement;
40use crate::dom::htmlinputelement::text_input_widget::TextInputWidget;
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::textcontrol::{TextControlElement, TextControlSelection};
47use crate::dom::types::{FocusEvent, MouseEvent};
48use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
49use crate::dom::validitystate::{ValidationFlags, ValidityState};
50use crate::dom::virtualmethods::VirtualMethods;
51use crate::script_runtime::CanGc;
52use crate::textinput::{ClipboardEventFlags, IsComposing, KeyReaction, Lines, TextInput};
53
54#[dom_struct]
55pub(crate) struct HTMLTextAreaElement {
56 htmlelement: HTMLElement,
57 #[no_trace]
58 textinput: DomRefCell<TextInput<EmbedderClipboardProvider>>,
59 placeholder: RefCell<DOMString>,
60 value_dirty: Cell<bool>,
62 form_owner: MutNullableDom<HTMLFormElement>,
63 labels_node_list: MutNullableDom<NodeList>,
64 validity_state: MutNullableDom<ValidityState>,
65 text_input_widget: DomRefCell<TextInputWidget>,
67 #[no_trace]
70 #[conditional_malloc_size_of]
71 shared_selection: SharedSelection,
72}
73
74pub(crate) trait LayoutHTMLTextAreaElementHelpers {
75 fn selection_for_layout(self) -> SharedSelection;
76 fn get_cols(self) -> u32;
77 fn get_rows(self) -> u32;
78}
79
80impl LayoutHTMLTextAreaElementHelpers for LayoutDom<'_, HTMLTextAreaElement> {
81 fn selection_for_layout(self) -> SharedSelection {
82 self.unsafe_get().shared_selection.clone()
83 }
84
85 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 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 }
141 }
142
143 pub(crate) fn new(
144 cx: &mut js::context::JSContext,
145 local_name: LocalName,
146 prefix: Option<Prefix>,
147 document: &Document,
148 proto: Option<HandleObject>,
149 ) -> DomRoot<HTMLTextAreaElement> {
150 Node::reflect_node_with_proto(
151 cx,
152 Box::new(HTMLTextAreaElement::new_inherited(
153 local_name, prefix, document,
154 )),
155 document,
156 proto,
157 )
158 }
159
160 pub(crate) fn auto_directionality(&self) -> String {
161 let value: String = self.Value().to_string();
162 HTMLInputElement::directionality_from_value(&value)
163 }
164
165 pub(crate) fn is_mutable(&self) -> bool {
167 !(self.upcast::<Element>().disabled_state() || self.ReadOnly())
170 }
171
172 fn handle_focus_event(&self, event: &FocusEvent) {
173 let event_type = event.upcast::<Event>().type_();
174 if *event_type == *"blur" {
175 self.owner_document()
176 .embedder_controls()
177 .hide_embedder_control(self.upcast());
178 } else if *event_type == *"focus" {
179 self.owner_document()
180 .embedder_controls()
181 .show_embedder_control(
182 ControlElement::Ime(DomRoot::from_ref(self.upcast())),
183 EmbedderControlRequest::InputMethod(InputMethodRequest {
184 input_method_type: InputMethodType::Text,
185 text: self.Value().to_string(),
186 insertion_point: self.GetSelectionEnd(),
187 multiline: false,
188 allow_virtual_keyboard: self.owner_window().has_sticky_activation(),
190 }),
191 None,
192 );
193 } else {
194 unreachable!("Got unexpected FocusEvent {event_type:?}");
195 }
196
197 self.maybe_update_shared_selection();
199 }
200
201 #[expect(unsafe_code)]
202 fn handle_text_content_changed(&self, _can_gc: CanGc) {
203 let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
205 let cx = &mut cx;
206
207 self.validity_state(CanGc::from_cx(cx))
208 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
209
210 let placeholder_shown =
211 self.textinput.borrow().is_empty() && !self.placeholder.borrow().is_empty();
212 self.upcast::<Element>()
213 .set_placeholder_shown_state(placeholder_shown);
214
215 self.text_input_widget.borrow().update_shadow_tree(cx, self);
216 self.text_input_widget
217 .borrow()
218 .update_placeholder_contents(cx, self);
219 self.maybe_update_shared_selection();
220 }
221
222 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
223 if mouse_event.upcast::<Event>().DefaultPrevented() {
224 return;
225 }
226
227 if self.textinput.borrow().is_empty() {
230 return;
231 }
232 let node = self.upcast();
233 if self
234 .textinput
235 .borrow_mut()
236 .handle_mouse_event(node, mouse_event)
237 {
238 self.maybe_update_shared_selection();
239 }
240 }
241}
242
243impl TextControlElement for HTMLTextAreaElement {
244 fn selection_api_applies(&self) -> bool {
245 true
246 }
247
248 fn has_selectable_text(&self) -> bool {
249 !self.textinput.borrow().get_content().is_empty()
250 }
251
252 fn has_uncollapsed_selection(&self) -> bool {
253 self.textinput.borrow().has_uncollapsed_selection()
254 }
255
256 fn set_dirty_value_flag(&self, value: bool) {
257 self.value_dirty.set(value)
258 }
259
260 fn select_all(&self) {
261 self.textinput.borrow_mut().select_all();
262 self.maybe_update_shared_selection();
263 }
264
265 fn maybe_update_shared_selection(&self) {
266 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
267 let (start, end) = (offsets.start.0, offsets.end.0);
268 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
269 let enabled = self.upcast::<Element>().focus_state();
270
271 let mut shared_selection = self.shared_selection.borrow_mut();
272 if range == shared_selection.range && enabled == shared_selection.enabled {
273 return;
274 }
275 *shared_selection = ScriptSelection {
276 range,
277 character_range: self
278 .textinput
279 .borrow()
280 .sorted_selection_character_offsets_range(),
281 enabled,
282 };
283 self.owner_window().layout().set_needs_new_display_list();
284 }
285
286 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
287 self.placeholder.borrow()
288 }
289
290 fn value_text(&self) -> DOMString {
291 self.Value()
292 }
293}
294
295impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
296 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
301
302 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
304
305 make_getter!(DirName, "dirname");
307
308 make_setter!(SetDirName, "dirname");
310
311 make_bool_getter!(Disabled, "disabled");
313
314 make_bool_setter!(SetDisabled, "disabled");
316
317 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
319 self.form_owner()
320 }
321
322 make_getter!(Name, "name");
324
325 make_atomic_setter!(SetName, "name");
327
328 make_getter!(Placeholder, "placeholder");
330
331 make_setter!(SetPlaceholder, "placeholder");
333
334 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
336
337 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
339
340 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
342
343 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
345
346 make_bool_getter!(ReadOnly, "readonly");
348
349 make_bool_setter!(SetReadOnly, "readonly");
351
352 make_bool_getter!(Required, "required");
354
355 make_bool_setter!(SetRequired, "required");
357
358 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
360
361 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
363
364 make_getter!(Wrap, "wrap");
366
367 make_setter!(SetWrap, "wrap");
369
370 fn Type(&self) -> DOMString {
372 DOMString::from("textarea")
373 }
374
375 fn DefaultValue(&self) -> DOMString {
377 self.upcast::<Node>().GetTextContent().unwrap()
378 }
379
380 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
382 self.upcast::<Node>()
383 .set_text_content_for_element(cx, Some(value));
384
385 if !self.value_dirty.get() {
388 self.reset(CanGc::from_cx(cx));
389 }
390 }
391
392 fn Value(&self) -> DOMString {
394 self.textinput.borrow().get_content()
395 }
396
397 fn SetValue(&self, value: DOMString, can_gc: CanGc) {
399 let old_api_value = self.Value();
401
402 self.textinput.borrow_mut().set_content(value);
404
405 self.value_dirty.set(true);
407
408 if old_api_value != self.Value() {
413 self.textinput.borrow_mut().clear_selection_to_end();
414 self.handle_text_content_changed(can_gc);
415 }
416 }
417
418 fn TextLength(&self) -> u32 {
420 self.textinput.borrow().len_utf16().0 as u32
421 }
422
423 make_labels_getter!(Labels, labels_node_list);
425
426 fn Select(&self) {
428 self.selection().dom_select();
429 }
430
431 fn GetSelectionStart(&self) -> Option<u32> {
433 self.selection().dom_start().map(|start| start.0 as u32)
434 }
435
436 fn SetSelectionStart(&self, start: Option<u32>) -> ErrorResult {
438 self.selection()
439 .set_dom_start(start.map(Utf16CodeUnitLength::from))
440 }
441
442 fn GetSelectionEnd(&self) -> Option<u32> {
444 self.selection().dom_end().map(|end| end.0 as u32)
445 }
446
447 fn SetSelectionEnd(&self, end: Option<u32>) -> ErrorResult {
449 self.selection()
450 .set_dom_end(end.map(Utf16CodeUnitLength::from))
451 }
452
453 fn GetSelectionDirection(&self) -> Option<DOMString> {
455 self.selection().dom_direction()
456 }
457
458 fn SetSelectionDirection(&self, direction: Option<DOMString>) -> ErrorResult {
460 self.selection().set_dom_direction(direction)
461 }
462
463 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
465 self.selection().set_dom_range(
466 Utf16CodeUnitLength::from(start),
467 Utf16CodeUnitLength::from(end),
468 direction,
469 )
470 }
471
472 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
474 self.selection()
475 .set_dom_range_text(replacement, None, None, Default::default())
476 }
477
478 fn SetRangeText_(
480 &self,
481 replacement: DOMString,
482 start: u32,
483 end: u32,
484 selection_mode: SelectionMode,
485 ) -> ErrorResult {
486 self.selection().set_dom_range_text(
487 replacement,
488 Some(Utf16CodeUnitLength::from(start)),
489 Some(Utf16CodeUnitLength::from(end)),
490 selection_mode,
491 )
492 }
493
494 fn WillValidate(&self) -> bool {
496 self.is_instance_validatable()
497 }
498
499 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
501 self.validity_state(can_gc)
502 }
503
504 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
506 self.check_validity(cx)
507 }
508
509 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
511 self.report_validity(cx)
512 }
513
514 fn ValidationMessage(&self) -> DOMString {
516 self.validation_message()
517 }
518
519 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
521 self.validity_state(can_gc).set_custom_error_message(error);
522 }
523}
524
525impl HTMLTextAreaElement {
526 pub(crate) fn clear(&self) {
529 self.value_dirty.set(false);
530 self.textinput.borrow_mut().set_content(DOMString::from(""));
531 }
532
533 pub(crate) fn reset(&self, can_gc: CanGc) {
534 self.value_dirty.set(false);
536 self.textinput.borrow_mut().set_content(self.DefaultValue());
537 self.handle_text_content_changed(can_gc);
538 }
539
540 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
541 fn selection(&self) -> TextControlSelection<'_, Self> {
542 TextControlSelection::new(self, &self.textinput)
543 }
544
545 fn handle_key_reaction(&self, action: KeyReaction, event: &Event, can_gc: CanGc) {
546 match action {
547 KeyReaction::TriggerDefaultAction => (),
548 KeyReaction::DispatchInput(text, is_composing, input_type) => {
549 if event.IsTrusted() {
550 self.textinput.borrow().queue_input_event(
551 self.upcast(),
552 text,
553 is_composing,
554 input_type,
555 );
556 }
557 self.value_dirty.set(true);
558 self.handle_text_content_changed(can_gc);
559 event.mark_as_handled();
560 },
561 KeyReaction::RedrawSelection => {
562 self.maybe_update_shared_selection();
563 event.mark_as_handled();
564 },
565 KeyReaction::Nothing => (),
566 }
567 }
568}
569
570impl VirtualMethods for HTMLTextAreaElement {
571 fn super_type(&self) -> Option<&dyn VirtualMethods> {
572 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
573 }
574
575 fn attribute_mutated(
576 &self,
577 cx: &mut js::context::JSContext,
578 attr: &Attr,
579 mutation: AttributeMutation,
580 ) {
581 self.super_type()
582 .unwrap()
583 .attribute_mutated(cx, attr, mutation);
584 match *attr.local_name() {
585 local_name!("disabled") => {
586 let el = self.upcast::<Element>();
587 match mutation {
588 AttributeMutation::Set(..) => {
589 el.set_disabled_state(true);
590 el.set_enabled_state(false);
591
592 el.set_read_write_state(false);
593 },
594 AttributeMutation::Removed => {
595 el.set_disabled_state(false);
596 el.set_enabled_state(true);
597 el.check_ancestors_disabled_state_for_form_control();
598
599 if !el.disabled_state() && !el.read_write_state() {
600 el.set_read_write_state(true);
601 }
602 },
603 }
604 },
605 local_name!("maxlength") => match *attr.value() {
606 AttrValue::Int(_, value) => {
607 let mut textinput = self.textinput.borrow_mut();
608
609 if value < 0 {
610 textinput.set_max_length(None);
611 } else {
612 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
613 }
614 },
615 _ => panic!("Expected an AttrValue::Int"),
616 },
617 local_name!("minlength") => match *attr.value() {
618 AttrValue::Int(_, value) => {
619 let mut textinput = self.textinput.borrow_mut();
620
621 if value < 0 {
622 textinput.set_min_length(None);
623 } else {
624 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
625 }
626 },
627 _ => panic!("Expected an AttrValue::Int"),
628 },
629 local_name!("placeholder") => {
630 {
631 let mut placeholder = self.placeholder.borrow_mut();
632 match mutation {
633 AttributeMutation::Set(..) => {
634 let value = attr.value();
635 let value_str: &str = value.as_ref();
636 *placeholder =
637 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
638 },
639 AttributeMutation::Removed => placeholder.clear(),
640 }
641 }
642 self.handle_text_content_changed(CanGc::from_cx(cx));
643 },
644 local_name!("readonly") => {
645 let el = self.upcast::<Element>();
646 match mutation {
647 AttributeMutation::Set(..) => {
648 el.set_read_write_state(false);
649 },
650 AttributeMutation::Removed => {
651 el.set_read_write_state(!el.disabled_state());
652 },
653 }
654 },
655 local_name!("form") => {
656 self.form_attribute_mutated(mutation, CanGc::from_cx(cx));
657 },
658 _ => {},
659 }
660
661 self.validity_state(CanGc::from_cx(cx))
662 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
663 }
664
665 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
666 if let Some(s) = self.super_type() {
667 s.bind_to_tree(cx, context);
668 }
669
670 self.upcast::<Element>()
671 .check_ancestors_disabled_state_for_form_control();
672
673 self.handle_text_content_changed(CanGc::from_cx(cx));
674 }
675
676 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
677 match *name {
678 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
679 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
680 local_name!("maxlength") => {
681 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
682 },
683 local_name!("minlength") => {
684 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
685 },
686 _ => self
687 .super_type()
688 .unwrap()
689 .parse_plain_attribute(name, value),
690 }
691 }
692
693 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
694 self.super_type().unwrap().unbind_from_tree(context, can_gc);
695
696 let node = self.upcast::<Node>();
697 let el = self.upcast::<Element>();
698 if node
699 .ancestors()
700 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
701 {
702 el.check_ancestors_disabled_state_for_form_control();
703 } else {
704 el.check_disabled_attribute();
705 }
706
707 self.validity_state(can_gc)
708 .perform_validation_and_update(ValidationFlags::all(), can_gc);
709 }
710
711 fn cloning_steps(
714 &self,
715 cx: &mut JSContext,
716 copy: &Node,
717 maybe_doc: Option<&Document>,
718 clone_children: CloneChildrenFlag,
719 ) {
720 if let Some(s) = self.super_type() {
721 s.cloning_steps(cx, copy, maybe_doc, clone_children);
722 }
723 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
724 el.value_dirty.set(self.value_dirty.get());
725 {
726 let mut textinput = el.textinput.borrow_mut();
727 textinput.set_content(self.textinput.borrow().get_content());
728 }
729 el.validity_state(CanGc::from_cx(cx))
730 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
731 }
732
733 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
734 if let Some(s) = self.super_type() {
735 s.children_changed(cx, mutation);
736 }
737 if !self.value_dirty.get() {
738 self.reset(CanGc::from_cx(cx));
739 }
740 }
741
742 fn handle_event(&self, event: &Event, can_gc: CanGc) {
744 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
745 self.handle_mouse_event(mouse_event);
746 event.mark_as_handled();
747 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
748 if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
749 let action = self.textinput.borrow_mut().handle_keydown(keyboard_event);
752 self.handle_key_reaction(action, event, can_gc);
753 }
754 } else if event.type_() == atom!("compositionstart") ||
755 event.type_() == atom!("compositionupdate") ||
756 event.type_() == atom!("compositionend")
757 {
758 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
759 if event.type_() == atom!("compositionend") {
760 let action = self
761 .textinput
762 .borrow_mut()
763 .handle_compositionend(compositionevent);
764 self.handle_key_reaction(action, event, can_gc);
765 self.upcast::<Node>().dirty(NodeDamage::Other);
766 } else if event.type_() == atom!("compositionupdate") {
767 let action = self
768 .textinput
769 .borrow_mut()
770 .handle_compositionupdate(compositionevent);
771 self.handle_key_reaction(action, event, can_gc);
772 self.upcast::<Node>().dirty(NodeDamage::Other);
773 }
774 self.maybe_update_shared_selection();
775 event.mark_as_handled();
776 }
777 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
778 let reaction = self
779 .textinput
780 .borrow_mut()
781 .handle_clipboard_event(clipboard_event);
782
783 let flags = reaction.flags;
784 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
785 self.owner_document().event_handler().fire_clipboard_event(
786 None,
787 ClipboardEventType::Change,
788 can_gc,
789 );
790 }
791 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
792 self.textinput.borrow().queue_input_event(
793 self.upcast(),
794 reaction.text,
795 IsComposing::NotComposing,
796 reaction.input_type,
797 );
798 }
799 if !flags.is_empty() {
800 event.mark_as_handled();
801 self.handle_text_content_changed(can_gc);
802 }
803 } else if let Some(event) = event.downcast::<FocusEvent>() {
804 self.handle_focus_event(event);
805 }
806
807 self.validity_state(can_gc)
808 .perform_validation_and_update(ValidationFlags::all(), can_gc);
809
810 if let Some(super_type) = self.super_type() {
811 super_type.handle_event(event, can_gc);
812 }
813 }
814
815 fn pop(&self) {
816 self.super_type().unwrap().pop();
817
818 self.reset(CanGc::note());
820 }
821}
822
823impl FormControl for HTMLTextAreaElement {
824 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
825 self.form_owner.get()
826 }
827
828 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
829 self.form_owner.set(form);
830 }
831
832 fn to_element(&self) -> &Element {
833 self.upcast::<Element>()
834 }
835}
836
837impl Validatable for HTMLTextAreaElement {
838 fn as_element(&self) -> &Element {
839 self.upcast()
840 }
841
842 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
843 self.validity_state
844 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
845 }
846
847 fn is_instance_validatable(&self) -> bool {
848 !self.upcast::<Element>().disabled_state() &&
852 !self.ReadOnly() &&
853 !is_barred_by_datalist_ancestor(self.upcast())
854 }
855
856 fn perform_validation(
857 &self,
858 validate_flags: ValidationFlags,
859 _can_gc: CanGc,
860 ) -> ValidationFlags {
861 let mut failed_flags = ValidationFlags::empty();
862
863 let textinput = self.textinput.borrow();
864 let Utf16CodeUnitLength(value_len) = textinput.len_utf16();
865 let last_edit_by_user = !textinput.was_last_change_by_set_content();
866 let value_dirty = self.value_dirty.get();
867
868 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
871 self.Required() &&
872 self.is_mutable() &&
873 value_len == 0
874 {
875 failed_flags.insert(ValidationFlags::VALUE_MISSING);
876 }
877
878 if value_dirty && last_edit_by_user && value_len > 0 {
879 if validate_flags.contains(ValidationFlags::TOO_LONG) {
882 let max_length = self.MaxLength();
883 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
884 failed_flags.insert(ValidationFlags::TOO_LONG);
885 }
886 }
887
888 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
891 let min_length = self.MinLength();
892 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
893 failed_flags.insert(ValidationFlags::TOO_SHORT);
894 }
895 }
896 }
897
898 failed_flags
899 }
900}