/* 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/. */ //! Necessary types for [grid](https://drafts.csswg.org/css-grid/). use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; use std::ascii::AsciiExt; use std::fmt; use style_traits::ToCss; use values::{CSSFloat, HasViewportPercentage}; use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; use values::specified::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 #[allow(missing_docs)] 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 integer: Option, } impl Default for GridLine { fn default() -> Self { GridLine { is_span: false, ident: None, integer: None, } } } impl ToCss for GridLine { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { if !self.is_span && self.ident.is_none() && self.integer.is_none() { return dest.write_str("auto") } if self.is_span { try!(dest.write_str("span")); } if let Some(i) = self.integer { try!(write!(dest, " {}", i)); } if let Some(ref s) = self.ident { try!(write!(dest, " {}", s)); } 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 { return Err(()) } grid_line.is_span = true; } else if let Ok(i) = input.try(|i| i.expect_integer()) { if i == 0 || grid_line.integer.is_some() { return Err(()) } grid_line.integer = Some(i); } else if let Ok(name) = input.try(|i| i.expect_ident()) { if grid_line.ident.is_some() { return Err(()) } grid_line.ident = Some(name.into_owned()); } else { break } } if grid_line.is_span { if let Some(i) = grid_line.integer { if i < 0 { // disallow negative integers for grid spans return Err(()) } } else { grid_line.integer = Some(1); } } 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), } /// Parse a single flexible length. pub fn parse_flex(input: &mut Parser) -> Result { match try!(input.next()) { Token::Dimension(ref value, ref unit) if unit.eq_ignore_ascii_case("fr") && value.value.is_sign_positive() => Ok(value.value), _ => Err(()), } } impl Parse for TrackBreadth { fn parse(context: &ParserContext, input: &mut Parser) -> Result { if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) { return Ok(TrackBreadth::Breadth(lop)) } if let Ok(f) = input.try(parse_flex) { return Ok(TrackBreadth::Flex(f)) } TrackKeyword::parse(input).map(TrackBreadth::Keyword) } } 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 { if let TrackBreadth::Breadth(ref lop) = *self { lop.has_viewport_percentage() } else { false } } } 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, PartialEq, Debug)] #[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 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)) { return Ok(TrackSize::Breadth(b)) } if input.try(|i| i.expect_function_matching("minmax")).is_ok() { return input.parse_nested_block(|input| { let inflexible_breadth = match input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) { Ok(lop) => TrackBreadth::Breadth(lop), Err(..) => { let keyword = try!(TrackKeyword::parse(input)); TrackBreadth::Keyword(keyword) } }; try!(input.expect_comma()); Ok(TrackSize::MinMax(inflexible_breadth, try!(TrackBreadth::parse(context, input)))) }); } try!(input.expect_function_matching("fit-content")); // FIXME(emilio): This needs a parse_nested_block, doesn't it? Ok(try!(LengthOrPercentage::parse(context, input).map(TrackSize::FitContent))) } } 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) => { try!(dest.write_str("minmax(")); try!(infexible.to_css(dest)); try!(dest.write_str(",")); try!(flexible.to_css(dest)); dest.write_str(")") }, TrackSize::FitContent(ref lop) => { try!(dest.write_str("fit-content(")); try!(lop.to_css(dest)); dest.write_str(")") }, } } } impl HasViewportPercentage for TrackSize { #[inline] fn has_viewport_percentage(&self) -> bool { match *self { TrackSize::Breadth(ref b) => b.has_viewport_percentage(), TrackSize::MinMax(ref inf_b, ref b) => inf_b.has_viewport_percentage() || b.has_viewport_percentage(), TrackSize::FitContent(ref lop) => lop.has_viewport_percentage(), } } } impl ToComputedValue for TrackSize { type ComputedValue = TrackSize; #[inline] fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { TrackSize::Breadth(ref b) => 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)), } } }