diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index b0fbfb36f365..05ca796abd28 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -48,7 +48,6 @@ use script_traits::ScriptToConstellationChan; use servo_atoms::Atom; use std::borrow::ToOwned; use std::cell::Cell; -use std::mem; use std::ops::Range; use style::attr::AttrValue; use style::element_state::ElementState; @@ -990,42 +989,34 @@ impl HTMLInputElement { } // https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm - fn sanitize_value(&self) { + fn sanitize_value(&self, value: &mut DOMString) { match self.input_type() { InputType::Text | InputType::Search | InputType::Tel | InputType::Password => { - self.textinput.borrow_mut().single_line_content_mut().strip_newlines(); + value.strip_newlines(); } InputType::Url => { - let mut textinput = self.textinput.borrow_mut(); - let content = textinput.single_line_content_mut(); - content.strip_newlines(); - content.strip_leading_and_trailing_ascii_whitespace(); + value.strip_newlines(); + value.strip_leading_and_trailing_ascii_whitespace(); } InputType::Date => { - let mut textinput = self.textinput.borrow_mut(); - if !textinput.single_line_content().is_valid_date_string() { - textinput.single_line_content_mut().clear(); + if !value.is_valid_date_string() { + value.clear(); } } InputType::Month => { - let mut textinput = self.textinput.borrow_mut(); - if !textinput.single_line_content().is_valid_month_string() { - textinput.single_line_content_mut().clear(); + if !value.is_valid_month_string() { + value.clear(); } } InputType::Week => { - let mut textinput = self.textinput.borrow_mut(); - if !textinput.single_line_content().is_valid_week_string() { - textinput.single_line_content_mut().clear(); + if !value.is_valid_week_string() { + value.clear(); } } InputType::Color => { - let mut textinput = self.textinput.borrow_mut(); - let is_valid = { - let content = textinput.single_line_content(); - let mut chars = content.chars(); - if content.len() == 7 && chars.next() == Some('#') { + let mut chars = value.chars(); + if value.len() == 7 && chars.next() == Some('#') { chars.all(|c| c.is_digit(16)) } else { false @@ -1033,38 +1024,29 @@ impl HTMLInputElement { }; if is_valid { - let content = textinput.single_line_content_mut(); - content.make_ascii_lowercase(); + value.make_ascii_lowercase(); } else { - textinput.set_content("#000000".into(), true); + *value = "#000000".into(); } } InputType::Time => { - let mut textinput = self.textinput.borrow_mut(); - - if !textinput.single_line_content().is_valid_time_string() { - textinput.single_line_content_mut().clear(); + if !value.is_valid_time_string() { + value.clear(); } } InputType::DatetimeLocal => { - let mut textinput = self.textinput.borrow_mut(); - if textinput.single_line_content_mut() - .convert_valid_normalized_local_date_and_time_string().is_err() { - textinput.single_line_content_mut().clear(); + if value.convert_valid_normalized_local_date_and_time_string().is_err() { + value.clear(); } } InputType::Number => { - let mut textinput = self.textinput.borrow_mut(); - if !textinput.single_line_content().is_valid_floating_point_number_string() { - textinput.single_line_content_mut().clear(); + if !value.is_valid_floating_point_number_string() { + value.clear(); } } // https://html.spec.whatwg.org/multipage/#range-state-(type=range):value-sanitization-algorithm InputType::Range => { - self.textinput - .borrow_mut() - .single_line_content_mut() - .set_best_representation_of_the_floating_point_number(); + value.set_best_representation_of_the_floating_point_number(); } _ => () } @@ -1075,20 +1057,23 @@ impl HTMLInputElement { TextControlSelection::new(&self, &self.textinput) } - fn update_text_contents(&self, value: DOMString, update_text_cursor: bool) -> ErrorResult { + fn update_text_contents(&self, mut value: DOMString, update_text_cursor: bool) -> ErrorResult { match self.value_mode() { ValueMode::Value => { - // Steps 1-2. - let old_value = mem::replace(self.textinput.borrow_mut().single_line_content_mut(), value); // Step 3. self.value_dirty.set(true); + // Step 4. - if update_text_cursor { - self.sanitize_value(); - } - // Step 5. - if *self.textinput.borrow().single_line_content() != old_value { - self.textinput.borrow_mut().clear_selection_to_limit(Direction::Forward, update_text_cursor); + self.sanitize_value(&mut value); + + let mut textinput = self.textinput.borrow_mut(); + + if *textinput.single_line_content() != value { + // Steps 1-2 + textinput.set_content(value, update_text_cursor); + + // Step 5. + textinput.clear_selection_to_limit(Direction::Forward, update_text_cursor); } } ValueMode::Default | @@ -1215,11 +1200,14 @@ impl VirtualMethods for HTMLInputElement { } // Step 6 - self.sanitize_value(); + let mut textinput = self.textinput.borrow_mut(); + let mut value = textinput.single_line_content().clone(); + self.sanitize_value(&mut value); + textinput.set_content(value, true); // Steps 7-9 if !previously_selectable && self.selection_api_applies() { - self.textinput.borrow_mut().clear_selection_to_limit(Direction::Backward, true); + textinput.clear_selection_to_limit(Direction::Backward, true); } }, AttributeMutation::Removed => { @@ -1240,9 +1228,10 @@ impl VirtualMethods for HTMLInputElement { }, &local_name!("value") if !self.value_dirty.get() => { let value = mutation.new_value(attr).map(|value| (**value).to_owned()); - self.textinput.borrow_mut().set_content( - value.map_or(DOMString::new(), DOMString::from), true); - self.sanitize_value(); + let mut value = value.map_or(DOMString::new(), DOMString::from); + + self.sanitize_value(&mut value); + self.textinput.borrow_mut().set_content(value, true); self.update_placeholder_shown_state(); }, &local_name!("name") if self.input_type() == InputType::Radio => { @@ -1252,10 +1241,12 @@ impl VirtualMethods for HTMLInputElement { &local_name!("maxlength") => { match *attr.value() { AttrValue::Int(_, value) => { + let mut textinput = self.textinput.borrow_mut(); + if value < 0 { - self.textinput.borrow_mut().max_length = None + textinput.set_max_length(None); } else { - self.textinput.borrow_mut().max_length = Some(value as usize) + textinput.set_max_length(Some(value as usize)) } }, _ => panic!("Expected an AttrValue::Int"), @@ -1264,10 +1255,12 @@ impl VirtualMethods for HTMLInputElement { &local_name!("minlength") => { match *attr.value() { AttrValue::Int(_, value) => { + let mut textinput = self.textinput.borrow_mut(); + if value < 0 { - self.textinput.borrow_mut().min_length = None + textinput.set_min_length(None); } else { - self.textinput.borrow_mut().min_length = Some(value as usize) + textinput.set_min_length(Some(value as usize)) } }, _ => panic!("Expected an AttrValue::Int"), diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index a50389185173..a6899b05a06f 100755 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -323,7 +323,6 @@ impl HTMLTextAreaElement { // Step 1 let old_value = textinput.get_content(); - let old_selection = textinput.selection_origin; // Step 2 textinput.set_content(value, update_text_cursor); @@ -334,8 +333,6 @@ impl HTMLTextAreaElement { if old_value != textinput.get_content() { // Step 4 textinput.clear_selection_to_limit(Direction::Forward, update_text_cursor); - } else { - textinput.selection_origin = old_selection; } self.upcast::().dirty(NodeDamage::OtherNodeDamage); diff --git a/components/script/dom/textcontrol.rs b/components/script/dom/textcontrol.rs index b47167ae9377..fa6c8d0b6043 100644 --- a/components/script/dom/textcontrol.rs +++ b/components/script/dom/textcontrol.rs @@ -257,7 +257,7 @@ impl<'a, E: TextControlElement> TextControlSelection<'a, E> { } fn direction(&self) -> SelectionDirection { - self.textinput.borrow().selection_direction + self.textinput.borrow().selection_direction() } // https://html.spec.whatwg.org/multipage/#set-the-selection-range diff --git a/components/script/textinput.rs b/components/script/textinput.rs index a4a5d5e89658..a61799dadb09 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -56,6 +56,18 @@ pub struct TextPoint { pub index: usize, } +impl TextPoint { + /// Returns a TextPoint constrained to be a valid location within lines + fn constrain_to(&self, lines: &[DOMString]) -> TextPoint { + let line = min(self.line, lines.len() - 1); + + TextPoint { + line, + index: min(self.index, lines[line].len()), + } + } +} + #[derive(Clone, Copy, PartialEq)] pub struct SelectionState { start: TextPoint, @@ -68,21 +80,26 @@ pub struct SelectionState { pub struct TextInput { /// Current text input content, split across lines without trailing '\n' lines: Vec, + /// Current cursor input point - pub edit_point: TextPoint, + edit_point: TextPoint, + /// The current selection goes from the selection_origin until the edit_point. Note that the /// selection_origin may be after the edit_point, in the case of a backward selection. - pub selection_origin: Option, + selection_origin: Option, + selection_direction: SelectionDirection, + /// Is this a multiline input? multiline: bool, + #[ignore_malloc_size_of = "Can't easily measure this generic type"] clipboard_provider: T, + /// The maximum number of UTF-16 code units this text input is allowed to hold. /// /// - pub max_length: Option, - pub min_length: Option, - pub selection_direction: SelectionDirection, + max_length: Option, + min_length: Option, } /// Resulting action to be taken by the owner of a text input that is handling an event. @@ -175,6 +192,32 @@ impl TextInput { i } + pub fn edit_point(&self) -> TextPoint { + self.edit_point + } + + pub fn selection_origin(&self) -> Option { + self.selection_origin + } + + /// The selection origin, or the edit point if there is no selection. Note that the selection + /// origin may be after the edit point, in the case of a backward selection. + pub fn selection_origin_or_edit_point(&self) -> TextPoint { + self.selection_origin.unwrap_or(self.edit_point) + } + + pub fn selection_direction(&self) -> SelectionDirection { + self.selection_direction + } + + pub fn set_max_length(&mut self, length: Option) { + self.max_length = length; + } + + pub fn set_min_length(&mut self, length: Option) { + self.min_length = length; + } + /// Remove a character at the current editing point pub fn delete_char(&mut self, dir: Direction) { if self.selection_origin.is_none() || self.selection_origin == Some(self.edit_point) { @@ -196,12 +239,6 @@ impl TextInput { self.replace_selection(DOMString::from(s.into())); } - /// The selection origin, or the edit point if there is no selection. Note that the selection - /// origin may be after the edit point, in the case of a backward selection. - pub fn selection_origin_or_edit_point(&self) -> TextPoint { - self.selection_origin.unwrap_or(self.edit_point) - } - /// The start of the selection (or the edit point, if there is no selection). Always less than /// or equal to selection_end(), regardless of the selection direction. pub fn selection_start(&self) -> TextPoint { @@ -832,12 +869,6 @@ impl TextInput { &self.lines[0] } - /// Get a mutable reference to the contents of a single-line text input. Panics if self is a multiline input. - pub fn single_line_content_mut(&mut self) -> &mut DOMString { - assert!(!self.multiline); - &mut self.lines[0] - } - /// Set the current contents of the text input. If this is control supports multiple lines, /// any \n encountered will be stripped and force a new logical line. pub fn set_content(&mut self, content: DOMString, update_text_cursor: bool) { @@ -850,11 +881,15 @@ impl TextInput { } else { vec!(content) }; + if update_text_cursor { - self.edit_point.line = min(self.edit_point.line, self.lines.len() - 1); - self.edit_point.index = min(self.edit_point.index, self.current_line_length()); + self.edit_point = self.edit_point.constrain_to(&self.lines); } - self.selection_origin = None; + + if let Some(origin) = self.selection_origin { + self.selection_origin = Some(origin.constrain_to(&self.lines)); + } + self.assert_ok_selection(); } diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 0bdeec59cda1..916e26c9164e 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -42,7 +42,7 @@ fn test_textinput_when_inserting_multiple_lines_over_a_selection_respects_max_le SelectionDirection::None, ); - textinput.edit_point = TextPoint { line: 0, index: 1 }; + textinput.adjust_horizontal(1, Selection::NotSelected); textinput.adjust_horizontal(3, Selection::Selected); textinput.adjust_vertical(1, Selection::Selected); @@ -67,8 +67,7 @@ fn test_textinput_when_inserting_multiple_lines_still_respects_max_length() { SelectionDirection::None ); - textinput.edit_point = TextPoint { line: 1, index: 0 }; - + textinput.adjust_vertical(1, Selection::NotSelected); textinput.insert_string("cruel\nterrible".to_string()); assert_eq!(textinput.get_content(), "hello\ncruel\nworld"); @@ -117,7 +116,7 @@ fn test_single_line_textinput_with_max_length_doesnt_allow_appending_characters_ SelectionDirection::None, ); - textinput.edit_point = TextPoint { line: 0, index: 1 }; + textinput.adjust_horizontal(1, Selection::NotSelected); textinput.adjust_horizontal(3, Selection::Selected); // Selection is now "abcde" @@ -220,9 +219,7 @@ fn test_textinput_delete_char() { assert_eq!(textinput.get_content(), "ab"); let mut textinput = text_input(Lines::Single, "abcdefg"); - textinput.adjust_horizontal(2, Selection::NotSelected); - // Set an empty selection range. - textinput.selection_origin = Some(textinput.edit_point); + textinput.set_selection_range(2, 2, SelectionDirection::None); textinput.delete_char(Direction::Backward); assert_eq!(textinput.get_content(), "acdefg"); } @@ -300,16 +297,16 @@ fn test_textinput_adjust_vertical() { let mut textinput = text_input(Lines::Multiple, "abc\nde\nf"); textinput.adjust_horizontal(3, Selection::NotSelected); textinput.adjust_vertical(1, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 2); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 2); textinput.adjust_vertical(-1, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 2); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 2); textinput.adjust_vertical(2, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 2); - assert_eq!(textinput.edit_point.index, 1); + assert_eq!(textinput.edit_point().line, 2); + assert_eq!(textinput.edit_point().index, 1); } #[test] @@ -317,32 +314,32 @@ fn test_textinput_adjust_vertical_multibyte() { let mut textinput = text_input(Lines::Multiple, "áé\nae"); textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 2); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 2); textinput.adjust_vertical(1, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 1); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 1); } #[test] fn test_textinput_adjust_horizontal() { let mut textinput = text_input(Lines::Multiple, "abc\nde\nf"); textinput.adjust_horizontal(4, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 0); textinput.adjust_horizontal(1, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 1); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 1); textinput.adjust_horizontal(2, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 2); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().line, 2); + assert_eq!(textinput.edit_point().index, 0); textinput.adjust_horizontal(-1, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 2); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 2); } #[test] @@ -351,45 +348,45 @@ fn test_textinput_adjust_horizontal_by_word() { let mut textinput = text_input(Lines::Single, "abc def"); textinput.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); textinput.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 7); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 7); textinput.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 4); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 4); textinput.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 0); // Test new line case of movement word by word based on UAX#29 rules let mut textinput_2 = text_input(Lines::Multiple, "abc\ndef"); textinput_2.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); textinput_2.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_2.edit_point.line, 1); - assert_eq!(textinput_2.edit_point.index, 3); + assert_eq!(textinput_2.edit_point().line, 1); + assert_eq!(textinput_2.edit_point().index, 3); textinput_2.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput_2.edit_point.line, 1); - assert_eq!(textinput_2.edit_point.index, 0); + assert_eq!(textinput_2.edit_point().line, 1); + assert_eq!(textinput_2.edit_point().index, 0); textinput_2.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput_2.edit_point.line, 0); - assert_eq!(textinput_2.edit_point.index, 0); + assert_eq!(textinput_2.edit_point().line, 0); + assert_eq!(textinput_2.edit_point().index, 0); // Test non-standard sized characters case of movement word by word based on UAX#29 rules let mut textinput_3 = text_input(Lines::Single, "áéc d🌠bc"); textinput_3.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 5); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 5); textinput_3.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 7); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 7); textinput_3.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 13); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 13); textinput_3.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 11); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 11); textinput_3.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 6); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 6); } #[test] @@ -397,29 +394,29 @@ fn test_textinput_adjust_horizontal_to_line_end() { // Test standard case of movement to end based on UAX#29 rules let mut textinput = text_input(Lines::Single, "abc def"); textinput.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 7); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 7); // Test new line case of movement to end based on UAX#29 rules let mut textinput_2 = text_input(Lines::Multiple, "abc\ndef"); textinput_2.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_2.edit_point.line, 0); - assert_eq!(textinput_2.edit_point.index, 3); + assert_eq!(textinput_2.edit_point().line, 0); + assert_eq!(textinput_2.edit_point().index, 3); textinput_2.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_2.edit_point.line, 0); - assert_eq!(textinput_2.edit_point.index, 3); + assert_eq!(textinput_2.edit_point().line, 0); + assert_eq!(textinput_2.edit_point().index, 3); textinput_2.adjust_horizontal_to_line_end(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput_2.edit_point.line, 0); - assert_eq!(textinput_2.edit_point.index, 0); + assert_eq!(textinput_2.edit_point().line, 0); + assert_eq!(textinput_2.edit_point().index, 0); // Test non-standard sized characters case of movement to end based on UAX#29 rules let mut textinput_3 = text_input(Lines::Single, "áéc d🌠bc"); textinput_3.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 13); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 13); textinput_3.adjust_horizontal_to_line_end(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput_3.edit_point.line, 0); - assert_eq!(textinput_3.edit_point.index, 0); + assert_eq!(textinput_3.edit_point().line, 0); + assert_eq!(textinput_3.edit_point().index, 0); } #[test] @@ -429,29 +426,29 @@ fn test_navigation_keyboard_shortcuts() { // Test that CMD + Right moves to the end of the current line. textinput.handle_keydown_aux(None, Key::Right, KeyModifiers::SUPER); - assert_eq!(textinput.edit_point.index, 11); + assert_eq!(textinput.edit_point().index, 11); // Test that CMD + Right moves to the beginning of the current line. textinput.handle_keydown_aux(None, Key::Left, KeyModifiers::SUPER); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); // Test that CTRL + ALT + E moves to the end of the current line also. textinput.handle_keydown_aux(None, Key::E, KeyModifiers::CONTROL | KeyModifiers::ALT); - assert_eq!(textinput.edit_point.index, 11); + assert_eq!(textinput.edit_point().index, 11); // Test that CTRL + ALT + A moves to the beginning of the current line also. textinput.handle_keydown_aux(None, Key::A, KeyModifiers::CONTROL | KeyModifiers::ALT); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); // Test that ALT + Right moves to the end of the word. textinput.handle_keydown_aux(None, Key::Right, KeyModifiers::ALT); - assert_eq!(textinput.edit_point.index, 5); + assert_eq!(textinput.edit_point().index, 5); // Test that CTRL + ALT + F moves to the end of the word also. textinput.handle_keydown_aux(None, Key::F, KeyModifiers::CONTROL | KeyModifiers::ALT); - assert_eq!(textinput.edit_point.index, 11); + assert_eq!(textinput.edit_point().index, 11); // Test that ALT + Left moves to the end of the word. textinput.handle_keydown_aux(None, Key::Left, KeyModifiers::ALT); - assert_eq!(textinput.edit_point.index, 6); + assert_eq!(textinput.edit_point().index, 6); // Test that CTRL + ALT + B moves to the end of the word also. textinput.handle_keydown_aux(None, Key::B, KeyModifiers::CONTROL | KeyModifiers::ALT); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); } #[test] @@ -470,12 +467,12 @@ fn test_textinput_handle_return() { #[test] fn test_textinput_select_all() { let mut textinput = text_input(Lines::Multiple, "abc\nde\nf"); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 0); textinput.select_all(); - assert_eq!(textinput.edit_point.line, 2); - assert_eq!(textinput.edit_point.index, 1); + assert_eq!(textinput.edit_point().line, 2); + assert_eq!(textinput.edit_point().index, 1); } #[test] @@ -495,15 +492,15 @@ fn test_textinput_set_content() { textinput.set_content(DOMString::from("abc\nf"), true); assert_eq!(textinput.get_content(), "abc\nf"); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 0); textinput.adjust_horizontal(3, Selection::Selected); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 3); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 3); textinput.set_content(DOMString::from("de"), true); assert_eq!(textinput.get_content(), "de"); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 2); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 2); } #[test] @@ -520,7 +517,7 @@ fn test_clipboard_paste() { None, SelectionDirection::None); assert_eq!(textinput.get_content(), "defg"); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); textinput.handle_keydown_aux(Some('v'), Key::V, MODIFIERS); assert_eq!(textinput.get_content(), "abcdefg"); } @@ -532,23 +529,23 @@ fn test_textinput_cursor_position_correct_after_clearing_selection() { // Single line - Forward textinput.adjust_horizontal(3, Selection::Selected); textinput.adjust_horizontal(1, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 3); + assert_eq!(textinput.edit_point().index, 3); textinput.adjust_horizontal(-3, Selection::NotSelected); textinput.adjust_horizontal(3, Selection::Selected); textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 3); + assert_eq!(textinput.edit_point().index, 3); // Single line - Backward textinput.adjust_horizontal(-3, Selection::NotSelected); textinput.adjust_horizontal(3, Selection::Selected); textinput.adjust_horizontal(-1, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); textinput.adjust_horizontal(-3, Selection::NotSelected); textinput.adjust_horizontal(3, Selection::Selected); textinput.adjust_horizontal_by_one(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); let mut textinput = text_input(Lines::Multiple, "abc\nde\nf"); @@ -556,27 +553,27 @@ fn test_textinput_cursor_position_correct_after_clearing_selection() { // Multiline - Forward textinput.adjust_horizontal(4, Selection::Selected); textinput.adjust_horizontal(1, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 0); - assert_eq!(textinput.edit_point.line, 1); + assert_eq!(textinput.edit_point().index, 0); + assert_eq!(textinput.edit_point().line, 1); textinput.adjust_horizontal(-4, Selection::NotSelected); textinput.adjust_horizontal(4, Selection::Selected); textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 0); - assert_eq!(textinput.edit_point.line, 1); + assert_eq!(textinput.edit_point().index, 0); + assert_eq!(textinput.edit_point().line, 1); // Multiline - Backward textinput.adjust_horizontal(-4, Selection::NotSelected); textinput.adjust_horizontal(4, Selection::Selected); textinput.adjust_horizontal(-1, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 0); - assert_eq!(textinput.edit_point.line, 0); + assert_eq!(textinput.edit_point().index, 0); + assert_eq!(textinput.edit_point().line, 0); textinput.adjust_horizontal(-4, Selection::NotSelected); textinput.adjust_horizontal(4, Selection::Selected); textinput.adjust_horizontal_by_one(Direction::Backward, Selection::NotSelected); - assert_eq!(textinput.edit_point.index, 0); - assert_eq!(textinput.edit_point.line, 0); + assert_eq!(textinput.edit_point().index, 0); + assert_eq!(textinput.edit_point().line, 0); } @@ -584,53 +581,53 @@ fn test_textinput_cursor_position_correct_after_clearing_selection() { fn test_textinput_set_selection_with_direction() { let mut textinput = text_input(Lines::Single, "abcdef"); textinput.set_selection_range(2, 6, SelectionDirection::Forward); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 6); - assert_eq!(textinput.selection_direction, SelectionDirection::Forward); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 6); + assert_eq!(textinput.selection_direction(), SelectionDirection::Forward); - assert!(textinput.selection_origin.is_some()); - assert_eq!(textinput.selection_origin.unwrap().line, 0); - assert_eq!(textinput.selection_origin.unwrap().index, 2); + assert!(textinput.selection_origin().is_some()); + assert_eq!(textinput.selection_origin().unwrap().line, 0); + assert_eq!(textinput.selection_origin().unwrap().index, 2); textinput.set_selection_range(2, 6, SelectionDirection::Backward); - assert_eq!(textinput.edit_point.line, 0); - assert_eq!(textinput.edit_point.index, 2); - assert_eq!(textinput.selection_direction, SelectionDirection::Backward); + assert_eq!(textinput.edit_point().line, 0); + assert_eq!(textinput.edit_point().index, 2); + assert_eq!(textinput.selection_direction(), SelectionDirection::Backward); - assert!(textinput.selection_origin.is_some()); - assert_eq!(textinput.selection_origin.unwrap().line, 0); - assert_eq!(textinput.selection_origin.unwrap().index, 6); + assert!(textinput.selection_origin().is_some()); + assert_eq!(textinput.selection_origin().unwrap().line, 0); + assert_eq!(textinput.selection_origin().unwrap().index, 6); textinput = text_input(Lines::Multiple, "\n\n"); textinput.set_selection_range(0, 1, SelectionDirection::Forward); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 0); - assert_eq!(textinput.selection_direction, SelectionDirection::Forward); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 0); + assert_eq!(textinput.selection_direction(), SelectionDirection::Forward); - assert!(textinput.selection_origin.is_some()); - assert_eq!(textinput.selection_origin.unwrap().line, 0); - assert_eq!(textinput.selection_origin.unwrap().index, 0); + assert!(textinput.selection_origin().is_some()); + assert_eq!(textinput.selection_origin().unwrap().line, 0); + assert_eq!(textinput.selection_origin().unwrap().index, 0); textinput = text_input(Lines::Multiple, "\n"); textinput.set_selection_range(0, 1, SelectionDirection::Forward); - assert_eq!(textinput.edit_point.line, 1); - assert_eq!(textinput.edit_point.index, 0); - assert_eq!(textinput.selection_direction, SelectionDirection::Forward); + assert_eq!(textinput.edit_point().line, 1); + assert_eq!(textinput.edit_point().index, 0); + assert_eq!(textinput.selection_direction(), SelectionDirection::Forward); - assert!(textinput.selection_origin.is_some()); - assert_eq!(textinput.selection_origin.unwrap().line, 0); - assert_eq!(textinput.selection_origin.unwrap().index, 0); + assert!(textinput.selection_origin().is_some()); + assert_eq!(textinput.selection_origin().unwrap().line, 0); + assert_eq!(textinput.selection_origin().unwrap().index, 0); } #[test] fn test_textinput_unicode_handling() { let mut textinput = text_input(Lines::Single, "éèùµ$£"); - assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point().index, 0); textinput.set_edit_point_index(1); - assert_eq!(textinput.edit_point.index, 2); + assert_eq!(textinput.edit_point().index, 2); textinput.set_edit_point_index(4); - assert_eq!(textinput.edit_point.index, 8); + assert_eq!(textinput.edit_point().index, 8); } #[test] diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index f8a55cc49d51..3a750e74cfbe 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -39947,6 +39947,12 @@ {} ] ], + "mozilla/textcontrol-selection-cannot-exceed-content.html": [ + [ + "/_mozilla/mozilla/textcontrol-selection-cannot-exceed-content.html", + {} + ] + ], "mozilla/timeout-in-discarded-document.html": [ [ "/_mozilla/mozilla/timeout-in-discarded-document.html", @@ -71956,6 +71962,10 @@ "094fbd794b78520d6c2d8aae549557bed3529a7a", "testharness" ], + "mozilla/textcontrol-selection-cannot-exceed-content.html": [ + "f8b982ba0abb23f8e53ef28413e8da2f8dd7ca17", + "testharness" + ], "mozilla/timeout-in-discarded-document.html": [ "7ff88491f20ee72ed40aa5ce4e84840b98d5eef5", "testharness" diff --git a/tests/wpt/mozilla/tests/mozilla/textcontrol-selection-cannot-exceed-content.html b/tests/wpt/mozilla/tests/mozilla/textcontrol-selection-cannot-exceed-content.html new file mode 100644 index 000000000000..b162fba0ec54 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/textcontrol-selection-cannot-exceed-content.html @@ -0,0 +1,25 @@ + + +The selection bounds of a text control should never exceed the content + + + + + +