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(cx)
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() {
53 for el in value.str().chars() {
55 execute_insert_text_command(cx, document, selection, DOMString::from(el.to_string()));
56 }
57
58 return true;
60 }
61
62 if value.is_empty() {
64 return true;
65 }
66
67 if value == "\n" {
69 execute_insert_paragraph_command(cx, document, selection);
70 return true;
71 }
72
73 let mut node = active_range.start_container();
75 let mut offset = active_range.start_offset();
76
77 if offset > 0 &&
79 let Some(child) = node.children().nth((offset - 1) as usize) &&
80 child.is::<Text>()
81 {
82 node = child;
83 offset = node.len();
84 }
85
86 if let Some(child) = node.children().nth((offset) as usize) &&
88 child.is::<Text>()
89 {
90 node = child;
91 offset = 0;
92 }
93
94 let overrides = CommandName::record_current_overrides(document);
96
97 if selection.Collapse(cx, Some(&node), offset).is_err() {
99 unreachable!("Must always be able to collapse the selection");
100 }
101
102 node.canonicalize_whitespace(cx, offset, Default::default());
104
105 active_range = selection
107 .active_range(cx)
108 .expect("Must always have an active range.");
109 node = active_range.start_container();
110 offset = active_range.start_offset();
111
112 if let Some(node_as_text) = node.downcast::<Text>() {
114 if node_as_text
116 .upcast::<CharacterData>()
117 .InsertData(cx, offset, value.clone())
118 .is_err()
119 {
120 unreachable!("Must always be able to insert");
121 }
122
123 if selection.Collapse(cx, Some(&node), offset).is_err() {
125 unreachable!("Must always be able to collapse the selection.");
126 }
127
128 if selection
131 .Extend(cx, &node, offset + (value.len_utf16().0 as u32))
132 .is_err()
133 {
134 unreachable!("Must always be able to extend the selection");
135 }
136
137 active_range = selection
138 .active_range(cx)
139 .expect("Must always have an active range.");
140 }
141 else {
143 let text = document.CreateTextNode(cx, value.clone());
148 let text = text.upcast::<Node>();
149
150 if active_range.InsertNode(cx, text).is_err() {
152 unreachable!("Must always be able to insert");
153 }
154
155 if selection.Collapse(cx, Some(text), 0).is_err() {
157 unreachable!("Must always be able to collapse the selection");
158 }
159
160 if selection
163 .Extend(cx, text, value.len_utf16().0 as u32)
164 .is_err()
165 {
166 unreachable!("Must always be able to extend the selection");
167 }
168
169 active_range = selection
170 .active_range(cx)
171 .expect("Must always have an active range.");
172 }
173
174 active_range.restore_states_and_values(cx, selection, document, overrides);
176
177 active_range
179 .start_container()
180 .canonicalize_whitespace(cx, active_range.start_offset(), false);
181
182 active_range
184 .end_container()
185 .canonicalize_whitespace(cx, active_range.end_offset(), false);
186
187 if value == " " {
189 }
191
192 if selection.CollapseToEnd(cx).is_err() {
194 unreachable!("Must always be able to CollapseToEnd here.");
195 }
196
197 true
199}