Bug 1105111 part 1: Add support for 'flex-basis:content' in the style system (servo side). r=xidorn draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Fri, 30 Mar 2018 16:18:48 -0700
changeset 775377 ddbab98713a1
parent 775351 99e609ea0c47
child 775378 856c8d061e46
child 775395 fa00ad1c6ad7
push id104705
push userdholbert@mozilla.com
push dateFri, 30 Mar 2018 23:37:28 +0000
reviewersxidorn
bugs1105111
milestone61.0a1
Bug 1105111 part 1: Add support for 'flex-basis:content' in the style system (servo side). r=xidorn (Patch by emilio, except for the line in gecko.mako.rs with... CoordDataValue::Enumerated(structs::NS_STYLE_FLEX_BASIS_CONTENT) ...which is by dholbert to integrate into gecko changes in next patch.) MozReview-Commit-ID: 5WhgHJJ0mDB
servo/components/layout/flex.rs
servo/components/style/gecko/values.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/values/computed/flex.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/percentage.rs
servo/components/style/values/generics/flex.rs
servo/components/style/values/specified/flex.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/layout/flex.rs
+++ b/servo/components/layout/flex.rs
@@ -70,38 +70,26 @@ impl AxisSize {
 
 /// This function accepts the flex-basis and the size property in main direction from style,
 /// and the container size, then return the used value of flex basis. it can be used to help
 /// determining the flex base size and to indicate whether the main size of the item
 /// is definite after flex size resolving.
 fn from_flex_basis(
     flex_basis: FlexBasis,
     main_length: LengthOrPercentageOrAuto,
-    containing_length: Option<Au>
+    containing_length: Au,
 ) -> MaybeAuto {
-    match (flex_basis, containing_length) {
-        (GenericFlexBasis::Length(LengthOrPercentage::Length(length)), _) =>
-            MaybeAuto::Specified(Au::from(length)),
-        (GenericFlexBasis::Length(LengthOrPercentage::Percentage(percent)), Some(size)) =>
-            MaybeAuto::Specified(size.scale_by(percent.0)),
-        (GenericFlexBasis::Length(LengthOrPercentage::Percentage(_)), None) =>
-            MaybeAuto::Auto,
-        (GenericFlexBasis::Length(LengthOrPercentage::Calc(calc)), _) =>
-            MaybeAuto::from_option(calc.to_used_value(containing_length)),
-        (GenericFlexBasis::Content, _) =>
-            MaybeAuto::Auto,
-        (GenericFlexBasis::Auto, Some(size)) =>
-            MaybeAuto::from_style(main_length, size),
-        (GenericFlexBasis::Auto, None) => {
-            if let LengthOrPercentageOrAuto::Length(length) = main_length {
-                MaybeAuto::Specified(Au::from(length))
-            } else {
-                MaybeAuto::Auto
-            }
-        }
+    let width = match flex_basis {
+        GenericFlexBasis::Content => return MaybeAuto::Auto,
+        GenericFlexBasis::Width(width) => width,
+    };
+
+    match width.0 {
+        LengthOrPercentageOrAuto::Auto => MaybeAuto::from_style(main_length, containing_length),
+        other => MaybeAuto::from_style(other, containing_length),
     }
 }
 
 /// Represents a child in a flex container. Most fields here are used in
 /// flex size resolving, and items are sorted by the 'order' property.
 #[derive(Debug, Serialize)]
 struct FlexItem {
     /// Main size of a flex item, used to store results of flexible length calcuation.
@@ -156,17 +144,17 @@ impl FlexItem {
         let block = flow.as_mut_block();
         match direction {
             // TODO(stshine): the definition of min-{width, height} in style component
             // should change to LengthOrPercentageOrAuto for automatic implied minimal size.
             // https://drafts.csswg.org/css-flexbox-1/#min-size-auto
             Direction::Inline => {
                 let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
                                             block.fragment.style.content_inline_size(),
-                                            Some(containing_length));
+                                            containing_length);
 
                 // These methods compute auto margins to zero length, which is exactly what we want.
                 block.fragment.compute_border_and_padding(containing_length);
                 block.fragment.compute_inline_direction_margins(containing_length);
                 block.fragment.compute_block_direction_margins(containing_length);
 
                 let (border_padding, margin) = block.fragment.surrounding_intrinsic_inline_size();
                 let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size
@@ -178,17 +166,17 @@ impl FlexItem {
                     block.fragment.style.max_inline_size()
                          .to_used_value(containing_length)
                          .unwrap_or(MAX_AU);
                 self.min_size = block.fragment.style.min_inline_size().to_used_value(containing_length);
             }
             Direction::Block => {
                 let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
                                             block.fragment.style.content_block_size(),
-                                            Some(containing_length));
+                                            containing_length);
                 let content_size = block.fragment.border_box.size.block
                     - block.fragment.border_padding.block_start_end()
                     + block.fragment.box_sizing_boundary(direction);
                 self.base_size = basis.specified_or_default(content_size);
                 self.max_size =
                     block.fragment.style.max_block_size()
                          .to_used_value(containing_length)
                          .unwrap_or(MAX_AU);
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -5,33 +5,35 @@
 #![allow(unsafe_code)]
 
 //! Different kind of helpers to interact with Gecko values.
 
 use Atom;
 use app_units::Au;
 use counter_style::{Symbol, Symbols};
 use cssparser::RGBA;
-use gecko_bindings::structs::{CounterStylePtr, nsStyleCoord};
+use gecko_bindings::structs::{self, CounterStylePtr, nsStyleCoord};
 use gecko_bindings::structs::{StyleGridTrackBreadth, StyleShapeRadius};
 use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use media_queries::Device;
 use nsstring::{nsACString, nsCStr};
 use std::cmp::max;
 use values::{Auto, Either, None_, Normal};
 use values::computed::{Angle, ExtremumLength, Length, LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::computed::{LengthOrPercentageOrNone, Number, NumberOrPercentage};
 use values::computed::{MaxLength, MozLength, Percentage};
 use values::computed::{NonNegativeLength, NonNegativeLengthOrPercentage, NonNegativeNumber};
 use values::computed::basic_shape::ShapeRadius as ComputedShapeRadius;
+use values::computed::FlexBasis as ComputedFlexBasis;
 use values::generics::{CounterStyleOrNone, NonNegative};
 use values::generics::basic_shape::ShapeRadius;
 use values::generics::box_::Perspective;
 use values::generics::gecko::ScrollSnapPoint;
 use values::generics::grid::{TrackBreadth, TrackKeyword};
+use values::generics::flex::FlexBasis;
 
 /// A trait that defines an interface to convert from and to `nsStyleCoord`s.
 pub trait GeckoStyleCoordConvertible : Sized {
     /// Convert this to a `nsStyleCoord`.
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T);
     /// Given a `nsStyleCoord`, try to get a value of this type..
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self>;
 }
@@ -54,16 +56,41 @@ impl<A: GeckoStyleCoordConvertible, B: G
 
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
         A::from_gecko_style_coord(coord)
           .map(Either::First)
           .or_else(|| B::from_gecko_style_coord(coord).map(Either::Second))
     }
 }
 
+impl GeckoStyleCoordConvertible for ComputedFlexBasis {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        match *self {
+            FlexBasis::Content => {
+                coord.set_value(
+                    CoordDataValue::Enumerated(structs::NS_STYLE_FLEX_BASIS_CONTENT)
+                )
+            },
+            FlexBasis::Width(ref w) => w.to_gecko_style_coord(coord),
+        }
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        if let Some(width) = MozLength::from_gecko_style_coord(coord) {
+            return Some(FlexBasis::Width(width))
+        }
+
+        if let CoordDataValue::Enumerated(structs::NS_STYLE_FLEX_BASIS_CONTENT) = coord.as_value() {
+            return Some(FlexBasis::Content)
+        }
+
+        None
+    }
+}
+
 impl GeckoStyleCoordConvertible for Number {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         coord.set_value(CoordDataValue::Factor(*self));
     }
 
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
         match coord.as_value() {
             CoordDataValue::Factor(f) => Some(f),
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1441,16 +1441,17 @@ impl Clone for ${style_struct.gecko_stru
     predefined_types = {
         "Color": impl_color,
         "GreaterThanOrEqualToOneNumber": impl_simple,
         "Integer": impl_simple,
         "length::LengthOrAuto": impl_style_coord,
         "length::LengthOrNormal": impl_style_coord,
         "length::NonNegativeLengthOrAuto": impl_style_coord,
         "length::NonNegativeLengthOrNormal": impl_style_coord,
+        "FlexBasis": impl_style_coord,
         "Length": impl_absolute_length,
         "LengthOrNormal": impl_style_coord,
         "LengthOrPercentage": impl_style_coord,
         "LengthOrPercentageOrAuto": impl_style_coord,
         "LengthOrPercentageOrNone": impl_style_coord,
         "MaxLength": impl_style_coord,
         "MozLength": impl_style_coord,
         "MozScriptMinSize": impl_absolute_length,
@@ -1743,24 +1744,24 @@ fn static_assert() {
     <% impl_split_style_coord("padding_%s" % side.ident,
                               "mPadding",
                               side.index) %>
     % endfor
 </%self:impl_trait>
 
 <% skip_position_longhands = " ".join(x.ident for x in SIDES + GRID_LINES) %>
 <%self:impl_trait style_struct_name="Position"
-                  skip_longhands="${skip_position_longhands} z-index order align-content
-                                  justify-content align-self justify-self align-items
-                                  justify-items grid-auto-rows grid-auto-columns grid-auto-flow
-                                  grid-template-areas grid-template-rows grid-template-columns">
+                  skip_longhands="${skip_position_longhands} z-index order
+                                  align-content justify-content align-self
+                                  justify-self align-items justify-items
+                                  grid-auto-rows grid-auto-columns
+                                  grid-auto-flow grid-template-areas
+                                  grid-template-rows grid-template-columns">
     % for side in SIDES:
-    <% impl_split_style_coord("%s" % side.ident,
-                              "mOffset",
-                              side.index) %>
+    <% impl_split_style_coord(side.ident, "mOffset", side.index) %>
     % endfor
 
     pub fn set_z_index(&mut self, v: longhands::z_index::computed_value::T) {
         match v {
             ZIndex::Integer(n) => self.gecko.mZIndex.set_value(CoordDataValue::Integer(n)),
             ZIndex::Auto => self.gecko.mZIndex.set_value(CoordDataValue::Auto),
         }
     }
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -169,42 +169,26 @@ macro_rules! impl_align_conversions {
 
 // https://drafts.csswg.org/css-flexbox/#propdef-order
 ${helpers.predefined_type("order", "Integer", "0",
                           extra_prefixes="webkit",
                           animation_value_type="ComputedValue",
                           spec="https://drafts.csswg.org/css-flexbox/#order-property",
                           servo_restyle_damage = "reflow")}
 
-% if product == "gecko":
-    // FIXME: Gecko doesn't support content value yet.
-    //
-    // FIXME(emilio): I suspect this property shouldn't allow quirks, and this
-    // was just a mistake, it's kind of justificable to support it given the
-    // spec grammar is just `content | <width>`, but other browsers don't...
-    ${helpers.predefined_type(
-        "flex-basis",
-        "MozLength",
-        "computed::MozLength::auto()",
-        extra_prefixes="webkit",
-        animation_value_type="MozLength",
-        allow_quirks=True,
-        spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
-        servo_restyle_damage = "reflow"
-    )}
-% else:
-    // FIXME: This property should be animatable.
-    ${helpers.predefined_type("flex-basis",
-                              "FlexBasis",
-                              "computed::FlexBasis::auto()",
-                              spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
-                              extra_prefixes="webkit",
-                              animation_value_type="none",
-                              servo_restyle_damage = "reflow")}
-% endif
+${helpers.predefined_type(
+    "flex-basis",
+    "FlexBasis",
+    "computed::FlexBasis::auto()",
+    spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
+    extra_prefixes="webkit",
+    animation_value_type="FlexBasis",
+    servo_restyle_damage = "reflow"
+)}
+
 % for (size, logical) in ALL_SIZES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-%s"
         if logical:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
     %>
     // NOTE: Block-size doesn't support -moz-*-content keywords, since they make
     // no sense on the block axis, but it simplifies things the have that it has
--- a/servo/components/style/values/computed/flex.rs
+++ b/servo/components/style/values/computed/flex.rs
@@ -1,11 +1,26 @@
 /* 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/. */
 
 //! Computed types for CSS values related to flexbox.
 
-use values::computed::length::LengthOrPercentage;
 use values::generics::flex::FlexBasis as GenericFlexBasis;
 
+/// The `width` value type.
+#[cfg(feature = "servo")]
+pub type Width = ::values::computed::NonNegativeLengthOrPercentageOrAuto;
+
+/// The `width` value type.
+#[cfg(feature = "gecko")]
+pub type Width = ::values::computed::MozLength;
+
 /// A computed value for the `flex-basis` property.
-pub type FlexBasis = GenericFlexBasis<LengthOrPercentage>;
+pub type FlexBasis = GenericFlexBasis<Width>;
+
+impl FlexBasis {
+    /// `auto`
+    #[inline]
+    pub fn auto() -> Self {
+        GenericFlexBasis::Width(Width::auto())
+    }
+}
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -422,20 +422,20 @@ impl LengthOrPercentage {
         }
     }
 
     /// Returns the clamped non-negative values.
     #[inline]
     pub fn clamp_to_non_negative(self) -> Self {
         match self {
             LengthOrPercentage::Length(length) => {
-                LengthOrPercentage::Length(Length::new(length.px().max(0.)))
+                LengthOrPercentage::Length(length.clamp_to_non_negative())
             },
             LengthOrPercentage::Percentage(percentage) => {
-                LengthOrPercentage::Percentage(Percentage(percentage.0.max(0.)))
+                LengthOrPercentage::Percentage(percentage.clamp_to_non_negative())
             },
             _ => self
         }
     }
 }
 
 impl ToComputedValue for specified::LengthOrPercentage {
     type ComputedValue = LengthOrPercentage;
@@ -506,29 +506,63 @@ impl LengthOrPercentageOrAuto {
     ) -> Result<SquaredDistance, ()> {
         <Option<CalcLengthOrPercentage>>::compute_squared_distance(
             &(*self).into(),
             &(*other).into(),
         )
     }
 }
 
+/// A wrapper of LengthOrPercentageOrAuto, whose value must be >= 0.
+pub type NonNegativeLengthOrPercentageOrAuto = NonNegative<LengthOrPercentageOrAuto>;
+
+impl NonNegativeLengthOrPercentageOrAuto {
+    /// `auto`
+    #[inline]
+    pub fn auto() -> Self {
+        NonNegative(LengthOrPercentageOrAuto::Auto)
+    }
+}
+
+impl ToAnimatedValue for NonNegativeLengthOrPercentageOrAuto {
+    type AnimatedValue = LengthOrPercentageOrAuto;
+
+    #[inline]
+    fn to_animated_value(self) -> Self::AnimatedValue {
+        self.0
+    }
+
+    #[inline]
+    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+        NonNegative(animated.clamp_to_non_negative())
+    }
+}
+
 impl LengthOrPercentageOrAuto {
     /// Returns true if the computed value is absolute 0 or 0%.
     ///
     /// (Returns false for calc() values, even if ones that may resolve to zero.)
     #[inline]
     pub fn is_definitely_zero(&self) -> bool {
         use self::LengthOrPercentageOrAuto::*;
         match *self {
             Length(l) => l.px() == 0.0,
             Percentage(p) => p.0 == 0.0,
             Calc(_) | Auto => false
         }
     }
+
+    fn clamp_to_non_negative(self) -> Self {
+        use self::LengthOrPercentageOrAuto::*;
+        match self {
+            Length(l) => Length(l.clamp_to_non_negative()),
+            Percentage(p) => Percentage(p.clamp_to_non_negative()),
+            _ => self,
+        }
+    }
 }
 
 impl ToComputedValue for specified::LengthOrPercentageOrAuto {
     type ComputedValue = LengthOrPercentageOrAuto;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAuto {
         match *self {
@@ -732,16 +766,21 @@ impl CSSPixelLength {
     }
 
     /// Return the containing pixel value.
     #[inline]
     pub fn px(&self) -> CSSFloat {
         self.0
     }
 
+    #[inline]
+    fn clamp_to_non_negative(self) -> Self {
+        Self::new(self.px().max(0.))
+    }
+
     /// Return the length with app_unit i32 type.
     #[inline]
     pub fn to_i32_au(&self) -> i32 {
         Au::from(*self).0
     }
 
     /// Return the absolute value of this length.
     pub fn abs(self) -> Self {
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -55,17 +55,18 @@ pub use self::image::{Gradient, Gradient
 pub use self::inherited_box::{Orientation, ImageOrientation};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 pub use super::specified::{BorderStyle, TextDecorationLine};
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
-pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength, NonNegativeLengthOrPercentage};
+pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength};
+pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto};
 pub use self::list::{ListStyleImage, Quotes};
 #[cfg(feature = "gecko")]
 pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::percentage::Percentage;
 pub use self::pointing::{CaretColor, Cursor};
 #[cfg(feature = "gecko")]
 pub use self::pointing::CursorImage;
--- a/servo/components/style/values/computed/percentage.rs
+++ b/servo/components/style/values/computed/percentage.rs
@@ -27,16 +27,22 @@ impl Percentage {
         Percentage(1.)
     }
 
     /// Returns the absolute value for this percentage.
     #[inline]
     pub fn abs(&self) -> Self {
         Percentage(self.0.abs())
     }
+
+    /// Clamps this percentage to a non-negative percentage.
+    #[inline]
+    pub fn clamp_to_non_negative(self) -> Self {
+        Percentage(self.0.max(0.))
+    }
 }
 
 impl ToCss for Percentage {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
         serialize_percentage(self.0, dest)
--- a/servo/components/style/values/generics/flex.rs
+++ b/servo/components/style/values/generics/flex.rs
@@ -1,37 +1,16 @@
 /* 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 CSS values related to flexbox.
 
-use values::computed::Percentage;
-
 /// A generic value for the `flex-basis` property.
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
-pub enum FlexBasis<LengthOrPercentage> {
-    /// `auto`
-    Auto,
+#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
+#[derive(ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+pub enum FlexBasis<Width> {
     /// `content`
     Content,
-    /// `<length-percentage>`
-    Length(LengthOrPercentage),
-}
-
-impl<L> FlexBasis<L> {
-    /// Returns `auto`.
-    #[inline]
-    pub fn auto() -> Self {
-        FlexBasis::Auto
-    }
+    /// `<width>`
+    Width(Width),
 }
-
-impl<L> FlexBasis<L>
-where Percentage: Into<L>,
-{
-    /// Returns `0%`.
-    #[inline]
-    pub fn zero_percent() -> Self {
-        FlexBasis::Length(Percentage(0.).into())
-    }
-}
--- a/servo/components/style/values/specified/flex.rs
+++ b/servo/components/style/values/specified/flex.rs
@@ -3,27 +3,47 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for CSS values related to flexbox.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use style_traits::ParseError;
 use values::generics::flex::FlexBasis as GenericFlexBasis;
-use values::specified::length::LengthOrPercentage;
+
+/// The `width` value type.
+#[cfg(feature = "servo")]
+pub type Width = ::values::specified::NonNegativeLengthOrPercentageOrAuto;
+
+/// The `width` value type.
+#[cfg(feature = "gecko")]
+pub type Width = ::values::specified::MozLength;
 
 /// A specified value for the `flex-basis` property.
-pub type FlexBasis = GenericFlexBasis<LengthOrPercentage>;
+pub type FlexBasis = GenericFlexBasis<Width>;
 
 impl Parse for FlexBasis {
     fn parse<'i, 't>(
         context: &ParserContext,
-        input: &mut Parser<'i, 't>)
-    -> Result<Self, ParseError<'i>> {
-        if let Ok(length) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
-            return Ok(GenericFlexBasis::Length(length));
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        if let Ok(width) = input.try(|i| Width::parse(context, i)) {
+            return Ok(GenericFlexBasis::Width(width));
         }
         try_match_ident_ignore_ascii_case! { input,
-            "auto" => Ok(GenericFlexBasis::Auto),
             "content" => Ok(GenericFlexBasis::Content),
         }
     }
 }
+
+impl FlexBasis {
+    /// `auto`
+    #[inline]
+    pub fn auto() -> Self {
+        GenericFlexBasis::Width(Width::auto())
+    }
+
+    /// `0%`
+    #[inline]
+    pub fn zero_percent() -> Self {
+        GenericFlexBasis::Width(Width::zero_percent())
+    }
+}
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -912,33 +912,62 @@ impl LengthOrPercentageOrAuto {
         LengthOrPercentageOrAuto::Length(NoCalcLength::zero())
     }
 
     /// Returns a value representing `0%`.
     #[inline]
     pub fn zero_percent() -> Self {
         LengthOrPercentageOrAuto::Percentage(computed::Percentage::zero())
     }
+
+    /// Parses, with quirks.
+    #[inline]
+    pub fn parse_quirky<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+        allow_quirks: AllowQuirks,
+    ) -> Result<Self, ParseError<'i>> {
+        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
+    }
 }
 
 impl Parse for LengthOrPercentageOrAuto {
     #[inline]
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         Self::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
-impl LengthOrPercentageOrAuto {
-    /// Parses, with quirks.
+/// A wrapper of LengthOrPercentageOrAuto, whose value must be >= 0.
+pub type NonNegativeLengthOrPercentageOrAuto = NonNegative<LengthOrPercentageOrAuto>;
+
+impl NonNegativeLengthOrPercentageOrAuto {
+    /// 0
+    #[inline]
+    pub fn zero() -> Self {
+        NonNegative(LengthOrPercentageOrAuto::zero())
+    }
+
+    /// 0%
     #[inline]
-    pub fn parse_quirky<'i, 't>(context: &ParserContext,
-                                input: &mut Parser<'i, 't>,
-                                allow_quirks: AllowQuirks)
-                                -> Result<Self, ParseError<'i>> {
-        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
+    pub fn zero_percent() -> Self {
+        NonNegative(LengthOrPercentageOrAuto::zero_percent())
+    }
+
+    /// `auto`
+    #[inline]
+    pub fn auto() -> Self {
+        NonNegative(LengthOrPercentageOrAuto::Auto)
+    }
+}
+
+impl Parse for NonNegativeLengthOrPercentageOrAuto {
+    #[inline]
+    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        Ok(NonNegative(LengthOrPercentageOrAuto::parse_non_negative(context, input)?))
     }
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `none` keyword.
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
 #[allow(missing_docs)]
 pub enum LengthOrPercentageOrNone {
     Length(NoCalcLength),
@@ -1082,18 +1111,21 @@ impl LengthOrNumber {
     /// Returns `0`.
     #[inline]
     pub fn zero() -> Self {
         Either::Second(Number::new(0.))
     }
 }
 
 /// A value suitable for a `min-width` or `min-height` property.
-/// Unlike `max-width` or `max-height` properties, a MozLength can be
-/// `auto`, and cannot be `none`.
+///
+/// Unlike `max-width` or `max-height` properties, a MozLength can be `auto`,
+/// and cannot be `none`.
+///
+/// Note that it only accepts non-negative values.
 #[allow(missing_docs)]
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
 pub enum MozLength {
     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
     ExtremumLength(ExtremumLength),
 }
 
 impl Parse for MozLength {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -50,17 +50,17 @@ pub use self::gecko::ScrollSnapPoint;
 pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
 pub use self::inherited_box::ImageOrientation;
 pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
 pub use self::length::{FontRelativeLength, Length, LengthOrNumber};
 pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{NoCalcLength, ViewportPercentageLength};
-pub use self::length::NonNegativeLengthOrPercentage;
+pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto};
 pub use self::list::{ListStyleImage, Quotes};
 #[cfg(feature = "gecko")]
 pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::rect::LengthOrNumberRect;
 pub use self::percentage::Percentage;
 pub use self::pointing::{CaretColor, Cursor};
 #[cfg(feature = "gecko")]