script/dom/
textcontrol.rs1use crate::clipboard_provider::EmbedderClipboardProvider;
11use crate::dom::bindings::cell::DomRefCell;
12use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
13use crate::dom::bindings::conversions::DerivedFrom;
14use crate::dom::bindings::error::{Error, ErrorResult};
15use crate::dom::bindings::str::DOMString;
16use crate::dom::event::{EventBubbles, EventCancelable};
17use crate::dom::eventtarget::EventTarget;
18use crate::dom::node::{Node, NodeDamage, NodeTraits};
19use crate::textinput::{SelectionDirection, SelectionState, TextInput, UTF8Bytes};
20
21pub(crate) trait TextControlElement: DerivedFrom<EventTarget> + DerivedFrom<Node> {
22 fn selection_api_applies(&self) -> bool;
23 fn has_selectable_text(&self) -> bool;
24 fn has_selection(&self) -> bool;
25 fn set_dirty_value_flag(&self, value: bool);
26 fn select_all(&self);
27}
28
29pub(crate) struct TextControlSelection<'a, E: TextControlElement> {
30 element: &'a E,
31 textinput: &'a DomRefCell<TextInput<EmbedderClipboardProvider>>,
32}
33
34impl<'a, E: TextControlElement> TextControlSelection<'a, E> {
35 pub(crate) fn new(
36 element: &'a E,
37 textinput: &'a DomRefCell<TextInput<EmbedderClipboardProvider>>,
38 ) -> Self {
39 TextControlSelection { element, textinput }
40 }
41
42 pub(crate) fn dom_select(&self) {
44 if !self.element.has_selectable_text() {
47 return;
48 }
49
50 self.set_range(Some(0), Some(u32::MAX), None, None);
52 }
53
54 pub(crate) fn dom_start(&self) -> Option<u32> {
56 if !self.element.selection_api_applies() {
58 return None;
59 }
60
61 Some(self.start())
63 }
64
65 pub(crate) fn set_dom_start(&self, start: Option<u32>) -> ErrorResult {
67 if !self.element.selection_api_applies() {
69 return Err(Error::InvalidState(None));
70 }
71
72 let mut end = self.end();
74
75 if let Some(s) = start {
77 if end < s {
78 end = s;
79 }
80 }
81
82 self.set_range(start, Some(end), Some(self.direction()), None);
84 Ok(())
85 }
86
87 pub(crate) fn dom_end(&self) -> Option<u32> {
89 if !self.element.selection_api_applies() {
91 return None;
92 }
93
94 Some(self.end())
96 }
97
98 pub(crate) fn set_dom_end(&self, end: Option<u32>) -> ErrorResult {
100 if !self.element.selection_api_applies() {
102 return Err(Error::InvalidState(None));
103 }
104
105 self.set_range(Some(self.start()), end, Some(self.direction()), None);
107 Ok(())
108 }
109
110 pub(crate) fn dom_direction(&self) -> Option<DOMString> {
112 if !self.element.selection_api_applies() {
114 return None;
115 }
116
117 Some(DOMString::from(self.direction()))
118 }
119
120 pub(crate) fn set_dom_direction(&self, direction: Option<DOMString>) -> ErrorResult {
122 if !self.element.selection_api_applies() {
124 return Err(Error::InvalidState(None));
125 }
126
127 self.set_range(
129 Some(self.start()),
130 Some(self.end()),
131 direction.map(SelectionDirection::from),
132 None,
133 );
134 Ok(())
135 }
136
137 pub(crate) fn set_dom_range(
139 &self,
140 start: u32,
141 end: u32,
142 direction: Option<DOMString>,
143 ) -> ErrorResult {
144 if !self.element.selection_api_applies() {
146 return Err(Error::InvalidState(None));
147 }
148
149 self.set_range(
151 Some(start),
152 Some(end),
153 direction.map(SelectionDirection::from),
154 None,
155 );
156 Ok(())
157 }
158
159 pub(crate) fn set_dom_range_text(
161 &self,
162 replacement: DOMString,
163 start: Option<u32>,
164 end: Option<u32>,
165 selection_mode: SelectionMode,
166 ) -> ErrorResult {
167 if !self.element.selection_api_applies() {
169 return Err(Error::InvalidState(None));
170 }
171
172 self.element.set_dirty_value_flag(true);
174
175 let mut start = start.unwrap_or_else(|| self.start());
177 let mut end = end.unwrap_or_else(|| self.end());
178
179 if start > end {
181 return Err(Error::IndexSize(None));
182 }
183
184 let original_selection_state = self.textinput.borrow().selection_state();
187
188 let UTF8Bytes(content_length) = self.textinput.borrow().len_utf8();
189 let content_length = content_length as u32;
190
191 if start > content_length {
193 start = content_length;
194 }
195
196 if end > content_length {
198 end = content_length;
199 }
200
201 let mut selection_start = self.start();
203
204 let mut selection_end = self.end();
206
207 let new_length = replacement.len() as u32;
211
212 {
213 let mut textinput = self.textinput.borrow_mut();
214
215 textinput.set_selection_range(start, end, SelectionDirection::None);
217 textinput.replace_selection(replacement);
218 }
219
220 let new_end = start + new_length;
222
223 match selection_mode {
225 SelectionMode::Select => {
226 selection_start = start;
227 selection_end = new_end;
228 },
229
230 SelectionMode::Start => {
231 selection_start = start;
232 selection_end = start;
233 },
234
235 SelectionMode::End => {
236 selection_start = new_end;
237 selection_end = new_end;
238 },
239
240 SelectionMode::Preserve => {
241 let old_length = end - start;
243
244 let delta = (new_length as isize) - (old_length as isize);
246
247 if selection_start > end {
249 selection_start = ((selection_start as isize) + delta) as u32;
250 } else if selection_start > start {
251 selection_start = start;
252 }
253
254 if selection_end > end {
256 selection_end = ((selection_end as isize) + delta) as u32;
257 } else if selection_end > start {
258 selection_end = new_end;
259 }
260 },
261 }
262
263 self.set_range(
265 Some(selection_start),
266 Some(selection_end),
267 None,
268 Some(original_selection_state),
269 );
270 Ok(())
271 }
272
273 fn start(&self) -> u32 {
274 let UTF8Bytes(offset) = self.textinput.borrow().selection_start_offset();
275 offset as u32
276 }
277
278 fn end(&self) -> u32 {
279 let UTF8Bytes(offset) = self.textinput.borrow().selection_end_offset();
280 offset as u32
281 }
282
283 fn direction(&self) -> SelectionDirection {
284 self.textinput.borrow().selection_direction()
285 }
286
287 fn set_range(
289 &self,
290 start: Option<u32>,
291 end: Option<u32>,
292 direction: Option<SelectionDirection>,
293 original_selection_state: Option<SelectionState>,
294 ) {
295 let mut textinput = self.textinput.borrow_mut();
296 let original_selection_state =
297 original_selection_state.unwrap_or_else(|| textinput.selection_state());
298
299 let start = start.unwrap_or(0);
301
302 let end = end.unwrap_or(0);
304
305 textinput.set_selection_range(start, end, direction.unwrap_or(SelectionDirection::None));
307
308 if textinput.selection_state() != original_selection_state {
310 self.element
311 .owner_global()
312 .task_manager()
313 .user_interaction_task_source()
314 .queue_event(
315 self.element.upcast::<EventTarget>(),
316 atom!("select"),
317 EventBubbles::Bubbles,
318 EventCancelable::NotCancelable,
319 );
320 }
321
322 self.element.upcast::<Node>().dirty(NodeDamage::Other);
323 }
324}