diff --git a/components/script/dom/htmlfontelement.rs b/components/script/dom/htmlfontelement.rs index d898a5840b2..fb5379e7954 100644 --- a/components/script/dom/htmlfontelement.rs +++ b/components/script/dom/htmlfontelement.rs @@ -150,8 +150,8 @@ pub fn parse_legacy_font_size(mut input: &str) -> Option<&'static str> { // Steps 6, 7, 8 let mut value = match read_numbers(input_chars) { - Some(v) => v, - None => return None, + (Some(v), _) => v, + (None, _) => return None, }; // Step 9 diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs index 5ad77e9e8b4..8ab213f6225 100644 --- a/components/script/dom/htmltableelement.rs +++ b/components/script/dom/htmltableelement.rs @@ -410,7 +410,7 @@ impl VirtualMethods for HTMLTableElement { } atom!("cellspacing") => { self.cellspacing.set(mutation.new_value(attr).and_then(|value| { - parse_unsigned_integer(value.chars()) + parse_unsigned_integer(value.chars()).ok() })); }, _ => {}, diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 07db2a42b0d..e86db731f95 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -2264,6 +2264,7 @@ dependencies = [ "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/style/attr.rs b/components/style/attr.rs index 363c5b44f52..bcd14d9851d 100644 --- a/components/style/attr.rs +++ b/components/style/attr.rs @@ -12,7 +12,7 @@ use std::str::FromStr; use string_cache::{Atom, Namespace}; use url::Url; use util::str::{DOMString, LengthOrPercentageOrAuto, HTML_SPACE_CHARACTERS}; -use util::str::{read_numbers, split_html_space_chars, str_join}; +use util::str::{read_exponent, read_fraction, read_numbers, split_html_space_chars, str_join}; use values::specified::{Length}; // Duplicated from script::dom::values. @@ -24,6 +24,7 @@ pub enum AttrValue { TokenList(DOMString, Vec), UInt(DOMString, u32), Int(DOMString, i32), + Double(DOMString, f64), Atom(Atom), Length(DOMString, Option), Color(DOMString, Option), @@ -52,25 +53,64 @@ fn do_parse_integer>(input: T) -> Option { Some(_) => 1, }; - let value = read_numbers(input); + let (value, _) = read_numbers(input); value.and_then(|value| value.checked_mul(sign)) } /// Parse an integer according to /// . -pub fn parse_integer>(input: T) -> Option { +pub fn parse_integer>(input: T) -> Result { do_parse_integer(input).and_then(|result| { result.to_i32() - }) + }).ok_or(()) } /// Parse an integer according to /// -pub fn parse_unsigned_integer>(input: T) -> Option { +pub fn parse_unsigned_integer>(input: T) -> Result { do_parse_integer(input).and_then(|result| { result.to_u32() - }) + }).ok_or(()) +} + +/// Parse a floating-point number according to +/// +pub fn parse_double(string: &DOMString) -> Result { + let trimmed = string.trim_matches(HTML_SPACE_CHARACTERS); + let mut input = trimmed.chars().peekable(); + + let (value, divisor) = match input.peek() { + None => return Err(()), + Some(&'-') => { + input.next(); + (-1f64, -1f64) + } + Some(&'+') => { + input.next(); + (1f64, 1f64) + } + _ => (1f64, 1f64) + }; + + let (value, value_digits) = if let Some(&'.') = input.peek() { + (0f64, 0) + } else { + let (read_val, read_digits) = read_numbers(input); + (value * read_val.and_then(|result| result.to_f64()).unwrap_or(1f64), read_digits) + }; + + let input = trimmed.chars().skip(value_digits).peekable(); + + let (mut value, fraction_digits) = read_fraction(input, divisor, value); + + let input = trimmed.chars().skip(value_digits + fraction_digits).peekable(); + + if let Some(exp) = read_exponent(input) { + value *= 10f64.powi(exp) + }; + + Ok(value) } impl AttrValue { @@ -107,6 +147,17 @@ impl AttrValue { AttrValue::Int(string, result) } + // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-double + pub fn from_double(string: DOMString, default: f64) -> AttrValue { + let result = parse_double(&string).unwrap_or(default); + let result = if result.is_infinite() { + default + } else { + result + }; + AttrValue::Double(string, result) + } + // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers pub fn from_limited_i32(string: DOMString, default: i32) -> AttrValue { let result = parse_integer(string.chars()).unwrap_or(default); @@ -250,6 +301,7 @@ impl Deref for AttrValue { AttrValue::String(ref value) | AttrValue::TokenList(ref value, _) | AttrValue::UInt(ref value, _) | + AttrValue::Double(ref value, _) | AttrValue::Length(ref value, _) | AttrValue::Color(ref value, _) | AttrValue::Int(ref value, _) | diff --git a/components/util/Cargo.toml b/components/util/Cargo.toml index ac7bc43b1dd..151348c0af1 100644 --- a/components/util/Cargo.toml +++ b/components/util/Cargo.toml @@ -38,6 +38,7 @@ heapsize_plugin = "0.1.2" lazy_static = "0.1" libc = "0.2" log = "0.3.5" +num = "0.1.32" num_cpus = "0.2.2" rand = "0.3" rustc-serialize = "0.3" diff --git a/components/util/lib.rs b/components/util/lib.rs index b4998b0580c..6f749556db1 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -34,6 +34,7 @@ extern crate lazy_static; extern crate libc; #[macro_use] extern crate log; +extern crate num; extern crate num_cpus; extern crate rand; extern crate rustc_serialize; diff --git a/components/util/str.rs b/components/util/str.rs index b0a3d6c2c9e..8d5a5e74bef 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -4,6 +4,7 @@ use app_units::Au; use libc::c_char; +use num::ToPrimitive; use std::borrow::ToOwned; use std::convert::AsRef; use std::ffi::CStr; @@ -158,24 +159,74 @@ fn is_ascii_digit(c: &char) -> bool { } } +fn is_decimal_point(c: char) -> bool { + c == '.' +} -pub fn read_numbers>(mut iter: Peekable) -> Option { +fn is_exponent_char(c: char) -> bool { + match c { + 'e' | 'E' => true, + _ => false, + } +} + +pub fn read_numbers>(mut iter: Peekable) -> (Option, usize) { match iter.peek() { Some(c) if is_ascii_digit(c) => (), - _ => return None, + _ => return (None, 0), } iter.take_while(is_ascii_digit).map(|d| { d as i64 - '0' as i64 - }).fold(Some(0i64), |accumulator, d| { - accumulator.and_then(|accumulator| { + }).fold((Some(0i64), 0), |accumulator, d| { + let digits = accumulator.0.and_then(|accumulator| { accumulator.checked_mul(10) }).and_then(|accumulator| { accumulator.checked_add(d) - }) + }); + (digits, accumulator.1 + 1) }) } +pub fn read_fraction>(mut iter: Peekable, + mut divisor: f64, + value: f64) -> (f64, usize) { + match iter.peek() { + Some(c) if is_decimal_point(*c) => (), + _ => return (value, 0), + } + iter.next(); + + iter.take_while(is_ascii_digit).map(|d| + d as i64 - '0' as i64 + ).fold((value, 1), |accumulator, d| { + divisor *= 10f64; + (accumulator.0 + d as f64 / divisor, + accumulator.1 + 1) + }) +} + +pub fn read_exponent>(mut iter: Peekable) -> Option { + match iter.peek() { + Some(c) if is_exponent_char(*c) => (), + _ => return None, + } + iter.next(); + + match iter.peek() { + None => None, + Some(&'-') => { + iter.next(); + read_numbers(iter).0.map(|exp| -exp.to_i32().unwrap_or(0)) + } + Some(&'+') => { + iter.next(); + read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0)) + } + Some(_) => read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0)) + } +} + #[derive(Clone, Copy, Debug, HeapSizeOf, PartialEq)] pub enum LengthOrPercentageOrAuto { Auto, diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 98520a5096f..8d97b02216a 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -2141,6 +2141,7 @@ dependencies = [ "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/geckolib/Cargo.lock b/ports/geckolib/Cargo.lock index 2d0d34535e5..68227d1d82e 100644 --- a/ports/geckolib/Cargo.lock +++ b/ports/geckolib/Cargo.lock @@ -556,6 +556,7 @@ dependencies = [ "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index b739edbb213..1b194b42b2f 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -2122,6 +2122,7 @@ dependencies = [ "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/unit/style/attr.rs b/tests/unit/style/attr.rs index aa7401113c7..564d5cbdde7 100644 --- a/tests/unit/style/attr.rs +++ b/tests/unit/style/attr.rs @@ -6,6 +6,15 @@ use app_units::Au; use style::attr::{AttrValue, parse_length}; use util::str::{DOMString, LengthOrPercentageOrAuto}; +#[test] +fn test_parse_double() { + let value = DOMString::from("432.5e2"); + match AttrValue::from_double(value, 0.0) { + AttrValue::Double(_, num) => assert_eq!(num, 43250f64), + _ => panic!("expected a double value") + } +} + #[test] fn test_from_limited_i32_should_be_default_when_less_than_0() { let value = DOMString::from("-1");