diff options
Diffstat (limited to 'components/util/str.rs')
-rw-r--r-- | components/util/str.rs | 137 |
1 files changed, 135 insertions, 2 deletions
diff --git a/components/util/str.rs b/components/util/str.rs index 28aedee4f30..7b2cfdf5107 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -4,6 +4,8 @@ use geometry::Au; +use cssparser::{mod, RGBA, RGBAColor}; +use std::ascii::AsciiExt; use std::from_str::FromStr; use std::iter::Filter; use std::str::{CharEq, CharSplits}; @@ -61,7 +63,8 @@ pub static HTML_SPACE_CHARACTERS: StaticCharVec = &[ '\u000d', ]; -pub fn split_html_space_chars<'a>(s: &'a str) -> Filter<'a, &'a str, CharSplits<'a, StaticCharVec>> { +pub fn split_html_space_chars<'a>(s: &'a str) + -> Filter<'a, &'a str, CharSplits<'a, StaticCharVec>> { s.split(HTML_SPACE_CHARACTERS).filter(|&split| !split.is_empty()) } @@ -76,7 +79,6 @@ fn do_parse_integer<T: Iterator<char>>(input: T) -> Option<i64> { } } - let mut input = input.skip_while(|c| { HTML_SPACE_CHARACTERS.iter().any(|s| s == c) }).peekable(); @@ -184,6 +186,137 @@ pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto { } } +/// Parses a legacy color per HTML5 § 2.4.6. If unparseable, `Err` is returned. +pub fn parse_legacy_color(mut input: &str) -> Result<RGBA,()> { + // Steps 1 and 2. + if input.len() == 0 { + return Err(()) + } + + // Step 3. + input = input.trim_left_chars(Whitespace).trim_right_chars(Whitespace); + + // Step 4. + if input.eq_ignore_ascii_case("transparent") { + return Err(()) + } + + // Step 5. + match cssparser::parse_color_keyword(input) { + Ok(RGBAColor(rgba)) => return Ok(rgba), + _ => {} + } + + // Step 6. + if input.len() == 4 { + match (input.as_bytes()[0], + hex(input.as_bytes()[1] as char), + hex(input.as_bytes()[2] as char), + hex(input.as_bytes()[3] as char)) { + (b'#', Ok(r), Ok(g), Ok(b)) => { + return Ok(RGBA { + red: (r as f32) * 17.0 / 255.0, + green: (g as f32) * 17.0 / 255.0, + blue: (b as f32) * 17.0 / 255.0, + alpha: 1.0, + }) + } + _ => {} + } + } + + // Step 7. + let mut new_input = String::new(); + for ch in input.chars() { + if ch as u32 > 0xffff { + new_input.push_str("00") + } else { + new_input.push(ch) + } + } + let mut input = new_input.as_slice(); + + // Step 8. + for (char_count, (index, _)) in input.char_indices().enumerate() { + if char_count == 128 { + input = input.slice_to(index); + break + } + } + + // Step 9. + if input.char_at(0) == '#' { + input = input.slice_from(1) + } + + // Step 10. + let mut new_input = Vec::new(); + for ch in input.chars() { + if hex(ch).is_ok() { + new_input.push(ch as u8) + } else { + new_input.push(b'0') + } + } + let mut input = new_input; + + // Step 11. + while input.len() == 0 || (input.len() % 3) != 0 { + input.push(b'0') + } + + // Step 12. + let mut length = input.len() / 3; + let (mut red, mut green, mut blue) = (input.slice_to(length), + input.slice(length, length * 2), + input.slice_from(length * 2)); + + // Step 13. + if length > 8 { + red = red.slice_from(length - 8); + green = green.slice_from(length - 8); + blue = blue.slice_from(length - 8); + length = 8 + } + + // Step 14. + while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' { + red = red.slice_from(1); + green = green.slice_from(1); + blue = blue.slice_from(1); + length -= 1 + } + + // Steps 15-20. + return Ok(RGBA { + red: hex_string(red).unwrap() as f32 / 255.0, + green: hex_string(green).unwrap() as f32 / 255.0, + blue: hex_string(blue).unwrap() as f32 / 255.0, + alpha: 1.0, + }); + + fn hex(ch: char) -> Result<u8,()> { + match ch { + '0'...'9' => Ok((ch as u8) - b'0'), + 'a'...'f' => Ok((ch as u8) - b'a' + 10), + 'A'...'F' => Ok((ch as u8) - b'A' + 10), + _ => Err(()), + } + } + + fn hex_string(string: &[u8]) -> Result<u8,()> { + match string.len() { + 0 => Err(()), + 1 => hex(string[0] as char), + _ => { + let upper = try!(hex(string[0] as char)); + let lower = try!(hex(string[1] as char)); + Ok((upper << 4) | lower) + } + } + } +} + #[deriving(Clone, Eq, PartialEq, Hash, Show)] pub struct LowercaseString { |