Auto merge of #15628 - Wafflespeanut:grid, r=Manishearth

Stylo: Implement grid-auto-{rows,columns}

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #15312 (github issue number if applicable).

<!-- Either: -->
- [x] These changes do not require tests because it's a stylo thing

r? @emilio (cc @Manishearth @upsuper)

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15628)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-02-20 01:11:03 -08:00 committed by GitHub
commit 30a31fb744
9 changed files with 318 additions and 38 deletions

View file

@ -285,6 +285,7 @@ mod bindings {
"mozilla::PropertyStyleAnimationValuePair",
"mozilla::TraversalRootBehavior",
"mozilla::StyleShapeRadius",
"mozilla::StyleGrid.*",
".*ThreadSafe.*Holder",
"AnonymousContent",
"AudioContext",

View file

@ -8,13 +8,14 @@
use app_units::Au;
use cssparser::RGBA;
use gecko_bindings::structs::{nsStyleCoord, StyleShapeRadius};
use gecko_bindings::structs::{nsStyleCoord, StyleGridTrackBreadth, StyleShapeRadius};
use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
use std::cmp::max;
use values::{Auto, Either, None_};
use values::computed::{Angle, LengthOrPercentageOrNone, Number};
use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use values::computed::basic_shape::ShapeRadius;
use values::specified::grid::{TrackBreadth, TrackKeyword};
/// A trait that defines an interface to convert from and to `nsStyleCoord`s.
pub trait GeckoStyleCoordConvertible : Sized {
@ -137,6 +138,39 @@ impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone {
}
}
impl<L: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for TrackBreadth<L> {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
match *self {
TrackBreadth::Breadth(ref lop) => lop.to_gecko_style_coord(coord),
TrackBreadth::Flex(fr) => coord.set_value(CoordDataValue::FlexFraction(fr)),
TrackBreadth::Keyword(TrackKeyword::Auto) => coord.set_value(CoordDataValue::Auto),
TrackBreadth::Keyword(TrackKeyword::MinContent) =>
coord.set_value(CoordDataValue::Enumerated(StyleGridTrackBreadth::MinContent as u32)),
TrackBreadth::Keyword(TrackKeyword::MaxContent) =>
coord.set_value(CoordDataValue::Enumerated(StyleGridTrackBreadth::MaxContent as u32)),
}
}
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
L::from_gecko_style_coord(coord).map(TrackBreadth::Breadth).or_else(|| {
match coord.as_value() {
CoordDataValue::Enumerated(v) => {
if v == StyleGridTrackBreadth::MinContent as u32 {
Some(TrackBreadth::Keyword(TrackKeyword::MinContent))
} else if v == StyleGridTrackBreadth::MaxContent as u32 {
Some(TrackBreadth::Keyword(TrackKeyword::MaxContent))
} else {
None
}
},
CoordDataValue::FlexFraction(fr) => Some(TrackBreadth::Flex(fr)),
CoordDataValue::Auto => Some(TrackBreadth::Keyword(TrackKeyword::Auto)),
_ => L::from_gecko_style_coord(coord).map(TrackBreadth::Breadth),
}
})
}
}
impl GeckoStyleCoordConvertible for ShapeRadius {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
match *self {

View file

@ -537,12 +537,6 @@ pub mod root {
pub const NS_STYLE_GRID_AUTO_FLOW_COLUMN: ::std::os::raw::c_uint = 2;
pub const NS_STYLE_GRID_AUTO_FLOW_DENSE: ::std::os::raw::c_uint = 4;
pub const NS_STYLE_GRID_TEMPLATE_SUBGRID: ::std::os::raw::c_uint = 0;
pub const NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT: ::std::os::raw::c_uint
=
1;
pub const NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT: ::std::os::raw::c_uint
=
2;
pub const NS_STYLE_GRID_REPEAT_AUTO_FILL: ::std::os::raw::c_uint = 0;
pub const NS_STYLE_GRID_REPEAT_AUTO_FIT: ::std::os::raw::c_uint = 1;
pub const NS_STYLE_WIDTH_MAX_CONTENT: ::std::os::raw::c_uint = 0;
@ -5813,6 +5807,9 @@ pub mod root {
MozGroupbox = 37,
MozPopup = 38,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum StyleGridTrackBreadth { MaxContent = 1, MinContent = 2, }
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct WritingMode([u8; 0]);

View file

@ -537,12 +537,6 @@ pub mod root {
pub const NS_STYLE_GRID_AUTO_FLOW_COLUMN: ::std::os::raw::c_uint = 2;
pub const NS_STYLE_GRID_AUTO_FLOW_DENSE: ::std::os::raw::c_uint = 4;
pub const NS_STYLE_GRID_TEMPLATE_SUBGRID: ::std::os::raw::c_uint = 0;
pub const NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT: ::std::os::raw::c_uint
=
1;
pub const NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT: ::std::os::raw::c_uint
=
2;
pub const NS_STYLE_GRID_REPEAT_AUTO_FILL: ::std::os::raw::c_uint = 0;
pub const NS_STYLE_GRID_REPEAT_AUTO_FIT: ::std::os::raw::c_uint = 1;
pub const NS_STYLE_WIDTH_MAX_CONTENT: ::std::os::raw::c_uint = 0;
@ -5653,6 +5647,9 @@ pub mod root {
MozGroupbox = 37,
MozPopup = 38,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum StyleGridTrackBreadth { MaxContent = 1, MinContent = 2, }
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct WritingMode([u8; 0]);

View file

@ -961,7 +961,7 @@ fn static_assert() {
<%self:impl_trait style_struct_name="Position"
skip_longhands="${skip_position_longhands} z-index box-sizing order align-content
justify-content align-self justify-self align-items
justify-items">
justify-items grid-auto-rows grid-auto-columns">
% for side in SIDES:
<% impl_split_style_coord("%s" % side.ident,
"mOffset",
@ -1083,6 +1083,36 @@ fn static_assert() {
}
% endfor
% for kind in ["rows", "columns"]:
pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_rows::computed_value::T) {
use values::specified::grid::TrackSize;
match v {
TrackSize::FitContent(lop) => {
// Gecko sets min value to None and max value to the actual value in fit-content
// https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8221
self.gecko.mGridAuto${kind.title()}Min.set_value(CoordDataValue::None);
lop.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Max);
},
TrackSize::Breadth(breadth) => {
// Set the value to both fields if there's one breadth value
// https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8230
breadth.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Min);
breadth.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Max);
},
TrackSize::MinMax(min, max) => {
min.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Min);
max.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Max);
},
}
}
pub fn copy_grid_auto_${kind}_from(&mut self, other: &Self) {
self.gecko.mGridAuto${kind.title()}Min.copy_from(&other.gecko.mGridAuto${kind.title()}Min);
self.gecko.mGridAuto${kind.title()}Max.copy_from(&other.gecko.mGridAuto${kind.title()}Max);
}
% endfor
</%self:impl_trait>
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +

View file

@ -255,28 +255,31 @@ ${helpers.predefined_type("object-position",
spec="https://drafts.csswg.org/css-images-3/#the-object-position",
animatable=True)}
<% grid_longhands = ["grid-row-start", "grid-row-end", "grid-column-start", "grid-column-end"] %>
% for kind in ["row", "column"]:
${helpers.predefined_type("grid-%s-gap" % kind,
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
animatable=True,
products="gecko")}
% for longhand in grid_longhands:
${helpers.predefined_type("%s" % longhand,
"GridLine",
% for range in ["start", "end"]:
${helpers.predefined_type("grid-%s-%s" % (kind, range),
"GridLine",
"Default::default()",
animatable=False,
spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
products="gecko",
boxed=True)}
% endfor
// NOTE: According to the spec, this should handle multiple values of `<track-size>`,
// but gecko supports only a single value
${helpers.predefined_type("grid-auto-%ss" % kind,
"TrackSize",
"Default::default()",
animatable=False,
spec="https://drafts.csswg.org/css-grid/#propdef-%s" % longhand,
spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
products="gecko",
boxed=True)}
% endfor
${helpers.predefined_type("grid-row-gap",
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-row-gap",
animatable=True,
products="gecko")}
${helpers.predefined_type("grid-column-gap",
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-column-gap",
animatable=True,
products="gecko")}

View file

@ -11,6 +11,7 @@ use properties::ComputedValues;
use std::fmt;
use style_traits::ToCss;
use super::{CSSFloat, RGBA, specified};
use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
pub use cssparser::Color as CSSColor;
pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientKind, Image};
@ -326,6 +327,12 @@ impl ToCss for ClipRect {
/// rect(...) | auto
pub type ClipRectOrAuto = Either<ClipRect, Auto>;
/// The computed value of a grid `<track-breadth>`
pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>;
/// The computed value of a grid `<track-size>`
pub type TrackSize = GenericTrackSize<LengthOrPercentage>;
impl ClipRectOrAuto {
/// Return an auto (default for clip-rect and image-region) value
pub fn auto() -> Self {

View file

@ -2,22 +2,30 @@
* 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/. */
//! A grid line type.
//! Necessary types for [grid](https://drafts.csswg.org/css-grid/).
use cssparser::Parser;
use cssparser::{Parser, Token};
use parser::{Parse, ParserContext};
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
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 `<grid-line>` 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<String>,
/// Denotes the nth grid line from grid item's placement.
pub integer: Option<i32>,
}
@ -97,3 +105,199 @@ impl Parse for GridLine {
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<L> {
/// The generic type is almost always a non-negative `<length-percentage>`
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<CSSFloat, ()> {
match try!(input.next()) {
Token::Dimension(ref value, ref unit) if unit.to_lowercase() == "fr" && value.value.is_sign_positive()
=> Ok(value.value),
_ => Err(()),
}
}
impl Parse for TrackBreadth<LengthOrPercentage> {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
if let Ok(lop) = input.try(LengthOrPercentage::parse_non_negative) {
Ok(TrackBreadth::Breadth(lop))
} else {
if let Ok(f) = input.try(parse_flex) {
Ok(TrackBreadth::Flex(f))
} else {
TrackKeyword::parse(input).map(TrackBreadth::Keyword)
}
}
}
}
impl<L: ToCss> ToCss for TrackBreadth<L> {
fn to_css<W>(&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<LengthOrPercentage> {
#[inline]
fn has_viewport_percentage(&self) -> bool {
if let TrackBreadth::Breadth(ref lop) = *self {
lop.has_viewport_percentage()
} else {
false
}
}
}
impl<L: ToComputedValue> ToComputedValue for TrackBreadth<L> {
type ComputedValue = TrackBreadth<L::ComputedValue>;
#[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 `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is
/// generic only to avoid code bloat. It only takes `<length-percentage>`
///
/// https://drafts.csswg.org/css-grid/#typedef-track-size
pub enum TrackSize<L> {
/// A flexible `<track-breadth>`
Breadth(TrackBreadth<L>),
/// A `minmax` function for a range over an inflexible `<track-breadth>`
/// and a flexible `<track-breadth>`
///
/// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax
MinMax(TrackBreadth<L>, TrackBreadth<L>),
/// A `fit-content` function.
///
/// https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content
FitContent(L),
}
impl<L> Default for TrackSize<L> {
fn default() -> Self {
TrackSize::Breadth(TrackBreadth::Keyword(TrackKeyword::Auto))
}
}
impl Parse for TrackSize<LengthOrPercentage> {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
if let Ok(b) = input.try(|i| TrackBreadth::parse(context, i)) {
Ok(TrackSize::Breadth(b))
} else {
if input.try(|i| i.expect_function_matching("minmax")).is_ok() {
Ok(try!(input.parse_nested_block(|input| {
let inflexible_breadth = if let Ok(lop) = input.try(LengthOrPercentage::parse_non_negative) {
Ok(TrackBreadth::Breadth(lop))
} else {
TrackKeyword::parse(input).map(TrackBreadth::Keyword)
};
try!(input.expect_comma());
Ok(TrackSize::MinMax(try!(inflexible_breadth), try!(TrackBreadth::parse(context, input))))
})))
} else {
try!(input.expect_function_matching("fit-content"));
Ok(try!(LengthOrPercentage::parse(context, input).map(TrackSize::FitContent)))
}
}
}
}
impl<L: ToCss> ToCss for TrackSize<L> {
fn to_css<W>(&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<LengthOrPercentage> {
#[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<L: ToComputedValue> ToComputedValue for TrackSize<L> {
type ComputedValue = TrackSize<L::ComputedValue>;
#[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)),
}
}
}

View file

@ -10,6 +10,7 @@ use app_units::Au;
use cssparser::{self, Parser, Token};
use euclid::size::Size2D;
use parser::{ParserContext, Parse};
use self::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
use self::url::SpecifiedUrl;
use std::ascii::AsciiExt;
use std::f32::consts::PI;
@ -22,7 +23,7 @@ use super::computed::Shadow as ComputedShadow;
#[cfg(feature = "gecko")]
pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
pub use self::grid::GridLine;
pub use self::grid::{GridLine, TrackKeyword};
pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
pub use self::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword};
pub use self::image::{SizeKeyword, VerticalDirection};
@ -554,6 +555,12 @@ impl ToCss for Opacity {
#[allow(missing_docs)]
pub type UrlOrNone = Either<SpecifiedUrl, None_>;
/// The specified value of a grid `<track-breadth>`
pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>;
/// The specified value of a grid `<track-size>`
pub type TrackSize = GenericTrackSize<LengthOrPercentage>;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]