diff --git a/components/style/gecko/values.rs b/components/style/gecko/values.rs index 63b035447a4..62cad421e87 100644 --- a/components/style/gecko/values.rs +++ b/components/style/gecko/values.rs @@ -17,8 +17,8 @@ use values::computed::{LengthOrPercentageOrNone, Number, NumberOrPercentage}; use values::computed::{MaxLength, MozLength}; use values::computed::basic_shape::ShapeRadius as ComputedShapeRadius; use values::generics::basic_shape::ShapeRadius; +use values::generics::grid::{TrackBreadth, TrackKeyword}; use values::specified::Percentage; -use values::specified::grid::{TrackBreadth, TrackKeyword}; /// A trait that defines an interface to convert from and to `nsStyleCoord`s. pub trait GeckoStyleCoordConvertible : Sized { diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 7eb66c2fe49..2b10b725d64 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1198,7 +1198,7 @@ fn static_assert() { % for kind in ["rows", "columns"]: pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_${kind}::computed_value::T) { - use values::specified::grid::TrackSize; + use values::generics::grid::TrackSize; match v { TrackSize::FitContent(lop) => { @@ -1231,8 +1231,8 @@ fn static_assert() { use gecko_bindings::structs::{nsTArray, nsStyleGridLine_kMaxLine}; use nsstring::{nsCString, nsStringRepr}; use std::usize; - use values::specified::grid::TrackListType::Auto; - use values::specified::grid::{RepeatCount, TrackSize}; + use values::generics::grid::TrackListType::Auto; + use values::generics::grid::{RepeatCount, TrackSize}; #[inline] fn set_bitfield(bitfield: &mut u8, pos: u8, val: bool) { diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 4a56a7b4872..7251b315796 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -18,9 +18,9 @@ use std::fmt; use style_traits::ToCss; use super::{CSSFloat, CSSInteger, RGBA}; use super::generics::BorderRadiusSize as GenericBorderRadiusSize; +use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; +use super::generics::grid::TrackList as GenericTrackList; use super::specified; -use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; -use super::specified::grid::TrackList as GenericTrackList; pub use app_units::Au; pub use cssparser::Color as CSSColor; @@ -28,7 +28,8 @@ pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; -pub use super::specified::{BorderStyle, GridLine, Percentage, UrlOrNone}; +pub use super::specified::{BorderStyle, Percentage, UrlOrNone}; +pub use super::generics::grid::GridLine; pub use super::specified::url::SpecifiedUrl; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs new file mode 100644 index 00000000000..0e11143097b --- /dev/null +++ b/components/style/values/generics/grid.rs @@ -0,0 +1,550 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Generic types for the handling of +//! [grids](https://drafts.csswg.org/css-grid/). + +use cssparser::{Parser, serialize_identifier}; +use parser::{Parse, ParserContext}; +use std::{fmt, mem, usize}; +use style_traits::ToCss; +use values::{CSSFloat, CustomIdent}; +use values::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue}; +use values::specified::Integer; + +#[derive(PartialEq, Clone, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +/// A `` type. +/// +/// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line +pub struct GridLine { + /// Flag to check whether it's a `span` keyword. + pub is_span: bool, + /// A custom identifier for named lines. + /// + /// https://drafts.csswg.org/css-grid/#grid-placement-slot + pub ident: Option, + /// Denotes the nth grid line from grid item's placement. + pub line_num: Option, +} + +impl GridLine { + /// Check whether this `` represents an `auto` value. + pub fn is_auto(&self) -> bool { + self.ident.is_none() && self.line_num.is_none() && !self.is_span + } +} + +impl Default for GridLine { + fn default() -> Self { + GridLine { + is_span: false, + ident: None, + line_num: None, + } + } +} + +impl ToCss for GridLine { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.is_auto() { + return dest.write_str("auto") + } + + if self.is_span { + dest.write_str("span")?; + } + + if let Some(i) = self.line_num { + write!(dest, " {}", i.value())?; + } + + if let Some(ref s) = self.ident { + dest.write_str(" ")?; + serialize_identifier(s, dest)?; + } + + Ok(()) + } +} + +impl Parse for GridLine { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + let mut grid_line = Default::default(); + if input.try(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(grid_line) + } + + for _ in 0..3 { // Maximum possible entities for + if input.try(|i| i.expect_ident_matching("span")).is_ok() { + if grid_line.is_span || grid_line.line_num.is_some() || grid_line.ident.is_some() { + return Err(()) // span (if specified) should be first + } + grid_line.is_span = true; // span (if specified) should be first + } else if let Ok(i) = input.try(|i| Integer::parse(context, i)) { + if i.value() == 0 || grid_line.line_num.is_some() { + return Err(()) + } + grid_line.line_num = Some(i); + } else if let Ok(name) = input.try(|i| i.expect_ident()) { + if grid_line.ident.is_some() || CustomIdent::from_ident((&*name).into(), &[]).is_err() { + return Err(()) + } + grid_line.ident = Some(name.into_owned()); + } else { + break + } + } + + if grid_line.is_auto() { + return Err(()) + } + + if grid_line.is_span { + if let Some(i) = grid_line.line_num { + if i.value() <= 0 { // disallow negative integers for grid spans + return Err(()) + } + } else if grid_line.ident.is_some() { // integer could be omitted + grid_line.line_num = Some(Integer::new(1)); + } else { + return Err(()) + } + } + + Ok(grid_line) + } +} + +impl ComputedValueAsSpecified for GridLine {} +no_viewport_percentage!(GridLine); + +define_css_keyword_enum!{ TrackKeyword: + "auto" => Auto, + "max-content" => MaxContent, + "min-content" => MinContent +} + +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +/// A track breadth for explicit grid track sizing. It's generic solely to +/// avoid re-implementing it for the computed type. +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-breadth +pub enum TrackBreadth { + /// The generic type is almost always a non-negative `` + Breadth(L), + /// A flex fraction specified in `fr` units. + Flex(CSSFloat), + /// One of the track-sizing keywords (`auto`, `min-content`, `max-content`) + Keyword(TrackKeyword), +} + +impl TrackBreadth { + /// Check whether this is a `` (i.e., it only has ``) + /// + /// https://drafts.csswg.org/css-grid/#typedef-fixed-breadth + #[inline] + pub fn is_fixed(&self) -> bool { + match *self { + TrackBreadth::Breadth(ref _lop) => true, + _ => false, + } + } +} + +impl ToCss for TrackBreadth { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + TrackBreadth::Breadth(ref lop) => lop.to_css(dest), + TrackBreadth::Flex(ref value) => write!(dest, "{}fr", value), + TrackBreadth::Keyword(ref k) => k.to_css(dest), + } + } +} + +impl ToComputedValue for TrackBreadth { + type ComputedValue = TrackBreadth; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + TrackBreadth::Breadth(ref lop) => TrackBreadth::Breadth(lop.to_computed_value(context)), + TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr), + TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k), + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + TrackBreadth::Breadth(ref lop) => + TrackBreadth::Breadth(ToComputedValue::from_computed_value(lop)), + TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr), + TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k), + } + } +} + +#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +/// A `` type for explicit grid track sizing. Like ``, this is +/// generic only to avoid code bloat. It only takes `` +/// +/// https://drafts.csswg.org/css-grid/#typedef-track-size +pub enum TrackSize { + /// A flexible `` + Breadth(TrackBreadth), + /// A `minmax` function for a range over an inflexible `` + /// and a flexible `` + /// + /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax + MinMax(TrackBreadth, TrackBreadth), + /// A `fit-content` function. + /// + /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content + FitContent(L), +} + +impl TrackSize { + /// Check whether this is a `` + /// + /// https://drafts.csswg.org/css-grid/#typedef-fixed-size + pub fn is_fixed(&self) -> bool { + match *self { + TrackSize::Breadth(ref breadth) => breadth.is_fixed(), + // For minmax function, it could be either + // minmax(, ) or minmax(, ), + // and since both variants are a subset of minmax(, ), we only + // need to make sure that they're fixed. So, we don't have to modify the parsing function. + TrackSize::MinMax(ref breadth_1, ref breadth_2) => { + if breadth_1.is_fixed() { + return true // the second value is always a + } + + match *breadth_1 { + TrackBreadth::Flex(_) => false, // should be at this point + _ => breadth_2.is_fixed(), + } + }, + TrackSize::FitContent(_) => false, + } + } +} + +impl Default for TrackSize { + fn default() -> Self { + TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto)) + } +} + +impl ToCss for TrackSize { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + TrackSize::Breadth(ref b) => b.to_css(dest), + TrackSize::MinMax(ref infexible, ref flexible) => { + dest.write_str("minmax(")?; + infexible.to_css(dest)?; + dest.write_str(", ")?; + flexible.to_css(dest)?; + dest.write_str(")") + }, + TrackSize::FitContent(ref lop) => { + dest.write_str("fit-content(")?; + lop.to_css(dest)?; + dest.write_str(")") + }, + } + } +} + +impl ToComputedValue for TrackSize { + type ComputedValue = TrackSize; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + TrackSize::Breadth(ref b) => match *b { + // outside `minmax()` expands to `mimmax(auto, )` + // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-flex + TrackBreadth::Flex(f) => + TrackSize::MinMax(TrackBreadth::Keyword(TrackKeyword::Auto), TrackBreadth::Flex(f)), + _ => TrackSize::Breadth(b.to_computed_value(context)), + }, + TrackSize::MinMax(ref b_1, ref b_2) => + TrackSize::MinMax(b_1.to_computed_value(context), b_2.to_computed_value(context)), + TrackSize::FitContent(ref lop) => TrackSize::FitContent(lop.to_computed_value(context)), + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + TrackSize::Breadth(ref b) => + TrackSize::Breadth(ToComputedValue::from_computed_value(b)), + TrackSize::MinMax(ref b_1, ref b_2) => + TrackSize::MinMax(ToComputedValue::from_computed_value(b_1), + ToComputedValue::from_computed_value(b_2)), + TrackSize::FitContent(ref lop) => + TrackSize::FitContent(ToComputedValue::from_computed_value(lop)), + } + } +} + +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 +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum RepeatCount { + /// A positive integer. This is allowed only for `` and `` + Number(Integer), + /// An `` keyword allowed only for `` + AutoFill, + /// An `` keyword allowed only for `` + AutoFit, +} + +impl ToCss for RepeatCount { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + RepeatCount::Number(ref c) => c.to_css(dest), + RepeatCount::AutoFill => dest.write_str("auto-fill"), + RepeatCount::AutoFit => dest.write_str("auto-fit"), + } + } +} + +impl Parse for RepeatCount { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(i) = input.try(|i| Integer::parse(context, i)) { + if i.value() > 0 { + Ok(RepeatCount::Number(i)) + } else { + Err(()) + } + } else { + match_ignore_ascii_case! { &input.expect_ident()?, + "auto-fill" => Ok(RepeatCount::AutoFill), + "auto-fit" => Ok(RepeatCount::AutoFit), + _ => Err(()), + } + } + } +} + +impl ComputedValueAsSpecified for RepeatCount {} +no_viewport_percentage!(RepeatCount); + +/// 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 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 ToComputedValue for TrackRepeat { + type ComputedValue = TrackRepeat; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + // If the repeat count is numeric, then expand the values and merge accordingly. + if let RepeatCount::Number(num) = self.count { + let mut line_names = vec![]; + let mut track_sizes = vec![]; + let mut prev_names = vec![]; + + for _ in 0..num.value() { + let mut names_iter = self.line_names.iter(); + for (size, names) in self.track_sizes.iter().zip(&mut names_iter) { + prev_names.extend_from_slice(&names); + line_names.push(mem::replace(&mut prev_names, vec![])); + track_sizes.push(size.to_computed_value(context)); + } + + if let Some(names) = names_iter.next() { + prev_names.extend_from_slice(&names); + } + } + + line_names.push(prev_names); + TrackRepeat { + count: self.count, + track_sizes: track_sizes, + line_names: line_names, + } + + } else { // if it's auto-fit/auto-fill, then it's left to the layout. + TrackRepeat { + count: self.count, + track_sizes: self.track_sizes.iter() + .map(|l| l.to_computed_value(context)) + .collect(), + line_names: self.line_names.clone(), + } + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + TrackRepeat { + count: computed.count, + track_sizes: computed.track_sizes.iter() + .map(ToComputedValue::from_computed_value) + .collect(), + line_names: computed.line_names.clone(), + } + } +} + +/// 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>, +} + +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(()) + } +} diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 983246b9885..65fca55feb6 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -16,6 +16,7 @@ use super::CustomIdent; pub use self::basic_shape::serialize_radius_values; pub mod basic_shape; +pub mod grid; pub mod image; pub mod position; diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index 0633a0b0e80..07cb293e63c 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -2,157 +2,19 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//! Necessary types for [grid](https://drafts.csswg.org/css-grid/). +//! CSS handling for the computed value of +//! [grids](https://drafts.csswg.org/css-grid/) -use cssparser::{Parser, Token, serialize_identifier}; +use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; -use std::{fmt, mem, usize}; +use std::{mem, usize}; use std::ascii::AsciiExt; -use style_traits::{HasViewportPercentage, ToCss}; +use style_traits::HasViewportPercentage; use values::{CSSFloat, CustomIdent, Either}; -use values::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue}; -use values::specified::{Integer, LengthOrPercentage}; - -#[derive(PartialEq, Clone, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// A `` type. -/// -/// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line -pub struct GridLine { - /// Flag to check whether it's a `span` keyword. - pub is_span: bool, - /// A custom identifier for named lines. - /// - /// https://drafts.csswg.org/css-grid/#grid-placement-slot - pub ident: Option, - /// Denotes the nth grid line from grid item's placement. - pub line_num: Option, -} - -impl GridLine { - /// Check whether this `` represents an `auto` value. - pub fn is_auto(&self) -> bool { - self.ident.is_none() && self.line_num.is_none() && !self.is_span - } -} - -impl Default for GridLine { - fn default() -> Self { - GridLine { - is_span: false, - ident: None, - line_num: None, - } - } -} - -impl ToCss for GridLine { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.is_auto() { - return dest.write_str("auto") - } - - if self.is_span { - dest.write_str("span")?; - } - - if let Some(i) = self.line_num { - write!(dest, " {}", i.value)?; - } - - if let Some(ref s) = self.ident { - dest.write_str(" ")?; - serialize_identifier(s, dest)?; - } - - Ok(()) - } -} - -impl Parse for GridLine { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - let mut grid_line = Default::default(); - if input.try(|i| i.expect_ident_matching("auto")).is_ok() { - return Ok(grid_line) - } - - for _ in 0..3 { // Maximum possible entities for - if input.try(|i| i.expect_ident_matching("span")).is_ok() { - if grid_line.is_span || grid_line.line_num.is_some() || grid_line.ident.is_some() { - return Err(()) // span (if specified) should be first - } - grid_line.is_span = true; // span (if specified) should be first - } else if let Ok(i) = input.try(|i| Integer::parse(context, i)) { - if i.value == 0 || grid_line.line_num.is_some() { - return Err(()) - } - grid_line.line_num = Some(i); - } else if let Ok(name) = input.try(|i| i.expect_ident()) { - if grid_line.ident.is_some() || CustomIdent::from_ident((&*name).into(), &[]).is_err() { - return Err(()) - } - grid_line.ident = Some(name.into_owned()); - } else { - break - } - } - - if grid_line.is_auto() { - return Err(()) - } - - if grid_line.is_span { - if let Some(i) = grid_line.line_num { - if i.value <= 0 { // disallow negative integers for grid spans - return Err(()) - } - } else if grid_line.ident.is_some() { // integer could be omitted - grid_line.line_num = Some(Integer::new(1)); - } else { - return Err(()) - } - } - - Ok(grid_line) - } -} - -impl ComputedValueAsSpecified for GridLine {} -no_viewport_percentage!(GridLine); - -define_css_keyword_enum!{ TrackKeyword: - "auto" => Auto, - "max-content" => MaxContent, - "min-content" => MinContent -} - -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// A track breadth for explicit grid track sizing. It's generic solely to -/// avoid re-implementing it for the computed type. -/// -/// https://drafts.csswg.org/css-grid/#typedef-track-breadth -pub enum TrackBreadth { - /// The generic type is almost always a non-negative `` - Breadth(L), - /// A flex fraction specified in `fr` units. - Flex(CSSFloat), - /// One of the track-sizing keywords (`auto`, `min-content`, `max-content`) - Keyword(TrackKeyword), -} - -impl TrackBreadth { - /// Check whether this is a `` (i.e., it only has ``) - /// - /// https://drafts.csswg.org/css-grid/#typedef-fixed-breadth - #[inline] - pub fn is_fixed(&self) -> bool { - match *self { - TrackBreadth::Breadth(ref _lop) => true, - _ => false, - } - } -} +use values::computed::{self, Context, ToComputedValue}; +use values::generics::grid::{RepeatCount, TrackBreadth, TrackKeyword, TrackRepeat}; +use values::generics::grid::{TrackSize, TrackList, TrackListType}; +use values::specified::LengthOrPercentage; /// Parse a single flexible length. pub fn parse_flex(input: &mut Parser) -> Result { @@ -177,16 +39,6 @@ impl Parse for TrackBreadth { } } -impl ToCss for TrackBreadth { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - TrackBreadth::Breadth(ref lop) => lop.to_css(dest), - TrackBreadth::Flex(ref value) => write!(dest, "{}fr", value), - TrackBreadth::Keyword(ref k) => k.to_css(dest), - } - } -} - impl HasViewportPercentage for TrackBreadth { #[inline] fn has_viewport_percentage(&self) -> bool { @@ -198,81 +50,6 @@ impl HasViewportPercentage for TrackBreadth { } } -impl ToComputedValue for TrackBreadth { - type ComputedValue = TrackBreadth; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - TrackBreadth::Breadth(ref lop) => TrackBreadth::Breadth(lop.to_computed_value(context)), - TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr), - TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - TrackBreadth::Breadth(ref lop) => - TrackBreadth::Breadth(ToComputedValue::from_computed_value(lop)), - TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr), - TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k), - } - } -} - -#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -/// A `` type for explicit grid track sizing. Like ``, this is -/// generic only to avoid code bloat. It only takes `` -/// -/// https://drafts.csswg.org/css-grid/#typedef-track-size -pub enum TrackSize { - /// A flexible `` - Breadth(TrackBreadth), - /// A `minmax` function for a range over an inflexible `` - /// and a flexible `` - /// - /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax - MinMax(TrackBreadth, TrackBreadth), - /// A `fit-content` function. - /// - /// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content - FitContent(L), -} - -impl TrackSize { - /// Check whether this is a `` - /// - /// https://drafts.csswg.org/css-grid/#typedef-fixed-size - pub fn is_fixed(&self) -> bool { - match *self { - TrackSize::Breadth(ref breadth) => breadth.is_fixed(), - // For minmax function, it could be either - // minmax(, ) or minmax(, ), - // and since both variants are a subset of minmax(, ), we only - // need to make sure that they're fixed. So, we don't have to modify the parsing function. - TrackSize::MinMax(ref breadth_1, ref breadth_2) => { - if breadth_1.is_fixed() { - return true // the second value is always a - } - - match *breadth_1 { - TrackBreadth::Flex(_) => false, // should be at this point - _ => breadth_2.is_fixed(), - } - }, - TrackSize::FitContent(_) => false, - } - } -} - -impl Default for TrackSize { - fn default() -> Self { - TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto)) - } -} - impl Parse for TrackSize { fn parse(context: &ParserContext, input: &mut Parser) -> Result { if let Ok(b) = input.try(|i| TrackBreadth::parse(context, i)) { @@ -301,59 +78,6 @@ impl Parse for TrackSize { } } -impl ToCss for TrackSize { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - TrackSize::Breadth(ref b) => b.to_css(dest), - TrackSize::MinMax(ref infexible, ref flexible) => { - dest.write_str("minmax(")?; - infexible.to_css(dest)?; - dest.write_str(", ")?; - flexible.to_css(dest)?; - dest.write_str(")") - }, - TrackSize::FitContent(ref lop) => { - dest.write_str("fit-content(")?; - lop.to_css(dest)?; - dest.write_str(")") - }, - } - } -} - -impl ToComputedValue for TrackSize { - type ComputedValue = TrackSize; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - TrackSize::Breadth(ref b) => match *b { - // outside `minmax()` expands to `mimmax(auto, )` - // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-flex - TrackBreadth::Flex(f) => - TrackSize::MinMax(TrackBreadth::Keyword(TrackKeyword::Auto), TrackBreadth::Flex(f)), - _ => TrackSize::Breadth(b.to_computed_value(context)), - }, - TrackSize::MinMax(ref b_1, ref b_2) => - TrackSize::MinMax(b_1.to_computed_value(context), b_2.to_computed_value(context)), - TrackSize::FitContent(ref lop) => TrackSize::FitContent(lop.to_computed_value(context)), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - TrackSize::Breadth(ref b) => - TrackSize::Breadth(ToComputedValue::from_computed_value(b)), - TrackSize::MinMax(ref b_1, ref b_2) => - TrackSize::MinMax(ToComputedValue::from_computed_value(b_1), - ToComputedValue::from_computed_value(b_2)), - TrackSize::FitContent(ref lop) => - TrackSize::FitContent(ToComputedValue::from_computed_value(lop)), - } - } -} - /// Parse the grid line names into a vector of owned strings. /// /// https://drafts.csswg.org/css-grid/#typedef-line-names @@ -373,69 +97,6 @@ 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 -#[derive(Clone, Copy, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum RepeatCount { - /// A positive integer. This is allowed only for `` and `` - Number(Integer), - /// An `` keyword allowed only for `` - AutoFill, - /// An `` keyword allowed only for `` - AutoFit, -} - -impl Parse for RepeatCount { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(i) = input.try(|i| Integer::parse(context, i)) { - if i.value > 0 { - Ok(RepeatCount::Number(i)) - } else { - Err(()) - } - } else { - match_ignore_ascii_case! { &input.expect_ident()?, - "auto-fill" => Ok(RepeatCount::AutoFill), - "auto-fit" => Ok(RepeatCount::AutoFit), - _ => Err(()), - } - } - } -} - -impl ToCss for RepeatCount { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - RepeatCount::Number(ref c) => c.to_css(dest), - RepeatCount::AutoFill => dest.write_str("auto-fill"), - RepeatCount::AutoFit => dest.write_str("auto-fit"), - } - } -} - -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 @@ -450,25 +111,6 @@ enum RepeatType { 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), ()> { @@ -525,32 +167,6 @@ impl TrackRepeat { } } -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 { @@ -558,112 +174,6 @@ impl HasViewportPercentage for TrackRepeat { } } -impl ToComputedValue for TrackRepeat { - type ComputedValue = TrackRepeat; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - // If the repeat count is numeric, then expand the values and merge accordingly. - if let RepeatCount::Number(num) = self.count { - let mut line_names = vec![]; - let mut track_sizes = vec![]; - let mut prev_names = vec![]; - - for _ in 0..num.value { - let mut names_iter = self.line_names.iter(); - for (size, names) in self.track_sizes.iter().zip(&mut names_iter) { - prev_names.extend_from_slice(&names); - line_names.push(mem::replace(&mut prev_names, vec![])); - track_sizes.push(size.to_computed_value(context)); - } - - if let Some(names) = names_iter.next() { - prev_names.extend_from_slice(&names); - } - } - - line_names.push(prev_names); - TrackRepeat { - count: self.count, - track_sizes: track_sizes, - line_names: line_names, - } - - } else { // if it's auto-fit/auto-fill, then it's left to the layout. - TrackRepeat { - count: self.count, - track_sizes: self.track_sizes.iter() - .map(|l| l.to_computed_value(context)) - .collect(), - line_names: self.line_names.clone(), - } - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - TrackRepeat { - count: computed.count, - track_sizes: computed.track_sizes.iter() - .map(ToComputedValue::from_computed_value) - .collect(), - line_names: computed.line_names.clone(), - } - } -} - -/// 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 @@ -739,49 +249,6 @@ impl Parse for TrackList { } } -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 { diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 88a44b50b3e..8d75e0c4a07 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -12,8 +12,7 @@ use cssparser::{self, Parser, Token}; use euclid::size::Size2D; use itoa; use parser::{ParserContext, Parse}; -use self::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; -use self::grid::{TrackList as GenericTrackList, TrackSizeOrRepeat}; +use self::grid::TrackSizeOrRepeat; use self::url::SpecifiedUrl; use std::ascii::AsciiExt; use std::f32; @@ -25,12 +24,14 @@ use super::{Auto, CSSFloat, CSSInteger, Either, None_}; use super::computed::{self, Context}; use super::computed::{Shadow as ComputedShadow, ToComputedValue}; use super::generics::BorderRadiusSize as GenericBorderRadiusSize; +use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; +use super::generics::grid::TrackList as GenericTrackList; use values::specified::calc::CalcNode; #[cfg(feature = "gecko")] pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use self::color::Color; -pub use self::grid::{GridLine, TrackKeyword}; +pub use super::generics::grid::GridLine; pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer}; pub use self::length::AbsoluteLength;