1use std::cell::{Cell, Ref, RefCell};
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use embedder_traits::{EmbedderControlRequest, InputMethodRequest, InputMethodType};
10use fonts::{ByteIndex, TextByteRange};
11use html5ever::{LocalName, Prefix, local_name, ns};
12use js::context::JSContext;
13use js::rust::HandleObject;
14use layout_api::{ScriptSelection, SharedSelection};
15use script_bindings::cell::DomRefCell;
16use servo_base::text::Utf16CodeUnitLength;
17use style::attr::AttrValue;
18use stylo_dom::ElementState;
19
20use crate::clipboard_provider::EmbedderClipboardProvider;
21use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
22use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
23use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
24use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
25use crate::dom::bindings::error::ErrorResult;
26use crate::dom::bindings::inheritance::Castable;
27use crate::dom::bindings::refcounted::Trusted;
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::attributes::storage::AttrRef;
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 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 }
195
196 self.maybe_update_shared_selection();
198 }
199
200 fn handle_text_content_changed(&self, cx: &mut JSContext) {
201 self.validity_state(CanGc::from_cx(cx))
202 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
203
204 let placeholder_shown =
205 self.textinput.borrow().is_empty() && !self.placeholder.borrow().is_empty();
206 self.upcast::<Element>()
207 .set_placeholder_shown_state(placeholder_shown);
208
209 self.text_input_widget.borrow().update_shadow_tree(cx, self);
210 self.text_input_widget
211 .borrow()
212 .update_placeholder_contents(cx, self);
213 self.maybe_update_shared_selection();
214 }
215
216 fn handle_mouse_event(&self, mouse_event: &MouseEvent) {
217 if mouse_event.upcast::<Event>().DefaultPrevented() {
218 return;
219 }
220
221 if self.textinput.borrow().is_empty() {
224 return;
225 }
226 let node = self.upcast();
227 if self
228 .textinput
229 .borrow_mut()
230 .handle_mouse_event(node, mouse_event)
231 {
232 self.maybe_update_shared_selection();
233 }
234 }
235
236 fn schedule_a_selection_change_event(&self) {
238 if self.has_scheduled_selectionchange_event.get() {
240 return;
241 }
242 self.has_scheduled_selectionchange_event.set(true);
244 let this = Trusted::new(self);
246 self.owner_global()
247 .task_manager()
248 .user_interaction_task_source()
249 .queue(
250 task!(selectionchange_task_steps: move |cx| {
252 let this = this.root();
253 this.has_scheduled_selectionchange_event.set(false);
255 this.upcast::<EventTarget>().fire_event_with_params(cx,
257 atom!("selectionchange"),
258 EventBubbles::Bubbles,
259 EventCancelable::NotCancelable,
260 EventComposed::Composed,
261 );
262 }),
267 );
268 }
269}
270
271impl TextControlElement for HTMLTextAreaElement {
272 fn selection_api_applies(&self) -> bool {
273 true
274 }
275
276 fn has_selectable_text(&self) -> bool {
277 !self.textinput.borrow().get_content().is_empty()
278 }
279
280 fn has_uncollapsed_selection(&self) -> bool {
281 self.textinput.borrow().has_uncollapsed_selection()
282 }
283
284 fn set_dirty_value_flag(&self, value: bool) {
285 self.value_dirty.set(value)
286 }
287
288 fn select_all(&self) {
289 self.textinput.borrow_mut().select_all();
290 self.maybe_update_shared_selection();
291 }
292
293 fn maybe_update_shared_selection(&self) {
294 let offsets = self.textinput.borrow().sorted_selection_offsets_range();
295 let (start, end) = (offsets.start.0, offsets.end.0);
296 let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
297 let enabled = self.upcast::<Element>().focus_state();
298
299 let mut shared_selection = self.shared_selection.borrow_mut();
300 let range_remained_equal = range == shared_selection.range;
301 if range_remained_equal && enabled == shared_selection.enabled {
302 return;
303 }
304
305 if !range_remained_equal {
306 self.schedule_a_selection_change_event();
311 }
312
313 *shared_selection = ScriptSelection {
314 range,
315 character_range: self
316 .textinput
317 .borrow()
318 .sorted_selection_character_offsets_range(),
319 enabled,
320 };
321 self.owner_window().layout().set_needs_new_display_list();
322 }
323
324 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
325 self.placeholder.borrow()
326 }
327
328 fn value_text(&self) -> DOMString {
329 self.Value()
330 }
331}
332
333impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
334 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
339
340 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
342
343 make_getter!(DirName, "dirname");
345
346 make_setter!(SetDirName, "dirname");
348
349 make_bool_getter!(Disabled, "disabled");
351
352 make_bool_setter!(SetDisabled, "disabled");
354
355 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
357 self.form_owner()
358 }
359
360 make_getter!(Name, "name");
362
363 make_atomic_setter!(SetName, "name");
365
366 make_getter!(Placeholder, "placeholder");
368
369 make_setter!(SetPlaceholder, "placeholder");
371
372 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
374
375 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
377
378 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
380
381 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
383
384 make_bool_getter!(ReadOnly, "readonly");
386
387 make_bool_setter!(SetReadOnly, "readonly");
389
390 make_bool_getter!(Required, "required");
392
393 make_bool_setter!(SetRequired, "required");
395
396 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
398
399 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
401
402 make_getter!(Wrap, "wrap");
404
405 make_setter!(SetWrap, "wrap");
407
408 fn Type(&self) -> DOMString {
410 DOMString::from("textarea")
411 }
412
413 fn DefaultValue(&self) -> DOMString {
415 self.upcast::<Node>().GetTextContent().unwrap()
416 }
417
418 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
420 self.upcast::<Node>()
421 .set_text_content_for_element(cx, Some(value));
422
423 if !self.value_dirty.get() {
426 self.reset(cx);
427 }
428 }
429
430 fn Value(&self) -> DOMString {
432 self.textinput.borrow().get_content()
433 }
434
435 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
437 let old_api_value = self.Value();
439
440 self.textinput.borrow_mut().set_content(value);
442
443 self.value_dirty.set(true);
445
446 if old_api_value != self.Value() {
451 self.textinput.borrow_mut().clear_selection_to_end();
452 self.handle_text_content_changed(cx);
453 }
454 }
455
456 fn TextLength(&self) -> u32 {
458 self.textinput.borrow().len_utf16().0 as u32
459 }
460
461 make_labels_getter!(Labels, labels_node_list);
463
464 fn Select(&self) {
466 self.selection().dom_select();
467 }
468
469 fn GetSelectionStart(&self) -> Option<u32> {
471 self.selection().dom_start().map(|start| start.0 as u32)
472 }
473
474 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
476 self.selection()
477 .set_dom_start(start.map(Utf16CodeUnitLength::from))
478 }
479
480 fn GetSelectionEnd(&self) -> Option<u32> {
482 self.selection().dom_end().map(|end| end.0 as u32)
483 }
484
485 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
487 self.selection()
488 .set_dom_end(end.map(Utf16CodeUnitLength::from))
489 }
490
491 fn GetSelectionDirection(&self) -> Option<DOMString> {
493 self.selection().dom_direction()
494 }
495
496 fn SetSelectionDirection(
498 &self,
499 _cx: &mut JSContext,
500 direction: Option<DOMString>,
501 ) -> ErrorResult {
502 self.selection().set_dom_direction(direction)
503 }
504
505 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
507 self.selection().set_dom_range(
508 Utf16CodeUnitLength::from(start),
509 Utf16CodeUnitLength::from(end),
510 direction,
511 )
512 }
513
514 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
516 self.selection()
517 .set_dom_range_text(replacement, None, None, Default::default())
518 }
519
520 fn SetRangeText_(
522 &self,
523 replacement: DOMString,
524 start: u32,
525 end: u32,
526 selection_mode: SelectionMode,
527 ) -> ErrorResult {
528 self.selection().set_dom_range_text(
529 replacement,
530 Some(Utf16CodeUnitLength::from(start)),
531 Some(Utf16CodeUnitLength::from(end)),
532 selection_mode,
533 )
534 }
535
536 fn WillValidate(&self) -> bool {
538 self.is_instance_validatable()
539 }
540
541 fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
543 self.validity_state(can_gc)
544 }
545
546 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
548 self.check_validity(cx)
549 }
550
551 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
553 self.report_validity(cx)
554 }
555
556 fn ValidationMessage(&self) -> DOMString {
558 self.validation_message()
559 }
560
561 fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
563 self.validity_state(can_gc).set_custom_error_message(error);
564 }
565}
566
567impl HTMLTextAreaElement {
568 pub(crate) fn clear(&self) {
571 self.value_dirty.set(false);
572 self.textinput.borrow_mut().set_content(DOMString::from(""));
573 }
574
575 pub(crate) fn reset(&self, cx: &mut JSContext) {
576 self.value_dirty.set(false);
578 self.textinput.borrow_mut().set_content(self.DefaultValue());
579 self.handle_text_content_changed(cx);
580 }
581
582 fn selection(&self) -> TextControlSelection<'_, Self> {
583 TextControlSelection::new(self, &self.textinput)
584 }
585
586 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction, event: &Event) {
587 match action {
588 KeyReaction::TriggerDefaultAction => (),
589 KeyReaction::DispatchInput(text, is_composing, input_type) => {
590 if event.IsTrusted() {
591 self.textinput.borrow().queue_input_event(
592 self.upcast(),
593 text,
594 is_composing,
595 input_type,
596 );
597 }
598 self.value_dirty.set(true);
599 self.handle_text_content_changed(cx);
600 event.mark_as_handled();
601 },
602 KeyReaction::RedrawSelection => {
603 self.maybe_update_shared_selection();
604 event.mark_as_handled();
605 },
606 KeyReaction::Nothing => (),
607 }
608 }
609}
610
611impl VirtualMethods for HTMLTextAreaElement {
612 fn super_type(&self) -> Option<&dyn VirtualMethods> {
613 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
614 }
615
616 fn attribute_mutated(
617 &self,
618 cx: &mut JSContext,
619 attr: AttrRef<'_>,
620 mutation: AttributeMutation,
621 ) {
622 self.super_type()
623 .unwrap()
624 .attribute_mutated(cx, attr, mutation);
625 match *attr.local_name() {
626 local_name!("disabled") => {
627 let el = self.upcast::<Element>();
628 match mutation {
629 AttributeMutation::Set(..) => {
630 el.set_disabled_state(true);
631 el.set_enabled_state(false);
632
633 el.set_read_write_state(false);
634 },
635 AttributeMutation::Removed => {
636 el.set_disabled_state(false);
637 el.set_enabled_state(true);
638 el.check_ancestors_disabled_state_for_form_control();
639
640 if !el.disabled_state() && !el.read_write_state() {
641 el.set_read_write_state(true);
642 }
643 },
644 }
645 },
646 local_name!("maxlength") => match *attr.value() {
647 AttrValue::Int(_, value) => {
648 let mut textinput = self.textinput.borrow_mut();
649
650 if value < 0 {
651 textinput.set_max_length(None);
652 } else {
653 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
654 }
655 },
656 _ => panic!("Expected an AttrValue::Int"),
657 },
658 local_name!("minlength") => match *attr.value() {
659 AttrValue::Int(_, value) => {
660 let mut textinput = self.textinput.borrow_mut();
661
662 if value < 0 {
663 textinput.set_min_length(None);
664 } else {
665 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
666 }
667 },
668 _ => panic!("Expected an AttrValue::Int"),
669 },
670 local_name!("placeholder") => {
671 {
672 let mut placeholder = self.placeholder.borrow_mut();
673 match mutation {
674 AttributeMutation::Set(..) => {
675 let value = attr.value();
676 let value_str: &str = value.as_ref();
677 *placeholder =
678 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
679 },
680 AttributeMutation::Removed => placeholder.clear(),
681 }
682 }
683 self.handle_text_content_changed(cx);
684 },
685 local_name!("readonly") => {
686 let el = self.upcast::<Element>();
687 match mutation {
688 AttributeMutation::Set(..) => {
689 el.set_read_write_state(false);
690 },
691 AttributeMutation::Removed => {
692 el.set_read_write_state(!el.disabled_state());
693 },
694 }
695 },
696 local_name!("form") => {
697 self.form_attribute_mutated(mutation, CanGc::from_cx(cx));
698 },
699 _ => {},
700 }
701
702 self.validity_state(CanGc::from_cx(cx))
703 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
704 }
705
706 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
707 if let Some(s) = self.super_type() {
708 s.bind_to_tree(cx, context);
709 }
710
711 self.upcast::<Element>()
712 .check_ancestors_disabled_state_for_form_control();
713
714 self.handle_text_content_changed(cx);
715 }
716
717 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
718 match *name {
719 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
720 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
721 local_name!("maxlength") => {
722 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
723 },
724 local_name!("minlength") => {
725 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
726 },
727 _ => self
728 .super_type()
729 .unwrap()
730 .parse_plain_attribute(name, value),
731 }
732 }
733
734 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
735 self.super_type().unwrap().unbind_from_tree(cx, context);
736
737 let node = self.upcast::<Node>();
738 let el = self.upcast::<Element>();
739 if node
740 .ancestors()
741 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
742 {
743 el.check_ancestors_disabled_state_for_form_control();
744 } else {
745 el.check_disabled_attribute();
746 }
747
748 self.validity_state(CanGc::from_cx(cx))
749 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
750 }
751
752 fn cloning_steps(
755 &self,
756 cx: &mut JSContext,
757 copy: &Node,
758 maybe_doc: Option<&Document>,
759 clone_children: CloneChildrenFlag,
760 ) {
761 if let Some(s) = self.super_type() {
762 s.cloning_steps(cx, copy, maybe_doc, clone_children);
763 }
764 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
765 el.value_dirty.set(self.value_dirty.get());
766 {
767 let mut textinput = el.textinput.borrow_mut();
768 textinput.set_content(self.textinput.borrow().get_content());
769 }
770 el.validity_state(CanGc::from_cx(cx))
771 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
772 }
773
774 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
775 if let Some(s) = self.super_type() {
776 s.children_changed(cx, mutation);
777 }
778 if !self.value_dirty.get() {
779 self.reset(cx);
780 }
781 }
782
783 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
785 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
786 self.handle_mouse_event(mouse_event);
787 event.mark_as_handled();
788 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
789 if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
790 let action = self.textinput.borrow_mut().handle_keydown(keyboard_event);
793 self.handle_key_reaction(cx, action, event);
794 }
795 } else if event.type_() == atom!("compositionstart") ||
796 event.type_() == atom!("compositionupdate") ||
797 event.type_() == atom!("compositionend")
798 {
799 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
800 if event.type_() == atom!("compositionend") {
801 let action = self
802 .textinput
803 .borrow_mut()
804 .handle_compositionend(compositionevent);
805 self.handle_key_reaction(cx, action, event);
806 self.upcast::<Node>().dirty(NodeDamage::Other);
807 } else if event.type_() == atom!("compositionupdate") {
808 let action = self
809 .textinput
810 .borrow_mut()
811 .handle_compositionupdate(compositionevent);
812 self.handle_key_reaction(cx, action, event);
813 self.upcast::<Node>().dirty(NodeDamage::Other);
814 }
815 self.maybe_update_shared_selection();
816 event.mark_as_handled();
817 }
818 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
819 let reaction = self
820 .textinput
821 .borrow_mut()
822 .handle_clipboard_event(clipboard_event);
823
824 let flags = reaction.flags;
825 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
826 self.owner_document().event_handler().fire_clipboard_event(
827 cx,
828 None,
829 ClipboardEventType::Change,
830 );
831 }
832 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
833 self.textinput.borrow().queue_input_event(
834 self.upcast(),
835 reaction.text,
836 IsComposing::NotComposing,
837 reaction.input_type,
838 );
839 }
840 if !flags.is_empty() {
841 event.mark_as_handled();
842 self.handle_text_content_changed(cx);
843 }
844 } else if let Some(event) = event.downcast::<FocusEvent>() {
845 self.handle_focus_event(event);
846 }
847
848 self.validity_state(CanGc::from_cx(cx))
849 .perform_validation_and_update(ValidationFlags::all(), CanGc::from_cx(cx));
850
851 if let Some(super_type) = self.super_type() {
852 super_type.handle_event(cx, event);
853 }
854 }
855
856 fn pop(&self, cx: &mut JSContext) {
857 self.super_type().unwrap().pop(cx);
858
859 self.reset(cx);
861 }
862}
863
864impl FormControl for HTMLTextAreaElement {
865 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
866 self.form_owner.get()
867 }
868
869 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
870 self.form_owner.set(form);
871 }
872
873 fn to_element(&self) -> &Element {
874 self.upcast::<Element>()
875 }
876}
877
878impl Validatable for HTMLTextAreaElement {
879 fn as_element(&self) -> &Element {
880 self.upcast()
881 }
882
883 fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
884 self.validity_state
885 .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
886 }
887
888 fn is_instance_validatable(&self) -> bool {
889 !self.upcast::<Element>().disabled_state() &&
893 !self.ReadOnly() &&
894 !is_barred_by_datalist_ancestor(self.upcast())
895 }
896
897 fn perform_validation(
898 &self,
899 validate_flags: ValidationFlags,
900 _can_gc: CanGc,
901 ) -> ValidationFlags {
902 let mut failed_flags = ValidationFlags::empty();
903
904 let textinput = self.textinput.borrow();
905 let Utf16CodeUnitLength(value_len) = textinput.len_utf16();
906 let last_edit_by_user = !textinput.was_last_change_by_set_content();
907 let value_dirty = self.value_dirty.get();
908
909 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
912 self.Required() &&
913 self.is_mutable() &&
914 value_len == 0
915 {
916 failed_flags.insert(ValidationFlags::VALUE_MISSING);
917 }
918
919 if value_dirty && last_edit_by_user && value_len > 0 {
920 if validate_flags.contains(ValidationFlags::TOO_LONG) {
923 let max_length = self.MaxLength();
924 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
925 failed_flags.insert(ValidationFlags::TOO_LONG);
926 }
927 }
928
929 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
932 let min_length = self.MinLength();
933 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
934 failed_flags.insert(ValidationFlags::TOO_SHORT);
935 }
936 }
937 }
938
939 failed_flags
940 }
941}