Auto merge of #18262 - KiChjang:value-sanitization, r=nox

Implement value sanitization on HTMLInputElement

https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18262)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-11-09 18:35:07 -06:00 committed by GitHub
commit 338e2ae520
15 changed files with 213 additions and 285 deletions

View file

@ -185,6 +185,29 @@ impl DOMString {
pub fn bytes(&self) -> Bytes {
self.0.bytes()
}
/// Removes newline characters according to <https://infra.spec.whatwg.org/#strip-newlines>.
pub fn strip_newlines(&mut self) {
self.0.retain(|c| c != '\r' && c != '\n');
}
/// Removes leading and trailing ASCII whitespaces according to
/// <https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace>.
pub fn strip_leading_and_trailing_ascii_whitespace(&mut self) {
if self.0.len() == 0 { return; }
let last_non_whitespace = match self.0.rfind(|ref c| !char::is_ascii_whitespace(c)) {
Some(idx) => idx + 1,
None => {
self.0.clear();
return;
}
};
let first_non_whitespace = self.0.find(|ref c| !char::is_ascii_whitespace(c)).unwrap();
self.0.truncate(last_non_whitespace);
let _ = self.0.splice(0..first_non_whitespace, "");
}
}
impl Borrow<str> for DOMString {

View file

@ -46,11 +46,12 @@ 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;
use style::str::split_commas;
use textinput::{SelectionDirection, TextInput};
use textinput::{Direction, Selection, SelectionDirection, TextInput};
use textinput::KeyReaction::{DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction};
use textinput::Lines::Single;
@ -175,7 +176,7 @@ impl HTMLInputElement {
.map_or_else(|| atom!(""), |a| a.value().as_atom().to_owned())
}
// https://html.spec.whatwg.org/multipage/#input-type-attr-summary
// https://html.spec.whatwg.org/multipage/#dom-input-value
fn value_mode(&self) -> ValueMode {
match self.input_type.get() {
InputType::InputSubmit |
@ -409,8 +410,17 @@ impl HTMLInputElementMethods for HTMLInputElement {
fn SetValue(&self, value: DOMString) -> ErrorResult {
match self.value_mode() {
ValueMode::Value => {
self.textinput.borrow_mut().set_content(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.
self.sanitize_value();
// Step 5.
if *self.textinput.borrow().single_line_content() != old_value {
self.textinput.borrow_mut()
.adjust_horizontal_to_limit(Direction::Forward, Selection::NotSelected);
}
}
ValueMode::Default |
ValueMode::DefaultOn => {
@ -859,6 +869,23 @@ impl HTMLInputElement {
target.fire_bubbling_event(atom!("change"));
}
}
// https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm
fn sanitize_value(&self) {
match self.type_() {
atom!("text") | atom!("search") | atom!("tel") | atom!("password") => {
self.textinput.borrow_mut().single_line_content_mut().strip_newlines();
}
atom!("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();
}
// TODO: Implement more value sanitization algorithms for different types of inputs
_ => ()
}
}
}
impl VirtualMethods for HTMLInputElement {
@ -972,7 +999,8 @@ impl VirtualMethods for HTMLInputElement {
self.radio_group_name().as_ref());
}
// TODO: Step 6 - value sanitization
// Step 6
self.sanitize_value();
},
AttributeMutation::Removed => {
if self.input_type.get() == InputType::InputRadio {

View file

@ -4,11 +4,14 @@
#![cfg_attr(feature = "unstable", feature(core_intrinsics))]
#![cfg_attr(feature = "unstable", feature(on_unimplemented))]
#![feature(ascii_ctype)]
#![feature(conservative_impl_trait)]
#![feature(const_fn)]
#![feature(mpsc_select)]
#![feature(plugin)]
#![feature(proc_macro)]
#![feature(splice)]
#![feature(string_retain)]
#![deny(unsafe_code)]
#![allow(non_snake_case)]

View file

@ -754,6 +754,18 @@ impl<T: ClipboardProvider> TextInput<T> {
DOMString::from(content)
}
/// Get a reference to the contents of a single-line text input. Panics if self is a multiline input.
pub fn single_line_content(&self) -> &DOMString {
assert!(!self.multiline);
&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) {