From 0d9bdb9a8524a39c5512e0c3065205581190b89f Mon Sep 17 00:00:00 2001 From: Ravi Shankar Date: Wed, 22 Mar 2017 00:41:32 +0530 Subject: [PATCH] Add parsing/serialization for --- components/style/values/specified/grid.rs | 182 +++++++++++++++++++++- 1 file changed, 179 insertions(+), 3 deletions(-) diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index 1f418305b0f..85762624caf 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -6,11 +6,10 @@ use cssparser::{Parser, Token, serialize_identifier}; use parser::{Parse, ParserContext}; +use std::{fmt, mem, usize}; use std::ascii::AsciiExt; -use std::fmt; -use std::mem; use style_traits::ToCss; -use values::{CSSFloat, CustomIdent, HasViewportPercentage}; +use values::{CSSFloat, CustomIdent, Either, HasViewportPercentage}; use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; use values::specified::{Integer, LengthOrPercentage}; @@ -603,3 +602,180 @@ impl ToComputedValue for TrackRepeat { } } } + +/// The type of a `` as determined during parsing. +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-list +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum TrackListType { + /// [``](https://drafts.csswg.org/css-grid/#typedef-auto-track-list) + /// + /// If this type exists, then the value at the index in `line_names` field in `TrackList` + /// has the `?` list that comes before ``. If it's a specified value, + /// then the `repeat()` function (that follows the line names list) is also at the given index + /// in `values` field. On the contrary, if it's a computed value, then the `repeat()` function + /// is in the `auto_repeat` field. + Auto(u16), + /// [``](https://drafts.csswg.org/css-grid/#typedef-track-list) + Normal, + /// [``](https://drafts.csswg.org/css-grid/#typedef-explicit-track-list) + /// + /// Note that this is a subset of the normal ``, and so it could be used in place + /// of the latter. + Explicit, +} + +/// A grid `` type. +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-list +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct TrackList { + /// The type of this `` (auto, explicit or general). + /// + /// In order to avoid parsing the same value multiple times, this does a single traversal + /// and arrives at the type of value it has parsed (or bails out gracefully with an error). + pub list_type: TrackListType, + /// A vector of ` | ` values. In its specified form, it may contain + /// any value, but once it's computed, it contains only `` values. + /// + /// Note that this may also contain `` at an index. If it exists, it's + /// given by the index in `TrackListType::Auto` + pub values: Vec, + /// `` 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>, + /// `` value after computation. This field is necessary, because + /// the `values` field (after computation) will only contain `` values, and + /// we need something to represent this function. + pub auto_repeat: Option>, +} + +/// Either a `` or `` component of `` +/// +/// This is required only for the specified form of ``, and will become +/// `TrackSize` in its computed form. +pub type TrackSizeOrRepeat = Either, TrackRepeat>; + +impl Parse for TrackList { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + let mut current_names; + let mut names = vec![]; + let mut values = vec![]; + + let mut list_type = TrackListType::Explicit; // assume it's the simplest case + // marker to check whether we've already encountered along the way + let mut is_auto = false; + // assume that everything is . This flag is useful when we encounter + let mut atleast_one_not_fixed = false; + + 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() { + atleast_one_not_fixed = true; + if is_auto { + return Err(()) // only accepts and + } + } + + names.push(current_names); + values.push(Either::First(track_size)); + } else if let Ok((repeat, type_)) = input.try(|i| TrackRepeat::parse_with_repeat_type(context, i)) { + if list_type == TrackListType::Explicit { + list_type = TrackListType::Normal; // doesn't contain repeat() + } + + match type_ { + RepeatType::Normal => { + atleast_one_not_fixed = true; + if is_auto { // only + return Err(()) + } + }, + RepeatType::Auto => { + if is_auto || atleast_one_not_fixed { + // We've either seen earlier, or there's at least one non-fixed value + return Err(()) + } + + is_auto = true; + list_type = TrackListType::Auto(values.len() as u16); + }, + RepeatType::Fixed => (), + } + + names.push(current_names); + values.push(Either::Second(repeat)); + } else { + if values.is_empty() { + return Err(()) + } + + names.push(current_names); + break + } + } + + Ok(TrackList { + list_type: list_type, + values: values, + line_names: names, + auto_repeat: None, // filled only in computation + }) + } +} + +impl ToCss for TrackList { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let auto_idx = match self.list_type { + TrackListType::Auto(i) => i as usize, + _ => usize::MAX, + }; + + let mut values_iter = self.values.iter().peekable(); + let mut line_names_iter = self.line_names.iter().peekable(); + + for idx in 0.. { + let names = line_names_iter.next().unwrap(); // This should exist! + concat_serialize_idents("[", "]", names, " ", dest)?; + + match self.auto_repeat { + Some(ref repeat) if idx == auto_idx => { + if !names.is_empty() { + dest.write_str(" ")?; + } + + repeat.to_css(dest)?; + }, + _ => match values_iter.next() { + Some(value) => { + if !names.is_empty() { + dest.write_str(" ")?; + } + + value.to_css(dest)?; + }, + None => break, + }, + } + + if values_iter.peek().is_some() || line_names_iter.peek().map_or(false, |v| !v.is_empty()) { + dest.write_str(" ")?; + } + } + + Ok(()) + } +} + +impl HasViewportPercentage for TrackList { + #[inline] + fn has_viewport_percentage(&self) -> bool { + self.values.iter().any(|ref v| v.has_viewport_percentage()) + } +}