diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 94 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLInputElement.webidl | 8 | ||||
-rw-r--r-- | components/script/textinput.rs | 34 |
3 files changed, 129 insertions, 7 deletions
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index bda81450d3d..8cce6576890 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -68,6 +68,14 @@ enum ValueMode { Filename, } +#[derive(JSTraceable, PartialEq, Copy, Clone)] +#[derive(HeapSizeOf)] +enum SelectionDirection { + Forward, + Backward, + None +} + #[dom_struct] pub struct HTMLInputElement { htmlelement: HTMLElement, @@ -83,6 +91,8 @@ pub struct HTMLInputElement { // https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag value_dirty: Cell<bool>, + selection_direction: Cell<SelectionDirection>, + // TODO: selected files for file input } @@ -132,6 +142,7 @@ impl HTMLInputElement { textinput: DOMRefCell::new(TextInput::new(Single, DOMString::new(), chan, None)), activation_state: DOMRefCell::new(InputActivationState::new()), value_dirty: Cell::new(false), + selection_direction: Cell::new(SelectionDirection::None) } } @@ -164,6 +175,31 @@ impl HTMLInputElement { } } + // this method exists so that the functions SetSelectionStart() and SetSelectionEnd() + // don't needlessly allocate strings + fn set_selection_range(&self, start: u32, end: u32, direction: &SelectionDirection) { + let mut text_input = self.textinput.borrow_mut(); + + let mut start = start as usize; + let mut end = end as usize; + + let text_end = text_input.get_content().len(); + if start > text_end { + start = text_end; + } + if end > text_end { + end = text_end; + } + + if start >= end { + start = end; + } + + text_input.selection_begin = Some(text_input.get_text_point_for_absolute_point(start)); + text_input.edit_point = text_input.get_text_point_for_absolute_point(end); + self.selection_direction.set(*direction); + } + } pub trait LayoutHTMLInputElementHelpers { @@ -444,6 +480,64 @@ impl HTMLInputElementMethods for HTMLInputElement { self.upcast::<HTMLElement>().labels() } } + + // https://html.spec.whatwg.org/multipage/#dom-input-selectionstart + fn SelectionStart(&self) -> u32 { + let text_input = self.textinput.borrow(); + let selection_start = match text_input.selection_begin { + Some(selection_begin_point) => { + text_input.get_absolute_point_for_text_point(&selection_begin_point) + }, + None => text_input.get_absolute_insertion_point() + }; + + selection_start as u32 + } + + // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectionstart + fn SetSelectionStart(&self, start: u32) { + self.set_selection_range(start, self.SelectionEnd(), &self.selection_direction.get()); + } + + // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectionend + fn SelectionEnd(&self) -> u32 { + let text_input = self.textinput.borrow(); + text_input.get_absolute_insertion_point() as u32 + } + + // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectionend + fn SetSelectionEnd(&self, end: u32) { + self.set_selection_range(self.SelectionStart(), end, &self.selection_direction.get()); + } + + // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectiondirection + fn SelectionDirection(&self) -> DOMString { + match self.selection_direction.get() { + SelectionDirection::Forward => DOMString::from("forward"), + SelectionDirection::Backward => DOMString::from("backward"), + SelectionDirection::None => DOMString::from("none"), + } + } + + // https://html.spec.whatwg.org/multipage/#dom-textarea/input-selectiondirection + fn SetSelectionDirection(&self, direction: DOMString) { + self.SetSelectionRange(self.SelectionStart(), self.SelectionEnd(), Some(direction)); + } + + // https://html.spec.whatwg.org/multipage/#dom-textarea/input-setselectionrange + fn SetSelectionRange(&self, start: u32, end: u32, direction: Option<DOMString>) { + let selection_direction = match direction { + Some(selection_direction) => { + match &*selection_direction { + "forward" => SelectionDirection::Forward, + "backward" => SelectionDirection::Backward, + _ => SelectionDirection::None, + } + }, + None => SelectionDirection::None, + }; + self.set_selection_range(start, end, &selection_direction); + } } diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl index d213334ef83..2e1c6215f2b 100644 --- a/components/script/dom/webidls/HTMLInputElement.webidl +++ b/components/script/dom/webidls/HTMLInputElement.webidl @@ -62,13 +62,13 @@ interface HTMLInputElement : HTMLElement { readonly attribute NodeList labels; //void select(); - // attribute unsigned long selectionStart; - // attribute unsigned long selectionEnd; - // attribute DOMString selectionDirection; + attribute unsigned long selectionStart; + attribute unsigned long selectionEnd; + attribute DOMString selectionDirection; //void setRangeText(DOMString replacement); //void setRangeText(DOMString replacement, unsigned long start, unsigned long end, // optional SelectionMode selectionMode = "preserve"); - //void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction); + void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction); // also has obsolete members }; diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 02fb79e04a8..f407592a0ef 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -36,7 +36,7 @@ pub struct TextInput<T: ClipboardProvider> { /// Current cursor input point pub edit_point: TextPoint, /// Beginning of selection range with edit_point as end that can span multiple lines. - selection_begin: Option<TextPoint>, + pub selection_begin: Option<TextPoint>, /// Is this a multiline input? multiline: bool, #[ignore_heap_size_of = "Can't easily measure this generic type"] @@ -500,12 +500,40 @@ impl<T: ClipboardProvider> TextInput<T> { } pub fn get_absolute_insertion_point(&self) -> usize { + self.get_absolute_point_for_text_point(&self.edit_point) + } + + pub fn get_absolute_point_for_text_point(&self, text_point: &TextPoint) -> usize { self.lines.iter().enumerate().fold(0, |acc, (i, val)| { - if i < self.edit_point.line { + if i < text_point.line { acc + val.len() + 1 // +1 for the \n } else { acc } - }) + self.edit_point.index + }) + text_point.index + } + + pub fn get_text_point_for_absolute_point(&self, abs_point: usize) -> TextPoint { + let mut index = abs_point; + let mut line = 0; + + let last_line_idx = self.lines.len() - 1; + self.lines.iter().enumerate().fold(0, |acc, (i, val)| { + if i != last_line_idx { + let line_end = max(val.len(), 1); + let new_acc = acc + line_end; + if abs_point > new_acc && index > line_end { + index -= line_end + 1; + line += 1; + } + new_acc + } else { + acc + } + }); + + TextPoint { + line: line, index: index + } } } |