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::virtualmethods::VirtualMethods;
46use crate::dom::node::{
47 BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, NodeTraits, UnbindContext,
48};
49use crate::dom::nodelist::NodeList;
50use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
51use crate::dom::types::{FocusEvent, MouseEvent};
52use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
53use crate::dom::validitystate::{ValidationFlags, ValidityState};
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 = String::from(self.Value());
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: String::from(self.Value()),
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(cx)
202 .perform_validation_and_update(cx, ValidationFlags::all());
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(
257 cx,
258 atom!("selectionchange"),
259 EventBubbles::Bubbles,
260 EventCancelable::NotCancelable,
261 EventComposed::Composed,
262 );
263 }),
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 let range_remained_equal = range == shared_selection.range;
302 if range_remained_equal && enabled == shared_selection.enabled {
303 return;
304 }
305
306 if !range_remained_equal {
307 self.schedule_a_selection_change_event();
312 }
313
314 *shared_selection = ScriptSelection {
315 range,
316 character_range: self
317 .textinput
318 .borrow()
319 .sorted_selection_character_offsets_range(),
320 enabled,
321 };
322 self.owner_window().layout().set_needs_new_display_list();
323 }
324
325 fn placeholder_text<'a>(&'a self) -> Ref<'a, DOMString> {
326 self.placeholder.borrow()
327 }
328
329 fn value_text(&self) -> DOMString {
330 self.Value()
331 }
332}
333
334impl HTMLTextAreaElementMethods<crate::DomTypeHolder> for HTMLTextAreaElement {
335 make_uint_getter!(Cols, "cols", DEFAULT_COLS);
340
341 make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);
343
344 make_getter!(DirName, "dirname");
346
347 make_setter!(SetDirName, "dirname");
349
350 make_bool_getter!(Disabled, "disabled");
352
353 make_bool_setter!(SetDisabled, "disabled");
355
356 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
358 self.form_owner()
359 }
360
361 make_getter!(Name, "name");
363
364 make_atomic_setter!(SetName, "name");
366
367 make_getter!(Placeholder, "placeholder");
369
370 make_setter!(SetPlaceholder, "placeholder");
372
373 make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH);
375
376 make_limited_int_setter!(SetMaxLength, "maxlength", DEFAULT_MAX_LENGTH);
378
379 make_int_getter!(MinLength, "minlength", DEFAULT_MIN_LENGTH);
381
382 make_limited_int_setter!(SetMinLength, "minlength", DEFAULT_MIN_LENGTH);
384
385 make_bool_getter!(ReadOnly, "readonly");
387
388 make_bool_setter!(SetReadOnly, "readonly");
390
391 make_bool_getter!(Required, "required");
393
394 make_bool_setter!(SetRequired, "required");
396
397 make_uint_getter!(Rows, "rows", DEFAULT_ROWS);
399
400 make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);
402
403 make_getter!(Wrap, "wrap");
405
406 make_setter!(SetWrap, "wrap");
408
409 fn Type(&self) -> DOMString {
411 DOMString::from("textarea")
412 }
413
414 fn DefaultValue(&self) -> DOMString {
416 self.upcast::<Node>().GetTextContent().unwrap()
417 }
418
419 fn SetDefaultValue(&self, cx: &mut JSContext, value: DOMString) {
421 self.upcast::<Node>()
422 .set_text_content_for_element(cx, Some(value));
423
424 if !self.value_dirty.get() {
427 self.reset(cx);
428 }
429 }
430
431 fn Value(&self) -> DOMString {
433 self.textinput.borrow().get_content()
434 }
435
436 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
438 let old_api_value = self.Value();
440
441 self.textinput.borrow_mut().set_content(value);
443
444 self.value_dirty.set(true);
446
447 if old_api_value != self.Value() {
452 self.textinput.borrow_mut().clear_selection_to_end();
453 self.handle_text_content_changed(cx);
454 }
455 }
456
457 fn TextLength(&self) -> u32 {
459 self.textinput.borrow().len_utf16().0 as u32
460 }
461
462 make_labels_getter!(Labels, labels_node_list);
464
465 fn Select(&self) {
467 self.selection().dom_select();
468 }
469
470 fn GetSelectionStart(&self) -> Option<u32> {
472 self.selection().dom_start().map(|start| start.0 as u32)
473 }
474
475 fn SetSelectionStart(&self, _cx: &mut JSContext, start: Option<u32>) -> ErrorResult {
477 self.selection()
478 .set_dom_start(start.map(Utf16CodeUnitLength::from))
479 }
480
481 fn GetSelectionEnd(&self) -> Option<u32> {
483 self.selection().dom_end().map(|end| end.0 as u32)
484 }
485
486 fn SetSelectionEnd(&self, _cx: &mut JSContext, end: Option<u32>) -> ErrorResult {
488 self.selection()
489 .set_dom_end(end.map(Utf16CodeUnitLength::from))
490 }
491
492 fn GetSelectionDirection(&self) -> Option<DOMString> {
494 self.selection().dom_direction()
495 }
496
497 fn SetSelectionDirection(
499 &self,
500 _cx: &mut JSContext,
501 direction: Option<DOMString>,
502 ) -> ErrorResult {
503 self.selection().set_dom_direction(direction)
504 }
505
506 fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) -> ErrorResult {
508 self.selection().set_dom_range(
509 Utf16CodeUnitLength::from(start),
510 Utf16CodeUnitLength::from(end),
511 direction,
512 )
513 }
514
515 fn SetRangeText(&self, replacement: DOMString) -> ErrorResult {
517 self.selection()
518 .set_dom_range_text(replacement, None, None, Default::default())
519 }
520
521 fn SetRangeText_(
523 &self,
524 replacement: DOMString,
525 start: u32,
526 end: u32,
527 selection_mode: SelectionMode,
528 ) -> ErrorResult {
529 self.selection().set_dom_range_text(
530 replacement,
531 Some(Utf16CodeUnitLength::from(start)),
532 Some(Utf16CodeUnitLength::from(end)),
533 selection_mode,
534 )
535 }
536
537 fn WillValidate(&self) -> bool {
539 self.is_instance_validatable()
540 }
541
542 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
544 self.validity_state(cx)
545 }
546
547 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
549 self.check_validity(cx)
550 }
551
552 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
554 self.report_validity(cx)
555 }
556
557 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
559 self.validation_message(cx)
560 }
561
562 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
564 self.validity_state(cx).set_custom_error_message(cx, error);
565 }
566}
567
568impl HTMLTextAreaElement {
569 pub(crate) fn clear(&self) {
572 self.value_dirty.set(false);
573 self.textinput.borrow_mut().set_content(DOMString::from(""));
574 }
575
576 pub(crate) fn reset(&self, cx: &mut JSContext) {
577 self.value_dirty.set(false);
579 self.textinput.borrow_mut().set_content(self.DefaultValue());
580 self.handle_text_content_changed(cx);
581 }
582
583 fn selection(&self) -> TextControlSelection<'_, Self> {
584 TextControlSelection::new(self, &self.textinput)
585 }
586
587 fn handle_key_reaction(&self, cx: &mut JSContext, action: KeyReaction, event: &Event) {
588 match action {
589 KeyReaction::TriggerDefaultAction => (),
590 KeyReaction::DispatchInput(text, is_composing, input_type) => {
591 if event.IsTrusted() {
592 self.textinput.borrow().queue_input_event(
593 self.upcast(),
594 text,
595 is_composing,
596 input_type,
597 );
598 }
599 self.value_dirty.set(true);
600 self.handle_text_content_changed(cx);
601 event.mark_as_handled();
602 },
603 KeyReaction::RedrawSelection => {
604 self.maybe_update_shared_selection();
605 event.mark_as_handled();
606 },
607 KeyReaction::Nothing => (),
608 }
609 }
610}
611
612impl VirtualMethods for HTMLTextAreaElement {
613 fn super_type(&self) -> Option<&dyn VirtualMethods> {
614 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
615 }
616
617 fn attribute_mutated(
618 &self,
619 cx: &mut JSContext,
620 attr: AttrRef<'_>,
621 mutation: AttributeMutation,
622 ) {
623 self.super_type()
624 .unwrap()
625 .attribute_mutated(cx, attr, mutation);
626 match *attr.local_name() {
627 local_name!("disabled") => {
628 let el = self.upcast::<Element>();
629 match mutation {
630 AttributeMutation::Set(..) => {
631 el.set_disabled_state(true);
632 el.set_enabled_state(false);
633
634 el.set_read_write_state(false);
635 },
636 AttributeMutation::Removed => {
637 el.set_disabled_state(false);
638 el.set_enabled_state(true);
639 el.check_ancestors_disabled_state_for_form_control();
640
641 if !el.disabled_state() && !el.read_write_state() {
642 el.set_read_write_state(true);
643 }
644 },
645 }
646 },
647 local_name!("maxlength") => match *attr.value() {
648 AttrValue::Int(_, value) => {
649 let mut textinput = self.textinput.borrow_mut();
650
651 if value < 0 {
652 textinput.set_max_length(None);
653 } else {
654 textinput.set_max_length(Some(Utf16CodeUnitLength(value as usize)))
655 }
656 },
657 _ => panic!("Expected an AttrValue::Int"),
658 },
659 local_name!("minlength") => match *attr.value() {
660 AttrValue::Int(_, value) => {
661 let mut textinput = self.textinput.borrow_mut();
662
663 if value < 0 {
664 textinput.set_min_length(None);
665 } else {
666 textinput.set_min_length(Some(Utf16CodeUnitLength(value as usize)))
667 }
668 },
669 _ => panic!("Expected an AttrValue::Int"),
670 },
671 local_name!("placeholder") => {
672 {
673 let mut placeholder = self.placeholder.borrow_mut();
674 match mutation {
675 AttributeMutation::Set(..) => {
676 let value = attr.value();
677 let value_str: &str = value.as_ref();
678 *placeholder =
679 value_str.replace("\r\n", "\n").replace('\r', "\n").into();
680 },
681 AttributeMutation::Removed => placeholder.clear(),
682 }
683 }
684 self.handle_text_content_changed(cx);
685 },
686 local_name!("readonly") => {
687 let el = self.upcast::<Element>();
688 match mutation {
689 AttributeMutation::Set(..) => {
690 el.set_read_write_state(false);
691 },
692 AttributeMutation::Removed => {
693 el.set_read_write_state(!el.disabled_state());
694 },
695 }
696 },
697 local_name!("form") => {
698 self.form_attribute_mutated(cx, mutation);
699 },
700 _ => {},
701 }
702
703 self.validity_state(cx)
704 .perform_validation_and_update(cx, ValidationFlags::all());
705 }
706
707 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
708 if let Some(s) = self.super_type() {
709 s.bind_to_tree(cx, context);
710 }
711
712 self.upcast::<Element>()
713 .check_ancestors_disabled_state_for_form_control();
714
715 self.handle_text_content_changed(cx);
716 }
717
718 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
719 match *name {
720 local_name!("cols") => AttrValue::from_limited_u32(value.into(), DEFAULT_COLS),
721 local_name!("rows") => AttrValue::from_limited_u32(value.into(), DEFAULT_ROWS),
722 local_name!("maxlength") => {
723 AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH)
724 },
725 local_name!("minlength") => {
726 AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH)
727 },
728 _ => self
729 .super_type()
730 .unwrap()
731 .parse_plain_attribute(name, value),
732 }
733 }
734
735 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
736 self.super_type().unwrap().unbind_from_tree(cx, context);
737
738 let node = self.upcast::<Node>();
739 let el = self.upcast::<Element>();
740 if node
741 .ancestors()
742 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
743 {
744 el.check_ancestors_disabled_state_for_form_control();
745 } else {
746 el.check_disabled_attribute();
747 }
748
749 self.validity_state(cx)
750 .perform_validation_and_update(cx, ValidationFlags::all());
751 }
752
753 fn cloning_steps(
756 &self,
757 cx: &mut JSContext,
758 copy: &Node,
759 maybe_doc: Option<&Document>,
760 clone_children: CloneChildrenFlag,
761 ) {
762 if let Some(s) = self.super_type() {
763 s.cloning_steps(cx, copy, maybe_doc, clone_children);
764 }
765 let el = copy.downcast::<HTMLTextAreaElement>().unwrap();
766 el.value_dirty.set(self.value_dirty.get());
767 {
768 let mut textinput = el.textinput.borrow_mut();
769 textinput.set_content(self.textinput.borrow().get_content());
770 }
771 el.validity_state(cx)
772 .perform_validation_and_update(cx, ValidationFlags::all());
773 }
774
775 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
776 if let Some(s) = self.super_type() {
777 s.children_changed(cx, mutation);
778 }
779 if !self.value_dirty.get() {
780 self.reset(cx);
781 }
782 }
783
784 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
786 if let Some(mouse_event) = event.downcast::<MouseEvent>() {
787 self.handle_mouse_event(mouse_event);
788 event.mark_as_handled();
789 } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
790 if let Some(keyboard_event) = event.downcast::<KeyboardEvent>() {
791 let action = self.textinput.borrow_mut().handle_keydown(keyboard_event);
794 self.handle_key_reaction(cx, action, event);
795 }
796 } else if event.type_() == atom!("compositionstart") ||
797 event.type_() == atom!("compositionupdate") ||
798 event.type_() == atom!("compositionend")
799 {
800 if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
801 if event.type_() == atom!("compositionend") {
802 let action = self
803 .textinput
804 .borrow_mut()
805 .handle_compositionend(compositionevent);
806 self.handle_key_reaction(cx, action, event);
807 self.upcast::<Node>().dirty(NodeDamage::Other);
808 } else if event.type_() == atom!("compositionupdate") {
809 let action = self
810 .textinput
811 .borrow_mut()
812 .handle_compositionupdate(compositionevent);
813 self.handle_key_reaction(cx, action, event);
814 self.upcast::<Node>().dirty(NodeDamage::Other);
815 }
816 self.maybe_update_shared_selection();
817 event.mark_as_handled();
818 }
819 } else if let Some(clipboard_event) = event.downcast::<ClipboardEvent>() {
820 let reaction = self
821 .textinput
822 .borrow_mut()
823 .handle_clipboard_event(clipboard_event);
824
825 let flags = reaction.flags;
826 if flags.contains(ClipboardEventFlags::FireClipboardChangedEvent) {
827 self.owner_document().event_handler().fire_clipboard_event(
828 cx,
829 None,
830 ClipboardEventType::Change,
831 );
832 }
833 if flags.contains(ClipboardEventFlags::QueueInputEvent) {
834 self.textinput.borrow().queue_input_event(
835 self.upcast(),
836 reaction.text,
837 IsComposing::NotComposing,
838 reaction.input_type,
839 );
840 }
841 if !flags.is_empty() {
842 event.mark_as_handled();
843 self.handle_text_content_changed(cx);
844 }
845 } else if let Some(event) = event.downcast::<FocusEvent>() {
846 self.handle_focus_event(event);
847 }
848
849 self.validity_state(cx)
850 .perform_validation_and_update(cx, ValidationFlags::all());
851
852 if let Some(super_type) = self.super_type() {
853 super_type.handle_event(cx, event);
854 }
855 }
856
857 fn pop(&self, cx: &mut JSContext) {
858 self.super_type().unwrap().pop(cx);
859
860 self.reset(cx);
862 }
863}
864
865impl FormControl for HTMLTextAreaElement {
866 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
867 self.form_owner.get()
868 }
869
870 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
871 self.form_owner.set(form);
872 }
873
874 fn to_html_element(&self) -> &HTMLElement {
875 self.upcast::<HTMLElement>()
876 }
877}
878
879impl Validatable for HTMLTextAreaElement {
880 fn as_element(&self) -> &Element {
881 self.upcast()
882 }
883
884 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
885 self.validity_state
886 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
887 }
888
889 fn is_instance_validatable(&self) -> bool {
890 !self.upcast::<Element>().disabled_state() &&
894 !self.ReadOnly() &&
895 !is_barred_by_datalist_ancestor(self.upcast())
896 }
897
898 fn perform_validation(
899 &self,
900 _cx: &mut JSContext,
901 validate_flags: ValidationFlags,
902 ) -> ValidationFlags {
903 let mut failed_flags = ValidationFlags::empty();
904
905 let textinput = self.textinput.borrow();
906 let Utf16CodeUnitLength(value_len) = textinput.len_utf16();
907 let last_edit_by_user = !textinput.was_last_change_by_set_content();
908 let value_dirty = self.value_dirty.get();
909
910 if validate_flags.contains(ValidationFlags::VALUE_MISSING) &&
913 self.Required() &&
914 self.is_mutable() &&
915 value_len == 0
916 {
917 failed_flags.insert(ValidationFlags::VALUE_MISSING);
918 }
919
920 if value_dirty && last_edit_by_user && value_len > 0 {
921 if validate_flags.contains(ValidationFlags::TOO_LONG) {
924 let max_length = self.MaxLength();
925 if max_length != DEFAULT_MAX_LENGTH && value_len > (max_length as usize) {
926 failed_flags.insert(ValidationFlags::TOO_LONG);
927 }
928 }
929
930 if validate_flags.contains(ValidationFlags::TOO_SHORT) {
933 let min_length = self.MinLength();
934 if min_length != DEFAULT_MIN_LENGTH && value_len < (min_length as usize) {
935 failed_flags.insert(ValidationFlags::TOO_SHORT);
936 }
937 }
938 }
939
940 failed_flags
941 }
942}