diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index cd25e678aac..5c61612355f 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -4,7 +4,7 @@ //! Necessary types for [grid](https://drafts.csswg.org/css-grid/). -use cssparser::{Parser, Token}; +use cssparser::{Parser, Token, serialize_identifier}; use parser::{Parse, ParserContext}; use std::ascii::AsciiExt; use std::fmt; @@ -294,7 +294,7 @@ impl ToCss for TrackSize { TrackSize::MinMax(ref infexible, ref flexible) => { try!(dest.write_str("minmax(")); try!(infexible.to_css(dest)); - try!(dest.write_str(",")); + try!(dest.write_str(", ")); try!(flexible.to_css(dest)); dest.write_str(")") }, @@ -364,6 +364,24 @@ pub fn parse_line_names(input: &mut Parser) -> Result, ()> { }) } +fn concat_serialize_idents(prefix: &str, suffix: &str, + slice: &[String], sep: &str, dest: &mut W) -> fmt::Result + where W: fmt::Write +{ + if let Some((ref first, rest)) = slice.split_first() { + dest.write_str(prefix)?; + serialize_identifier(first, dest)?; + for thing in rest { + dest.write_str(sep)?; + serialize_identifier(thing, dest)?; + } + + dest.write_str(suffix)?; + } + + Ok(()) +} + /// The initial argument of the `repeat` function. /// /// https://drafts.csswg.org/css-grid/#typedef-track-repeat @@ -408,3 +426,125 @@ impl ToCss for RepeatCount { impl ComputedValueAsSpecified for RepeatCount {} no_viewport_percentage!(RepeatCount); + +/// The type of `repeat` function (only used in parsing). +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-repeat +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +enum RepeatType { + /// [``](https://drafts.csswg.org/css-grid/#typedef-auto-repeat) + Auto, + /// [``](https://drafts.csswg.org/css-grid/#typedef-track-repeat) + Normal, + /// [``](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat) + Fixed, +} + +/// The structure containing `` and `` values. +/// +/// It can also hold `repeat()` function parameters, which expands into the respective +/// values in its computed form. +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct TrackRepeat { + /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`) + pub count: RepeatCount, + /// `` accompanying `` values. + /// + /// If there's no ``, then it's represented by an empty vector. + /// For N `` values, there will be N+1 ``, and so this vector's + /// length is always one value more than that of the ``. + pub line_names: Vec>, + /// `` values. + pub track_sizes: Vec>, +} + +impl TrackRepeat { + fn parse_with_repeat_type(context: &ParserContext, input: &mut Parser) + -> Result<(TrackRepeat, RepeatType), ()> { + input.try(|i| i.expect_function_matching("repeat")).and_then(|_| { + input.parse_nested_block(|input| { + let count = RepeatCount::parse(context, input)?; + input.expect_comma()?; + + let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill; + let mut repeat_type = if is_auto { + RepeatType::Auto + } else { // is a subset of , so it should work for both + RepeatType::Fixed + }; + + let mut names = vec![]; + let mut values = vec![]; + let mut current_names; + + loop { + current_names = input.try(parse_line_names).unwrap_or(vec![]); + if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) { + if !track_size.is_fixed() { + if is_auto { + return Err(()) // should be for + } + + if repeat_type == RepeatType::Fixed { + repeat_type = RepeatType::Normal // for sure + } + } + + values.push(track_size); + names.push(current_names); + } else { + if values.is_empty() { + return Err(()) // expecting at least one + } + + names.push(current_names); // final `` + break // no more , breaking + } + } + + let repeat = TrackRepeat { + count: count, + track_sizes: values, + line_names: names, + }; + + Ok((repeat, repeat_type)) + }) + }) + } +} + +impl ToCss for TrackRepeat { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str("repeat(")?; + self.count.to_css(dest)?; + dest.write_str(", ")?; + + let mut line_names_iter = self.line_names.iter(); + for (i, (ref size, ref names)) in self.track_sizes.iter() + .zip(&mut line_names_iter).enumerate() { + if i > 0 { + dest.write_str(" ")?; + } + + concat_serialize_idents("[", "] ", names, " ", dest)?; + size.to_css(dest)?; + } + + if let Some(line_names_last) = line_names_iter.next() { + concat_serialize_idents(" [", "]", line_names_last, " ", dest)?; + } + + dest.write_str(")")?; + Ok(()) + } +} + +impl HasViewportPercentage for TrackRepeat { + #[inline] + fn has_viewport_percentage(&self) -> bool { + self.track_sizes.iter().any(|ref v| v.has_viewport_percentage()) + } +} diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 663a39fb6ac..084224e284b 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -74,6 +74,7 @@ macro_rules! impl_to_css_for_predefined_type { impl_to_css_for_predefined_type!(f32); impl_to_css_for_predefined_type!(i32); +impl_to_css_for_predefined_type!(u16); impl_to_css_for_predefined_type!(u32); impl_to_css_for_predefined_type!(::cssparser::Token<'a>); impl_to_css_for_predefined_type!(::cssparser::RGBA);