From a3d77790a600021c95608d1fd54522ef5c72e1f4 Mon Sep 17 00:00:00 2001 From: Saurav Sachidanand Date: Tue, 8 Mar 2016 00:36:23 +0530 Subject: [PATCH] Implement input.setSelectionRange --- components/script/dom/htmlinputelement.rs | 94 +++++++++++++++++++ .../dom/webidls/HTMLInputElement.webidl | 8 +- components/script/textinput.rs | 34 ++++++- .../wpt/metadata/html/dom/interfaces.html.ini | 27 ------ ...tfieldselection-setSelectionRange.html.ini | 58 +----------- 5 files changed, 130 insertions(+), 91 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, + selection_direction: Cell, + // 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::().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) { + 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 { /// 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, + pub selection_begin: Option, /// Is this a multiline input? multiline: bool, #[ignore_heap_size_of = "Can't easily measure this generic type"] @@ -500,12 +500,40 @@ impl TextInput { } 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 + } } } diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index e58ea626d36..7969f4095d5 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -4644,24 +4644,12 @@ [HTMLInputElement interface: operation select()] expected: FAIL - [HTMLInputElement interface: attribute selectionStart] - expected: FAIL - - [HTMLInputElement interface: attribute selectionEnd] - expected: FAIL - - [HTMLInputElement interface: attribute selectionDirection] - expected: FAIL - [HTMLInputElement interface: operation setRangeText(DOMString)] expected: FAIL [HTMLInputElement interface: operation setRangeText(DOMString,unsigned long,unsigned long,SelectionMode)] expected: FAIL - [HTMLInputElement interface: operation setSelectionRange(unsigned long,unsigned long,DOMString)] - expected: FAIL - [HTMLInputElement interface: attribute align] expected: FAIL @@ -4770,15 +4758,6 @@ [HTMLInputElement interface: document.createElement("input") must inherit property "select" with the proper type (49)] expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "selectionStart" with the proper type (50)] - expected: FAIL - - [HTMLInputElement interface: document.createElement("input") must inherit property "selectionEnd" with the proper type (51)] - expected: FAIL - - [HTMLInputElement interface: document.createElement("input") must inherit property "selectionDirection" with the proper type (52)] - expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "setRangeText" with the proper type (53)] expected: FAIL @@ -4791,12 +4770,6 @@ [HTMLInputElement interface: calling setRangeText(DOMString,unsigned long,unsigned long,SelectionMode) on document.createElement("input") with too few arguments must throw TypeError] expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "setSelectionRange" with the proper type (55)] - expected: FAIL - - [HTMLInputElement interface: calling setSelectionRange(unsigned long,unsigned long,DOMString) on document.createElement("input") with too few arguments must throw TypeError] - expected: FAIL - [HTMLInputElement interface: document.createElement("input") must inherit property "align" with the proper type (56)] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html.ini b/tests/wpt/metadata/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html.ini index 14aa2a17ae8..b181665f0ef 100644 --- a/tests/wpt/metadata/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html.ini @@ -1,62 +1,6 @@ [textfieldselection-setSelectionRange.html] type: testharness - [input typeof(input.setSelectionRange)'] - expected: FAIL - - [input setSelectionRange return void] - expected: FAIL - - [input setSelectionRange(0,1)] - expected: FAIL - - [input setSelectionRange(0,input.value.length+1)] - expected: FAIL - - [input setSelectionRange(2,2)] - expected: FAIL - - [input setSelectionRange(2,1)] - expected: FAIL - - [input direction of setSelectionRange(0,1,"backward")] - expected: FAIL - - [input direction of setSelectionRange(0,1,"forward")] - expected: FAIL - - [input direction of setSelectionRange(0,1,"none")] - expected: FAIL - - [input direction of setSelectionRange(0,1,"hoge")] - expected: FAIL - - [input direction of setSelectionRange(0,1,"BACKWARD")] - expected: FAIL - - [input direction of setSelectionRange(0,1)] - expected: FAIL - - [input setSelectionRange("string",1)] - expected: FAIL - - [input setSelectionRange(true,1)] - expected: FAIL - - [input setSelectionRange([\],1)] - expected: FAIL - - [input setSelectionRange({},1)] - expected: FAIL - - [input setSelectionRange(NaN,1)] - expected: FAIL - - [input setSelectionRange(null,1)] - expected: FAIL - - [input setSelectionRange(undefined,1)] - expected: FAIL - + [textarea typeof(input.setSelectionRange)'] expected: FAIL