diff --git a/components/style/counter_style.rs b/components/style/counter_style.rs index 4cca58f75d7..ad8897e99c2 100644 --- a/components/style/counter_style.rs +++ b/components/style/counter_style.rs @@ -12,7 +12,9 @@ use cssparser::{serialize_string, serialize_identifier}; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSCounterDesc; use parser::{ParserContext, log_css_error, Parse}; use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; +use std::ascii::AsciiExt; use std::fmt; +use std::ops::Range; use style_traits::ToCss; use values::CustomIdent; @@ -139,6 +141,10 @@ counter_style_descriptors! { /// https://drafts.csswg.org/css-counter-styles/#counter-style-suffix "suffix" suffix / eCSSCounterDesc_Suffix: Symbol = Symbol::String(". ".to_owned()); + + /// https://drafts.csswg.org/css-counter-styles/#counter-style-range + "range" range / eCSSCounterDesc_Range: Ranges = + Ranges(Vec::new()); // Empty Vec represents 'auto' } /// https://drafts.csswg.org/css-counter-styles/#counter-style-system @@ -261,3 +267,67 @@ impl ToCss for Negative { Ok(()) } } + +/// https://drafts.csswg.org/css-counter-styles/#counter-style-range +/// +/// Empty Vec represents 'auto' +#[derive(Debug)] +pub struct Ranges(pub Vec>>); + +impl Parse for Ranges { + fn parse(_context: &ParserContext, input: &mut Parser) -> Result { + if input.try(|input| input.expect_ident_matching("auto")).is_ok() { + Ok(Ranges(Vec::new())) + } else { + input.parse_comma_separated(|input| { + let opt_start = parse_bound(input)?; + let opt_end = parse_bound(input)?; + if let (Some(start), Some(end)) = (opt_start, opt_end) { + if start > end { + return Err(()) + } + } + Ok(opt_start..opt_end) + }).map(Ranges) + } + } +} + +fn parse_bound(input: &mut Parser) -> Result, ()> { + match input.next() { + Ok(Token::Number(ref v)) if v.int_value.is_some() => Ok(Some(v.int_value.unwrap())), + Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("infinite") => Ok(None), + _ => Err(()) + } +} + +impl ToCss for Ranges { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.0.iter(); + if let Some(first) = iter.next() { + range_to_css(first, dest)?; + for item in iter { + dest.write_str(", ")?; + range_to_css(item, dest)?; + } + Ok(()) + } else { + dest.write_str("auto") + } + } +} + +fn range_to_css(range: &Range>, dest: &mut W) -> fmt::Result +where W: fmt::Write { + bound_to_css(range.start, dest)?; + dest.write_char(' ')?; + bound_to_css(range.end, dest) +} + +fn bound_to_css(range: Option, dest: &mut W) -> fmt::Result where W: fmt::Write { + if let Some(finite) = range { + write!(dest, "{}", finite) + } else { + dest.write_str("infinite") + } +} diff --git a/components/style/gecko/rules.rs b/components/style/gecko/rules.rs index 068696b1cdf..83429275e03 100644 --- a/components/style/gecko/rules.rs +++ b/components/style/gecko/rules.rs @@ -187,3 +187,26 @@ impl ToNsCssValue for counter_style::Symbol { } } } + +impl ToNsCssValue for counter_style::Ranges { + fn convert(&self, _nscssvalue: &mut nsCSSValue) { + if self.0.is_empty() { + //nscssvalue.set_auto(); // FIXME: add bindings for nsCSSValue::SetAutoValue + } else { + for range in &self.0 { + fn set_bound(bound: Option, nscssvalue: &mut nsCSSValue) { + if let Some(finite) = bound { + nscssvalue.set_integer(finite) + } else { + nscssvalue.set_enum(structs::NS_STYLE_COUNTER_RANGE_INFINITE as i32) + } + } + let mut start = nsCSSValue::null(); + let mut end = nsCSSValue::null(); + set_bound(range.start, &mut start); + set_bound(range.end, &mut end); + // FIXME: add bindings for nsCSSValuePairList + } + } + } +}