1use std::default::Default;
8use std::ops::Range;
9
10use app_units::Au;
11use embedder_traits::{EmbedderMsg, MouseButton, ScriptToEmbedderChan};
12use keyboard_types::{Key, KeyState, Modifiers, NamedKey, ShortcutMatcher};
13use script_bindings::codegen::GenericBindings::UIEventBinding::UIEventMethods;
14use script_bindings::match_domstring_ascii;
15use script_bindings::root::Dom;
16use script_bindings::trace::CustomTraceable;
17use script_traits::MouseButtons;
18use servo_base::generic_channel::GenericCallback;
19use servo_base::id::WebViewId;
20use servo_base::text::{AssumeUnder4GB, RangeAny, Utf8CodeUnits, Utf16CodeUnits, Utf32CodeUnits};
21use servo_base::{Rope, RopeIndex, RopeMovement, RopeSlice};
22
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::str::DOMString;
25use crate::dom::compositionevent::CompositionEvent;
26use crate::dom::event::Event;
27use crate::dom::inputevent::HitTestResult;
28use crate::dom::keyboardevent::KeyboardEvent;
29use crate::dom::mouseevent::MouseEvent;
30use crate::dom::text_control::TextControlElement;
31use crate::dom::types::{HTMLInputElement, HTMLTextAreaElement, UIEvent};
32use crate::dom::{Element, NodeTraits};
33use crate::drag::drag_gesture::{DragGesture, DragHandler};
34
35pub trait ClipboardProvider {
38 fn get_text(&mut self) -> Result<String, String>;
40 fn set_text(&mut self, _: String);
42}
43
44#[derive(MallocSizeOf)]
45pub(crate) struct EmbedderClipboardProvider {
46 pub embedder_sender: ScriptToEmbedderChan,
47 pub webview_id: WebViewId,
48}
49
50impl ClipboardProvider for EmbedderClipboardProvider {
51 fn get_text(&mut self) -> Result<String, String> {
52 let (callback, rx) = GenericCallback::new_blocking().unwrap();
53 self.embedder_sender
54 .send(EmbedderMsg::GetClipboardText(self.webview_id, callback))
55 .unwrap();
56 rx.recv().unwrap()
57 }
58 fn set_text(&mut self, s: String) {
59 self.embedder_sender
60 .send(EmbedderMsg::SetClipboardText(self.webview_id, s))
61 .unwrap();
62 }
63}
64
65#[derive(Clone, Copy, PartialEq)]
66pub enum Selection {
67 Selected,
68 NotSelected,
69}
70
71#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
72pub enum SelectionDirection {
73 Forward,
74 Backward,
75 None,
76}
77
78impl From<DOMString> for SelectionDirection {
79 fn from(direction: DOMString) -> SelectionDirection {
80 match_domstring_ascii!(direction,
81 "forward" => SelectionDirection::Forward,
82 "backward" => SelectionDirection::Backward,
83 _ => SelectionDirection::None,
84 )
85 }
86}
87
88impl From<SelectionDirection> for DOMString {
89 fn from(direction: SelectionDirection) -> DOMString {
90 match direction {
91 SelectionDirection::Forward => DOMString::from_static("forward"),
92 SelectionDirection::Backward => DOMString::from_static("backward"),
93 SelectionDirection::None => DOMString::from_static("none"),
94 }
95 }
96}
97
98#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
99pub enum Lines {
100 Single,
101 Multiple,
102}
103
104impl Lines {
105 fn normalize(&self, contents: impl Into<String>) -> String {
106 let contents = contents.into().replace("\r\n", "\n");
107 match self {
108 Self::Multiple => {
109 contents.replace("\r", "\n")
111 },
112 Lines::Single => contents.replace(['\r', '\n'], " "),
116 }
117 }
118}
119
120#[derive(Clone, Copy, PartialEq)]
121pub(crate) struct SelectionState {
122 start: RopeIndex,
123 end: RopeIndex,
124 direction: SelectionDirection,
125}
126
127#[derive(JSTraceable, MallocSizeOf)]
129pub struct TextInput<T: ClipboardProvider> {
130 #[no_trace]
131 rope: Rope,
132
133 mode: Lines,
138
139 #[no_trace]
141 edit_point: RopeIndex,
142
143 #[no_trace]
146 selection_origin: Option<RopeIndex>,
147 selection_direction: SelectionDirection,
148
149 #[ignore_malloc_size_of = "Can't easily measure this generic type"]
150 clipboard_provider: T,
151
152 max_length: Option<Utf16CodeUnits>,
156 min_length: Option<Utf16CodeUnits>,
157
158 was_last_change_by_set_content: bool,
160
161 #[no_trace]
162 pub(crate) previous_selection_range: Range<RopeIndex>,
163 #[no_trace]
164 pub(crate) selection_for_layout: Option<RangeAny<Utf32CodeUnits>>,
165}
166
167#[derive(Clone, Copy, PartialEq)]
168pub enum IsComposing {
169 Composing,
170 NotComposing,
171}
172
173impl From<IsComposing> for bool {
174 fn from(is_composing: IsComposing) -> Self {
175 match is_composing {
176 IsComposing::Composing => true,
177 IsComposing::NotComposing => false,
178 }
179 }
180}
181
182#[derive(Clone, Copy, PartialEq)]
184pub enum InputEventType {
185 InsertText,
186 InsertLineBreak,
187 InsertFromPaste,
188 InsertCompositionText,
189 DeleteByCut,
190 DeleteContentBackward,
191 DeleteContentForward,
192 Nothing,
193}
194
195impl InputEventType {
196 pub(crate) fn as_str(&self) -> &str {
197 match *self {
198 Self::InsertText => "insertText",
199 Self::InsertLineBreak => "insertLineBreak",
200 Self::InsertFromPaste => "insertFromPaste",
201 Self::InsertCompositionText => "insertCompositionText",
202 Self::DeleteByCut => "deleteByCut",
203 Self::DeleteContentBackward => "deleteContentBackward",
204 Self::DeleteContentForward => "deleteContentForward",
205 Self::Nothing => "",
206 }
207 }
208}
209
210pub enum KeyReaction {
212 TriggerDefaultAction,
213 DispatchInput(Option<String>, IsComposing, InputEventType),
214 RedrawSelection,
215 Nothing,
216}
217
218#[derive(Clone, Copy, Eq, PartialEq)]
220pub enum Direction {
221 Forward,
222 Backward,
223}
224
225#[cfg(target_os = "macos")]
227pub(crate) const CMD_OR_CONTROL: Modifiers = Modifiers::META;
228#[cfg(not(target_os = "macos"))]
229pub(crate) const CMD_OR_CONTROL: Modifiers = Modifiers::CONTROL;
230
231impl<T: ClipboardProvider> TextInput<T> {
232 pub fn new(lines: Lines, initial: DOMString, clipboard_provider: T) -> TextInput<T> {
234 Self {
235 rope: Rope::new(initial),
236 mode: lines,
237 edit_point: Default::default(),
238 selection_origin: None,
239 clipboard_provider,
240 max_length: Default::default(),
241 min_length: Default::default(),
242 selection_direction: SelectionDirection::None,
243 was_last_change_by_set_content: true,
244 previous_selection_range: Default::default(),
245 selection_for_layout: None,
246 }
247 }
248
249 pub fn edit_point(&self) -> RopeIndex {
250 self.edit_point
251 }
252
253 pub fn selection_origin(&self) -> Option<RopeIndex> {
254 self.selection_origin
255 }
256
257 pub fn selection_origin_or_edit_point(&self) -> RopeIndex {
260 self.selection_origin.unwrap_or(self.edit_point)
261 }
262
263 pub fn selection_direction(&self) -> SelectionDirection {
264 self.selection_direction
265 }
266
267 pub fn set_max_length(&mut self, length: Option<Utf16CodeUnits>) {
268 self.max_length = length;
269 }
270
271 pub fn set_min_length(&mut self, length: Option<Utf16CodeUnits>) {
272 self.min_length = length;
273 }
274
275 pub(crate) fn was_last_change_by_set_content(&self) -> bool {
277 self.was_last_change_by_set_content
278 }
279
280 pub(crate) fn delete_selection(&mut self) -> bool {
283 if self.selection_start() == self.selection_end() {
284 return false;
285 }
286 self.replace_selection(&DOMString::new());
287 true
288 }
289
290 pub fn delete_unit_or_selection(&mut self, unit: RopeMovement, direction: Direction) -> bool {
295 if !self.has_uncollapsed_selection() {
296 let amount = match direction {
297 Direction::Forward => 1,
298 Direction::Backward => -1,
299 };
300 self.modify_selection(amount, unit);
301 }
302 self.delete_selection()
303 }
304
305 pub fn insert<S: Into<String>>(&mut self, string: S) {
308 if self.selection_origin.is_none() {
309 self.selection_origin = Some(self.edit_point);
310 }
311 self.replace_selection(&DOMString::from(string.into()));
312 }
313
314 pub fn selection_start(&self) -> RopeIndex {
317 match self.selection_direction {
318 SelectionDirection::None | SelectionDirection::Forward => {
319 self.selection_origin_or_edit_point()
320 },
321 SelectionDirection::Backward => self.edit_point,
322 }
323 }
324
325 pub(crate) fn selection_start_utf16(&self) -> Utf16CodeUnits {
326 self.rope.index_to_utf16_offset(self.selection_start())
327 }
328
329 fn selection_start_offset(&self) -> Utf8CodeUnits {
331 self.rope.index_to_utf8_offset(self.selection_start())
332 }
333
334 pub fn selection_end(&self) -> RopeIndex {
337 match self.selection_direction {
338 SelectionDirection::None | SelectionDirection::Forward => self.edit_point,
339 SelectionDirection::Backward => self.selection_origin_or_edit_point(),
340 }
341 }
342
343 pub(crate) fn selection_end_utf16(&self) -> Utf16CodeUnits {
344 self.rope.index_to_utf16_offset(self.selection_end())
345 }
346
347 #[inline]
350 pub(crate) fn has_uncollapsed_selection(&self) -> bool {
351 self.selection_origin
352 .is_some_and(|selection_origin| selection_origin != self.edit_point)
353 }
354
355 pub(crate) fn sorted_selection_character_offsets_range(&self) -> RangeAny<Utf32CodeUnits> {
362 let rope = &self.rope;
363 let start = self.selection_start();
364 let end = self.selection_end();
365 let start = (start != rope.first_index()).then(|| rope.index_to_character_offset(start));
366 let end = Some(rope.index_to_character_offset(end));
371 RangeAny::new(start, end)
372 }
373
374 pub(crate) fn selection_state(&self) -> SelectionState {
376 SelectionState {
377 start: self.selection_start(),
378 end: self.selection_end(),
379 direction: self.selection_direction,
380 }
381 }
382
383 fn assert_ok_selection(&self) {
385 debug!(
386 "edit_point: {:?}, selection_origin: {:?}, direction: {:?}",
387 self.edit_point, self.selection_origin, self.selection_direction
388 );
389
390 debug_assert_eq!(self.edit_point, self.rope.normalize_index(self.edit_point));
391 if let Some(selection_origin) = self.selection_origin {
392 debug_assert_eq!(
393 selection_origin,
394 self.rope.normalize_index(selection_origin)
395 );
396 match self.selection_direction {
397 SelectionDirection::None | SelectionDirection::Forward => {
398 debug_assert!(selection_origin <= self.edit_point)
399 },
400 SelectionDirection::Backward => debug_assert!(self.edit_point <= selection_origin),
401 }
402 }
403 }
404
405 fn selection_slice(&self) -> RopeSlice<'_> {
406 self.rope
407 .slice(Some(self.selection_start()), Some(self.selection_end()))
408 }
409
410 pub(crate) fn selection_content(&self) -> Option<String> {
411 let text: String = self.selection_slice().into();
412 if text.is_empty() {
413 return None;
414 }
415 Some(text)
416 }
417
418 fn selection_utf16_len(&self) -> Utf16CodeUnits {
420 self.selection_slice().len_utf16()
421 }
422
423 pub fn replace_selection(&mut self, insert: &DOMString) {
427 let string_to_insert = if let Some(max_length) = self.max_length {
428 let utf16_length_without_selection =
429 self.len_utf16().saturating_sub(self.selection_utf16_len());
430 let utf16_length_that_can_be_inserted =
431 max_length.saturating_sub(utf16_length_without_selection);
432 let last_char_index = usize::from(
434 utf16_length_that_can_be_inserted
435 .to_utf8_code_units_in(AssumeUnder4GB, &insert.str()),
436 );
437 &insert.str()[..last_char_index]
438 } else {
439 &insert.str()
440 };
441 let string_to_insert = self.mode.normalize(string_to_insert);
442
443 let start = self.selection_start();
444 let end = self.selection_end();
445 let end_index_of_insertion = self.rope.replace_range(start..end, string_to_insert);
446
447 self.was_last_change_by_set_content = false;
448 self.clear_selection();
449 self.edit_point = end_index_of_insertion;
450 }
451
452 pub fn modify_edit_point(&mut self, amount: isize, movement: RopeMovement) {
453 if amount == 0 {
454 return;
455 }
456
457 if matches!(movement, RopeMovement::Line) || !self.has_uncollapsed_selection() {
460 self.clear_selection();
461 self.edit_point = self.rope.move_by(self.edit_point, movement, amount);
462 return;
463 }
464
465 let new_edit_point = if amount > 0 {
468 self.selection_end()
469 } else {
470 self.selection_start()
471 };
472 self.clear_selection();
473 self.edit_point = new_edit_point;
474 }
475
476 pub fn modify_selection(&mut self, amount: isize, movement: RopeMovement) {
477 let old_edit_point = self.edit_point;
478 self.edit_point = self.rope.move_by(old_edit_point, movement, amount);
479
480 if self.selection_origin.is_none() {
481 self.selection_origin = Some(old_edit_point);
482 }
483 self.update_selection_direction();
484 }
485
486 pub fn modify_selection_or_edit_point(
487 &mut self,
488 amount: isize,
489 movement: RopeMovement,
490 select: Selection,
491 ) {
492 match select {
493 Selection::Selected => self.modify_selection(amount, movement),
494 Selection::NotSelected => self.modify_edit_point(amount, movement),
495 }
496 self.assert_ok_selection();
497 }
498
499 fn update_selection_direction(&mut self) {
504 debug!(
505 "edit_point: {:?}, selection_origin: {:?}",
506 self.edit_point, self.selection_origin
507 );
508 self.selection_direction = if Some(self.edit_point) < self.selection_origin {
509 SelectionDirection::Backward
510 } else {
511 SelectionDirection::Forward
512 }
513 }
514
515 pub fn handle_return(&mut self) -> KeyReaction {
517 match self.mode {
518 Lines::Multiple => {
519 self.insert('\n');
520 KeyReaction::DispatchInput(
521 None,
522 IsComposing::NotComposing,
523 InputEventType::InsertLineBreak,
524 )
525 },
526 Lines::Single => KeyReaction::TriggerDefaultAction,
527 }
528 }
529
530 pub fn select_all(&mut self) {
532 self.selection_origin = Some(RopeIndex::default());
533 self.edit_point = self.rope.last_index();
534 self.selection_direction = SelectionDirection::Forward;
535 self.assert_ok_selection();
536 }
537
538 pub fn clear_selection(&mut self) {
540 self.selection_origin = None;
541 self.selection_direction = SelectionDirection::None;
542 }
543
544 pub(crate) fn clear_selection_to_end(&mut self) {
546 self.clear_selection();
547 self.edit_point = self.rope.last_index();
548 }
549
550 pub(crate) fn clear_selection_to_start(&mut self) {
551 self.clear_selection();
552 self.edit_point = Default::default();
553 }
554
555 pub(crate) fn handle_keydown(&mut self, event: &KeyboardEvent) -> KeyReaction {
557 let key = event.key();
558 let mods = event.modifiers();
559 self.handle_keydown_aux(key, mods, cfg!(target_os = "macos"))
560 }
561
562 pub fn handle_keydown_aux(
565 &mut self,
566 key: Key,
567 mut mods: Modifiers,
568 macos: bool,
569 ) -> KeyReaction {
570 let maybe_select = if mods.contains(Modifiers::SHIFT) {
571 Selection::Selected
572 } else {
573 Selection::NotSelected
574 };
575
576 let alt_or_control = if macos {
577 Modifiers::ALT
578 } else {
579 Modifiers::CONTROL
580 };
581
582 mods.remove(Modifiers::SHIFT);
583 ShortcutMatcher::new(KeyState::Down, key.clone(), mods)
584 .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'B', || {
585 self.modify_selection_or_edit_point(-1, RopeMovement::Word, maybe_select);
586 KeyReaction::RedrawSelection
587 })
588 .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'F', || {
589 self.modify_selection_or_edit_point(1, RopeMovement::Word, maybe_select);
590 KeyReaction::RedrawSelection
591 })
592 .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'A', || {
593 self.modify_selection_or_edit_point(-1, RopeMovement::LineStartOrEnd, maybe_select);
594 KeyReaction::RedrawSelection
595 })
596 .shortcut(Modifiers::CONTROL | Modifiers::ALT, 'E', || {
597 self.modify_selection_or_edit_point(1, RopeMovement::LineStartOrEnd, maybe_select);
598 KeyReaction::RedrawSelection
599 })
600 .optional_shortcut(macos, Modifiers::CONTROL, 'A', || {
601 self.modify_selection_or_edit_point(-1, RopeMovement::LineStartOrEnd, maybe_select);
602 KeyReaction::RedrawSelection
603 })
604 .optional_shortcut(macos, Modifiers::CONTROL, 'E', || {
605 self.modify_selection_or_edit_point(1, RopeMovement::LineStartOrEnd, maybe_select);
606 KeyReaction::RedrawSelection
607 })
608 .shortcut(CMD_OR_CONTROL, 'A', || {
609 self.select_all();
610 KeyReaction::RedrawSelection
611 })
612 .shortcut(CMD_OR_CONTROL, 'X', || {
613 if let Some(text) = self.selection_content() {
614 self.clipboard_provider.set_text(text);
615 self.delete_selection();
616 }
617 KeyReaction::DispatchInput(
618 None,
619 IsComposing::NotComposing,
620 InputEventType::DeleteByCut,
621 )
622 })
623 .shortcut(CMD_OR_CONTROL, 'C', || {
624 if let Some(text) = self.selection_content() {
626 self.clipboard_provider.set_text(text);
627 }
628 KeyReaction::DispatchInput(None, IsComposing::NotComposing, InputEventType::Nothing)
629 })
630 .shortcut(CMD_OR_CONTROL, 'V', || {
631 if let Ok(text_content) = self.clipboard_provider.get_text() {
632 self.insert(&text_content);
633 KeyReaction::DispatchInput(
634 Some(text_content),
635 IsComposing::NotComposing,
636 InputEventType::InsertFromPaste,
637 )
638 } else {
639 KeyReaction::DispatchInput(
640 Some(String::new()),
641 IsComposing::NotComposing,
642 InputEventType::InsertFromPaste,
643 )
644 }
645 })
646 .shortcut(Modifiers::empty(), Key::Named(NamedKey::Delete), || {
647 if self.delete_unit_or_selection(RopeMovement::Grapheme, Direction::Forward) {
648 KeyReaction::DispatchInput(
649 None,
650 IsComposing::NotComposing,
651 InputEventType::DeleteContentForward,
652 )
653 } else {
654 KeyReaction::Nothing
655 }
656 })
657 .shortcut(Modifiers::empty(), Key::Named(NamedKey::Backspace), || {
658 if self.delete_unit_or_selection(RopeMovement::Grapheme, Direction::Backward) {
659 KeyReaction::DispatchInput(
660 None,
661 IsComposing::NotComposing,
662 InputEventType::DeleteContentBackward,
663 )
664 } else {
665 KeyReaction::Nothing
666 }
667 })
668 .shortcut(alt_or_control, Key::Named(NamedKey::Backspace), || {
669 if self.delete_unit_or_selection(RopeMovement::Word, Direction::Backward) {
670 KeyReaction::DispatchInput(
671 None,
672 IsComposing::NotComposing,
673 InputEventType::DeleteContentBackward,
674 )
675 } else {
676 KeyReaction::Nothing
677 }
678 })
679 .optional_shortcut(
680 macos,
681 Modifiers::META,
682 Key::Named(NamedKey::ArrowLeft),
683 || {
684 self.modify_selection_or_edit_point(
685 -1,
686 RopeMovement::LineStartOrEnd,
687 maybe_select,
688 );
689 KeyReaction::RedrawSelection
690 },
691 )
692 .optional_shortcut(
693 macos,
694 Modifiers::META,
695 Key::Named(NamedKey::ArrowRight),
696 || {
697 self.modify_selection_or_edit_point(
698 1,
699 RopeMovement::LineStartOrEnd,
700 maybe_select,
701 );
702 KeyReaction::RedrawSelection
703 },
704 )
705 .optional_shortcut(
706 macos,
707 Modifiers::META,
708 Key::Named(NamedKey::ArrowUp),
709 || {
710 self.modify_selection_or_edit_point(
711 -1,
712 RopeMovement::RopeStartOrEnd,
713 maybe_select,
714 );
715 KeyReaction::RedrawSelection
716 },
717 )
718 .optional_shortcut(
719 macos,
720 Modifiers::META,
721 Key::Named(NamedKey::ArrowDown),
722 || {
723 self.modify_selection_or_edit_point(
724 1,
725 RopeMovement::RopeStartOrEnd,
726 maybe_select,
727 );
728 KeyReaction::RedrawSelection
729 },
730 )
731 .shortcut(alt_or_control, Key::Named(NamedKey::ArrowLeft), || {
732 self.modify_selection_or_edit_point(-1, RopeMovement::Word, maybe_select);
733 KeyReaction::RedrawSelection
734 })
735 .shortcut(alt_or_control, Key::Named(NamedKey::ArrowRight), || {
736 self.modify_selection_or_edit_point(1, RopeMovement::Word, maybe_select);
737 KeyReaction::RedrawSelection
738 })
739 .shortcut(Modifiers::empty(), Key::Named(NamedKey::ArrowLeft), || {
740 self.modify_selection_or_edit_point(-1, RopeMovement::Grapheme, maybe_select);
741 KeyReaction::RedrawSelection
742 })
743 .shortcut(Modifiers::empty(), Key::Named(NamedKey::ArrowRight), || {
744 self.modify_selection_or_edit_point(1, RopeMovement::Grapheme, maybe_select);
745 KeyReaction::RedrawSelection
746 })
747 .shortcut(Modifiers::empty(), Key::Named(NamedKey::ArrowUp), || {
748 self.modify_selection_or_edit_point(-1, RopeMovement::Line, maybe_select);
749 KeyReaction::RedrawSelection
750 })
751 .shortcut(Modifiers::empty(), Key::Named(NamedKey::ArrowDown), || {
752 self.modify_selection_or_edit_point(1, RopeMovement::Line, maybe_select);
753 KeyReaction::RedrawSelection
754 })
755 .shortcut(Modifiers::empty(), Key::Named(NamedKey::Enter), || {
756 self.handle_return()
757 })
758 .optional_shortcut(
759 macos,
760 Modifiers::empty(),
761 Key::Named(NamedKey::Home),
762 || {
763 self.modify_selection_or_edit_point(
764 -1,
765 RopeMovement::RopeStartOrEnd,
766 maybe_select,
767 );
768 KeyReaction::RedrawSelection
769 },
770 )
771 .optional_shortcut(macos, Modifiers::empty(), Key::Named(NamedKey::End), || {
772 self.modify_selection_or_edit_point(1, RopeMovement::RopeStartOrEnd, maybe_select);
773 KeyReaction::RedrawSelection
774 })
775 .shortcut(Modifiers::empty(), Key::Named(NamedKey::PageUp), || {
776 self.modify_selection_or_edit_point(-28, RopeMovement::Line, maybe_select);
777 KeyReaction::RedrawSelection
778 })
779 .shortcut(Modifiers::empty(), Key::Named(NamedKey::PageDown), || {
780 self.modify_selection_or_edit_point(28, RopeMovement::Line, maybe_select);
781 KeyReaction::RedrawSelection
782 })
783 .otherwise(|| {
784 if let Key::Character(ref character) = key {
785 self.insert(character);
786 return KeyReaction::DispatchInput(
787 Some(character.to_string()),
788 IsComposing::NotComposing,
789 InputEventType::InsertText,
790 );
791 }
792 if matches!(key, Key::Named(NamedKey::Process)) {
793 return KeyReaction::DispatchInput(
794 None,
795 IsComposing::Composing,
796 InputEventType::Nothing,
797 );
798 }
799 KeyReaction::Nothing
800 })
801 .unwrap()
802 }
803
804 pub(crate) fn handle_compositionend(&mut self, event: &CompositionEvent) -> KeyReaction {
805 let insertion = event.data().str();
806 if insertion.is_empty() {
807 self.clear_selection();
808 return KeyReaction::RedrawSelection;
809 }
810
811 self.insert(insertion.to_string());
812 KeyReaction::DispatchInput(
813 Some(insertion.to_string()),
814 IsComposing::NotComposing,
815 InputEventType::InsertCompositionText,
816 )
817 }
818
819 pub(crate) fn handle_compositionupdate(&mut self, event: &CompositionEvent) -> KeyReaction {
820 let insertion = event.data().str();
821 if insertion.is_empty() {
822 return KeyReaction::Nothing;
823 }
824
825 let start = self.selection_start_offset();
826 let insertion = insertion.to_string();
827 self.insert(insertion.clone());
828 self.set_selection_range_utf8(
829 start,
830 start + event.data().len_utf8(),
831 SelectionDirection::Forward,
832 );
833 KeyReaction::DispatchInput(
834 Some(insertion),
835 IsComposing::Composing,
836 InputEventType::InsertCompositionText,
837 )
838 }
839
840 fn edit_point_for_hit_test_result(&self, hit_test_result: &HitTestResult) -> RopeIndex {
841 hit_test_result
842 .dom_position_for_selection
843 .as_ref()
844 .map(|(_, character_offset)| {
845 self.rope.move_by(
846 Default::default(),
847 RopeMovement::Character,
848 character_offset.0 as isize,
849 )
850 })
851 .unwrap_or_else(|| self.rope.last_index())
852 }
853
854 fn drag_moved(&mut self, element: &impl TextControlElement, hit_test_result: &HitTestResult) {
855 let point_in_viewport = hit_test_result.point_in_frame.map(Au::from_f32_px);
856 let element = element.as_element();
857 self.edit_point = element
858 .owner_window()
859 .text_index_query_on_node_for_event(element.upcast(), point_in_viewport)
860 .map(|(_, character_offset)| {
861 self.rope.move_by(
862 Default::default(),
863 RopeMovement::Character,
864 character_offset.0 as isize,
865 )
866 })
867 .unwrap_or_else(|| self.rope.last_index());
868
869 self.update_selection_direction();
870 }
871
872 pub(crate) fn handle_mousedown_event(
877 &mut self,
878 element: &Element,
879 mouse_event: &MouseEvent,
880 hit_test_result: &HitTestResult,
881 ) -> bool {
882 assert_eq!(mouse_event.upcast::<Event>().type_(), atom!("mousedown"));
883
884 let button = mouse_event.button();
885 let selection_changed = match mouse_event.upcast::<UIEvent>().Detail() {
886 3 if button == MouseButton::Primary => {
887 let word_boundaries = self.rope.line_boundaries(self.edit_point);
888 self.edit_point = word_boundaries.end;
889 self.selection_origin = Some(word_boundaries.start);
890 self.update_selection_direction();
891 true
892 },
893 2 if button == MouseButton::Primary => {
894 let word_boundaries = self.rope.relevant_word_boundaries(self.edit_point);
895 self.edit_point = word_boundaries.end;
896 self.selection_origin = Some(word_boundaries.start);
897 self.update_selection_direction();
898 true
899 },
900 1 if matches!(button, MouseButton::Primary | MouseButton::Auxiliary) => {
901 self.clear_selection();
902 self.edit_point = self.edit_point_for_hit_test_result(hit_test_result);
903 self.selection_origin = Some(self.edit_point);
904 self.update_selection_direction();
905 true
906 },
907 _ => {
908 false
912 },
913 };
914
915 if selection_changed && mouse_event.buttons().contains(MouseButtons::Primary) {
916 element
917 .owner_document()
918 .event_handler()
919 .install_drag_gesture(DragGesture::new(DragHandler::TextInputSelection(
920 TextInputSelectionDragHandler(Dom::from_ref(element)),
921 )));
922 }
923
924 selection_changed
925 }
926
927 pub(crate) fn is_empty(&self) -> bool {
929 self.rope.is_empty()
930 }
931
932 pub(crate) fn len_utf16(&self) -> Utf16CodeUnits {
934 self.rope.len_utf16()
935 }
936
937 pub fn get_content(&self) -> DOMString {
939 self.rope.contents().into()
940 }
941
942 pub fn set_content(&mut self, content: DOMString) {
949 self.rope = Rope::new(content.str().replace("\r\n", "\n").replace("\r", "\n"));
950 self.was_last_change_by_set_content = true;
951
952 self.edit_point = self.rope.normalize_index(self.edit_point());
953 self.selection_origin = self
954 .selection_origin
955 .map(|selection_origin| self.rope.normalize_index(selection_origin));
956 }
957
958 pub fn set_selection_range_utf16(
959 &mut self,
960 start: Utf16CodeUnits,
961 end: Utf16CodeUnits,
962 direction: SelectionDirection,
963 ) {
964 self.set_selection_range_utf8(
965 self.rope.utf16_offset_to_utf8_offset(start),
966 self.rope.utf16_offset_to_utf8_offset(end),
967 direction,
968 );
969 }
970
971 pub fn set_selection_range_utf8(
972 &mut self,
973 mut start: Utf8CodeUnits,
974 mut end: Utf8CodeUnits,
975 direction: SelectionDirection,
976 ) {
977 let text_end = self.get_content().len_utf8();
978 if end > text_end {
979 end = text_end;
980 }
981 if start > end {
982 start = end;
983 }
984
985 self.selection_direction = direction;
986
987 match direction {
988 SelectionDirection::None | SelectionDirection::Forward => {
989 self.selection_origin = Some(self.rope.utf8_offset_to_rope_index(start));
990 self.edit_point = self.rope.utf8_offset_to_rope_index(end);
991 },
992 SelectionDirection::Backward => {
993 self.selection_origin = Some(self.rope.utf8_offset_to_rope_index(end));
994 self.edit_point = self.rope.utf8_offset_to_rope_index(start);
995 },
996 }
997
998 self.assert_ok_selection();
999 }
1000}
1001
1002#[derive(JSTraceable, MallocSizeOf)]
1003#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1004pub(crate) struct TextInputSelectionDragHandler(Dom<Element>);
1005
1006impl TextInputSelectionDragHandler {
1007 pub(crate) fn still_connected(&self) -> bool {
1008 self.0.is_connected()
1009 }
1010
1011 pub(crate) fn moved(&self, hit_test_result: &HitTestResult) -> bool {
1015 if !self.0.is_connected() {
1016 return false;
1017 }
1018
1019 if let Some(input) = self.0.downcast::<HTMLInputElement>() {
1020 input.text_input_mut().drag_moved(input, hit_test_result);
1021 input.maybe_update_shared_selection();
1022 true
1023 } else if let Some(text_area) = self.0.downcast::<HTMLTextAreaElement>() {
1024 text_area
1025 .text_input_mut()
1026 .drag_moved(text_area, hit_test_result);
1027 text_area.maybe_update_shared_selection();
1028 true
1029 } else {
1030 false
1031 }
1032 }
1033}