script/dom/execcommand/commands/
inserttext.rs1use js::context::JSContext;
6use script_bindings::codegen::GenericBindings::CharacterDataBinding::CharacterDataMethods;
7use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
8use script_bindings::codegen::GenericBindings::SelectionBinding::SelectionMethods;
9use script_bindings::inheritance::Castable;
10
11use crate::dom::Node;
12use crate::dom::bindings::codegen::Bindings::RangeBinding::RangeMethods;
13use crate::dom::bindings::str::DOMString;
14use crate::dom::characterdata::CharacterData;
15use crate::dom::document::Document;
16use crate::dom::execcommand::basecommand::CommandName;
17use crate::dom::execcommand::commands::insertparagraph::execute_insert_paragraph_command;
18use crate::dom::execcommand::contenteditable::selection::SelectionDeletionStripWrappers;
19use crate::dom::selection::Selection;
20use crate::dom::text::Text;
21
22pub(crate) fn execute_insert_text_command(
24 cx: &mut JSContext,
25 document: &Document,
26 selection: &Selection,
27 value: DOMString,
28) -> bool {
29 selection.delete_the_selection(
31 cx,
32 document,
33 Default::default(),
34 SelectionDeletionStripWrappers::NoStrip,
35 Default::default(),
36 );
37
38 let mut active_range = selection
40 .active_range()
41 .expect("Must always have an active range.");
42 if !active_range.start_container().is_editable_or_editing_host() {
43 return true;
44 }
45
46 if value.str().chars().nth(1).is_some() {
48 for el in value.str().chars() {
50 execute_insert_text_command(cx, document, selection, DOMString::from(el.to_string()));
51 }
52
53 return true;
55 }
56
57 if value.is_empty() {
59 return true;
60 }
61
62 if value == "\n" {
64 execute_insert_paragraph_command(cx, document, selection);
65 return true;
66 }
67
68 let mut node = active_range.start_container();
70 let mut offset = active_range.start_offset();
71
72 if offset > 0 &&
74 let Some(child) = node.children().nth((offset - 1) as usize) &&
75 child.is::<Text>()
76 {
77 node = child;
78 offset = node.len();
79 }
80
81 if let Some(child) = node.children().nth((offset) as usize) &&
83 child.is::<Text>()
84 {
85 node = child;
86 offset = 0;
87 }
88
89 let overrides = CommandName::record_current_overrides(document);
91
92 if selection.Collapse(cx, Some(&node), offset).is_err() {
94 unreachable!("Must always be able to collapse the selection");
95 }
96
97 node.canonicalize_whitespace(cx, offset, Default::default());
99
100 active_range = selection
102 .active_range()
103 .expect("Must always have an active range.");
104 node = active_range.start_container();
105 offset = active_range.start_offset();
106
107 if let Some(node_as_text) = node.downcast::<Text>() {
109 if node_as_text
111 .upcast::<CharacterData>()
112 .InsertData(cx, offset, value.clone())
113 .is_err()
114 {
115 unreachable!("Must always be able to insert");
116 }
117
118 if selection.Collapse(cx, Some(&node), offset).is_err() {
120 unreachable!("Must always be able to collapse the selection.");
121 }
122
123 if selection.Extend(cx, &node, offset + 1).is_err() {
125 unreachable!("Must always be able to extend the selection");
126 }
127
128 active_range = selection
129 .active_range()
130 .expect("Must always have an active range.");
131 }
132 else {
134 let text = document.CreateTextNode(cx, value.clone());
139 let text = text.upcast::<Node>();
140
141 if active_range.InsertNode(cx, text).is_err() {
143 unreachable!("Must always be able to insert");
144 }
145
146 if selection.Collapse(cx, Some(text), 0).is_err() {
148 unreachable!("Must always be able to collapse the selection");
149 }
150
151 if selection.Extend(cx, text, 1).is_err() {
153 unreachable!("Must always be able to extend the selection");
154 }
155
156 active_range = selection
157 .active_range()
158 .expect("Must always have an active range.");
159 }
160
161 active_range.restore_states_and_values(cx, selection, document, overrides);
163
164 active_range
166 .start_container()
167 .canonicalize_whitespace(cx, active_range.start_offset(), false);
168
169 active_range
171 .end_container()
172 .canonicalize_whitespace(cx, active_range.end_offset(), false);
173
174 if value == " " {
176 }
178
179 if selection.CollapseToEnd(cx).is_err() {
181 unreachable!("Must always be able to CollapseToEnd here.");
182 }
183
184 true
186}