aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/properties/longhand/position.mako.rs8
-rw-r--r--components/style/properties/shorthand/position.mako.rs256
-rw-r--r--components/style/values/generics/grid.rs30
3 files changed, 287 insertions, 7 deletions
diff --git a/components/style/properties/longhand/position.mako.rs b/components/style/properties/longhand/position.mako.rs
index 324b0112f02..754fc94732b 100644
--- a/components/style/properties/longhand/position.mako.rs
+++ b/components/style/properties/longhand/position.mako.rs
@@ -223,6 +223,7 @@ ${helpers.predefined_type("object-position",
${helpers.predefined_type("grid-%s-gap" % kind,
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
+ "parse_non_negative",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
animation_value_type="ComputedValue",
products="gecko")}
@@ -396,6 +397,13 @@ ${helpers.predefined_type("object-position",
while let Ok(string) = input.try(Parser::expect_string) {
strings.push(string.into_owned().into_boxed_str());
}
+
+ TemplateAreas::from_vec(strings)
+ }
+ }
+
+ impl TemplateAreas {
+ pub fn from_vec(strings: Vec<Box<str>>) -> Result<TemplateAreas, ()> {
if strings.is_empty() {
return Err(());
}
diff --git a/components/style/properties/shorthand/position.mako.rs b/components/style/properties/shorthand/position.mako.rs
index 11b2cd41b0b..94158701ac6 100644
--- a/components/style/properties/shorthand/position.mako.rs
+++ b/components/style/properties/shorthand/position.mako.rs
@@ -241,6 +241,262 @@
}
</%helpers:shorthand>
+<%helpers:shorthand name="grid-template"
+ sub_properties="grid-template-rows grid-template-columns grid-template-areas"
+ spec="https://drafts.csswg.org/css-grid/#propdef-grid-template"
+ disable_when_testing="True"
+ products="gecko">
+ use cssparser::serialize_string;
+ use parser::Parse;
+ use properties::longhands::grid_template_rows;
+ use properties::longhands::grid_template_areas::TemplateAreas;
+ use values::{Either, None_};
+ use values::generics::grid::{TrackSize, TrackList, TrackListType, concat_serialize_idents};
+ use values::specified::TrackListOrNone;
+ use values::specified::grid::parse_line_names;
+
+ /// Parsing for `<grid-template>` shorthand (also used by `grid` shorthand).
+ pub fn parse_grid_template(context: &ParserContext, input: &mut Parser)
+ -> Result<(TrackListOrNone, TrackListOrNone, Either<TemplateAreas, None_>), ()> {
+ if input.try(|i| i.expect_ident_matching("none")).is_ok() {
+ return Ok((Either::Second(None_), Either::Second(None_), Either::Second(None_)))
+ }
+
+ let first_line_names = input.try(parse_line_names).unwrap_or(vec![]);
+ if let Ok(s) = input.try(Parser::expect_string) {
+ let mut strings = vec![];
+ let mut values = vec![];
+ let mut line_names = vec![];
+ let mut names = first_line_names;
+ let mut string = s.into_owned().into_boxed_str();
+ loop {
+ line_names.push(names);
+ strings.push(string);
+ let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default();
+ values.push(Either::First(size));
+ names = input.try(parse_line_names).unwrap_or(vec![]);
+ if let Ok(v) = input.try(parse_line_names) {
+ names.extend(v);
+ }
+
+ string = match input.try(Parser::expect_string) {
+ Ok(s) => s.into_owned().into_boxed_str(),
+ _ => { // only the named area determines whether we should bail out
+ line_names.push(names);
+ break
+ },
+ };
+ }
+
+ if line_names.len() == values.len() {
+ line_names.push(vec![]); // should be one longer than track sizes
+ }
+
+ let template_areas = TemplateAreas::from_vec(strings)?;
+ let template_rows = TrackList {
+ list_type: TrackListType::Normal,
+ values: values,
+ line_names: line_names,
+ auto_repeat: None,
+ };
+
+ let template_cols = if input.try(|i| i.expect_delim('/')).is_ok() {
+ let track_list = TrackList::parse(context, input)?;
+ if track_list.list_type != TrackListType::Explicit {
+ return Err(())
+ }
+
+ Either::First(track_list)
+ } else {
+ Either::Second(None_)
+ };
+
+ Ok((Either::First(template_rows), template_cols, Either::First(template_areas)))
+ } else {
+ let mut template_rows = grid_template_rows::parse(context, input)?;
+ if let Either::First(ref mut list) = template_rows {
+ list.line_names[0] = first_line_names; // won't panic
+ }
+
+ Ok((template_rows, grid_template_rows::parse(context, input)?, Either::Second(None_)))
+ }
+ }
+
+ #[inline]
+ pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
+ let (rows, columns, areas) = parse_grid_template(context, input)?;
+ Ok(expanded! {
+ grid_template_rows: rows,
+ grid_template_columns: columns,
+ grid_template_areas: areas,
+ })
+ }
+
+ /// Serialization for `<grid-template>` shorthand (also used by `grid` shorthand).
+ pub fn serialize_grid_template<W>(template_rows: &TrackListOrNone,
+ template_columns: &TrackListOrNone,
+ template_areas: &Either<TemplateAreas, None_>,
+ dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *template_areas {
+ Either::Second(_none) => {
+ if template_rows == &Either::Second(None_) &&
+ template_columns == &Either::Second(None_) {
+ dest.write_str("none")
+ } else {
+ template_rows.to_css(dest)?;
+ dest.write_str(" / ")?;
+ template_columns.to_css(dest)
+ }
+ },
+ Either::First(ref areas) => {
+ let track_list = match *template_rows {
+ Either::First(ref list) => list,
+ Either::Second(_none) => unreachable!(), // should exist!
+ };
+
+ let mut names_iter = track_list.line_names.iter();
+ for (((i, string), names), size) in areas.strings.iter().enumerate()
+ .zip(&mut names_iter)
+ .zip(track_list.values.iter()) {
+ if i > 0 {
+ dest.write_str(" ")?;
+ }
+
+ if !names.is_empty() {
+ concat_serialize_idents("[", "] ", names, " ", dest)?;
+ }
+
+ serialize_string(string, dest)?;
+ dest.write_str(" ")?;
+ size.to_css(dest)?;
+ }
+
+ if let Some(names) = names_iter.next() {
+ concat_serialize_idents(" [", "]", names, " ", dest)?;
+ }
+
+ if let Either::First(ref list) = *template_columns {
+ dest.write_str(" / ")?;
+ list.to_css(dest)?;
+ }
+
+ Ok(())
+ },
+ }
+ }
+
+ impl<'a> ToCss for LonghandsToSerialize<'a> {
+ #[inline]
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ serialize_grid_template(self.grid_template_rows, self.grid_template_columns,
+ self.grid_template_areas, dest)
+ }
+ }
+</%helpers:shorthand>
+
+<%helpers:shorthand name="grid"
+ sub_properties="grid-template-rows grid-template-columns grid-template-areas
+ grid-auto-rows grid-auto-columns grid-row-gap grid-column-gap
+ grid-auto-flow"
+ spec="https://drafts.csswg.org/css-grid/#propdef-grid"
+ disable_when_testing="True"
+ products="gecko">
+ use properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
+ use properties::longhands::{grid_template_columns, grid_template_rows};
+ use properties::longhands::grid_auto_flow::computed_value::{AutoFlow, T as SpecifiedAutoFlow};
+ use values::{Either, None_};
+ use values::specified::{LengthOrPercentage, TrackSize};
+
+ pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
+ let mut temp_rows = Either::Second(None_);
+ let mut temp_cols = Either::Second(None_);
+ let mut temp_areas = Either::Second(None_);
+ let mut auto_rows = TrackSize::default();
+ let mut auto_cols = TrackSize::default();
+ let mut flow = grid_auto_flow::get_initial_value();
+
+ fn parse_auto_flow(input: &mut Parser, is_row: bool) -> Result<SpecifiedAutoFlow, ()> {
+ let mut auto_flow = None;
+ let mut dense = false;
+ for _ in 0..2 {
+ if input.try(|i| i.expect_ident_matching("auto-flow")).is_ok() {
+ auto_flow = if is_row {
+ Some(AutoFlow::Row)
+ } else {
+ Some(AutoFlow::Column)
+ };
+ } else if input.try(|i| i.expect_ident_matching("dense")).is_ok() {
+ dense = true;
+ } else {
+ break
+ }
+ }
+
+ auto_flow.map(|flow| {
+ SpecifiedAutoFlow {
+ autoflow: flow,
+ dense: dense,
+ }
+ }).ok_or(())
+ }
+
+ if let Ok((rows, cols, areas)) = input.try(|i| super::grid_template::parse_grid_template(context, i)) {
+ temp_rows = rows;
+ temp_cols = cols;
+ temp_areas = areas;
+ } else if let Ok(rows) = input.try(|i| grid_template_rows::parse(context, i)) {
+ temp_rows = rows;
+ input.expect_delim('/')?;
+ flow = parse_auto_flow(input, false)?;
+ auto_cols = grid_auto_columns::parse(context, input).unwrap_or_default();
+ } else {
+ flow = parse_auto_flow(input, true)?;
+ auto_rows = input.try(|i| grid_auto_rows::parse(context, i)).unwrap_or_default();
+ input.expect_delim('/')?;
+ temp_cols = grid_template_columns::parse(context, input)?;
+ }
+
+ Ok(expanded! {
+ grid_template_rows: temp_rows,
+ grid_template_columns: temp_cols,
+ grid_template_areas: temp_areas,
+ grid_auto_rows: auto_rows,
+ grid_auto_columns: auto_cols,
+ grid_auto_flow: flow,
+ // This shorthand also resets grid gap
+ grid_row_gap: LengthOrPercentage::zero(),
+ grid_column_gap: LengthOrPercentage::zero(),
+ })
+ }
+
+ impl<'a> ToCss for LonghandsToSerialize<'a> {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ if let Either::First(_) = *self.grid_template_areas {
+ super::grid_template::serialize_grid_template(self.grid_template_rows,
+ self.grid_template_columns,
+ self.grid_template_areas, dest)
+ } else if self.grid_auto_flow.autoflow == AutoFlow::Row {
+ self.grid_template_rows.to_css(dest)?;
+ dest.write_str(" / auto-flow")?;
+ if self.grid_auto_flow.dense {
+ dest.write_str(" dense")?;
+ }
+
+ self.grid_auto_columns.to_css(dest)
+ } else {
+ dest.write_str("auto-flow ")?;
+ if self.grid_auto_flow.dense {
+ dest.write_str("dense ")?;
+ }
+
+ self.grid_auto_rows.to_css(dest)?;
+ dest.write_str(" / ")?;
+ self.grid_template_columns.to_css(dest)
+ }
+ }
+ }
+</%helpers:shorthand>
+
<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
spec="https://drafts.csswg.org/css-align/#propdef-place-content"
products="gecko" disable_when_testing="True">
diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs
index 0e11143097b..73640a91e49 100644
--- a/components/style/values/generics/grid.rs
+++ b/components/style/values/generics/grid.rs
@@ -76,21 +76,35 @@ impl Parse for GridLine {
return Ok(grid_line)
}
+ // <custom-ident> | [ <integer> && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ]
+ // This <grid-line> horror is simply,
+ // [ span? && [ <custom-ident> || <integer> ] ]
+ // And, for some magical reason, "span" should be the first or last value and not in-between.
+ let mut val_before_span = false;
+
for _ in 0..3 { // Maximum possible entities for <grid-line>
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
+ if grid_line.is_span {
+ return Err(())
+ }
+
+ if grid_line.line_num.is_some() || grid_line.ident.is_some() {
+ val_before_span = true;
}
- grid_line.is_span = true; // span (if specified) should be first
+
+ grid_line.is_span = true;
} else if let Ok(i) = input.try(|i| Integer::parse(context, i)) {
- if i.value() == 0 || grid_line.line_num.is_some() {
+ if i.value() == 0 || val_before_span || 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() {
+ if val_before_span || grid_line.ident.is_some() ||
+ CustomIdent::from_ident((&*name).into(), &[]).is_err() {
return Err(())
}
+
grid_line.ident = Some(name.into_owned());
} else {
break
@@ -292,8 +306,10 @@ impl<L: ToComputedValue> ToComputedValue for TrackSize<L> {
}
}
-fn concat_serialize_idents<W>(prefix: &str, suffix: &str,
- slice: &[String], sep: &str, dest: &mut W) -> fmt::Result
+/// Helper function for serializing identifiers with a prefix and suffix, used
+/// for serializing <line-names> (in grid).
+pub fn concat_serialize_idents<W>(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() {