Bug 1374233 - Part 7: Add NonNegativeLengthOrPercentage. draft
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 21 Jul 2017 10:52:34 +0800
changeset 614998 6d0a21f50687384fc91b93f9a158338748e14376
parent 614997 8a2d21f3348c00e86ce2149f5240b13e4b0afe1d
child 614999 35a82792d901a9acaaf3a8da6287f113435ca071
push id70205
push userbmo:boris.chiou@gmail.com
push dateTue, 25 Jul 2017 08:53:17 +0000
bugs1374233
milestone56.0a1
Bug 1374233 - Part 7: Add NonNegativeLengthOrPercentage. For padding-{*} and grid-{*}-gap. MozReview-Commit-ID: 81G0b1k6JnD
servo/components/style/gecko/values.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/padding.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/shorthand/padding.mako.rs
servo/components/style/properties/shorthand/position.mako.rs
servo/components/style/values/animated/mod.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -14,16 +14,17 @@ use gecko_bindings::structs::{StyleGridT
 use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use media_queries::Device;
 use nsstring::{nsACString, nsCString};
 use std::cmp::max;
 use values::{Auto, Either, ExtremumLength, None_, Normal};
 use values::computed::{Angle, LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::computed::{LengthOrPercentageOrNone, Number, NumberOrPercentage, NonNegativeAu};
 use values::computed::{MaxLength, MozLength, Percentage};
+use values::computed::NonNegativeLengthOrPercentage;
 use values::computed::basic_shape::ShapeRadius as ComputedShapeRadius;
 use values::generics::CounterStyleOrNone;
 use values::generics::basic_shape::ShapeRadius;
 use values::generics::gecko::ScrollSnapPoint;
 use values::generics::grid::{TrackBreadth, TrackKeyword};
 
 /// A trait that defines an interface to convert from and to `nsStyleCoord`s.
 pub trait GeckoStyleCoordConvertible : Sized {
@@ -116,16 +117,26 @@ impl GeckoStyleCoordConvertible for Leng
             CoordDataValue::Coord(coord) => Some(LengthOrPercentage::Length(Au(coord))),
             CoordDataValue::Percent(p) => Some(LengthOrPercentage::Percentage(Percentage(p))),
             CoordDataValue::Calc(calc) => Some(LengthOrPercentage::Calc(calc.into())),
             _ => None,
         }
     }
 }
 
+impl GeckoStyleCoordConvertible for NonNegativeLengthOrPercentage {
+    fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
+        self.0.to_gecko_style_coord(coord);
+    }
+
+    fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
+        LengthOrPercentage::from_gecko_style_coord(coord).map(NonNegativeLengthOrPercentage)
+    }
+}
+
 impl GeckoStyleCoordConvertible for Au {
     fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
         coord.set_value(CoordDataValue::Coord(self.0));
     }
 
     fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
         match coord.as_value() {
             CoordDataValue::Coord(coord) => Some(Au(coord)),
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -917,16 +917,17 @@ impl Clone for ${style_struct.gecko_stru
         "Position": impl_position,
         "LengthOrPercentage": impl_style_coord,
         "LengthOrPercentageOrAuto": impl_style_coord,
         "LengthOrPercentageOrNone": impl_style_coord,
         "LengthOrNone": impl_style_coord,
         "LengthOrNormal": impl_style_coord,
         "MaxLength": impl_style_coord,
         "MozLength": impl_style_coord,
+        "NonNegativeLengthOrPercentage": impl_style_coord,
         "NonNegativeNumber": impl_simple_wrapper,
         "Number": impl_simple,
         "Integer": impl_simple,
         "Opacity": impl_simple,
         "Color": impl_color,
         "RGBAColor": impl_rgba_color,
         "SVGPaint": impl_svg_paint,
         "UrlOrNone": impl_css_url,
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -43,17 +43,17 @@ use values::animated::effects::FilterLis
 use values::animated::effects::TextShadowList as AnimatedTextShadowList;
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderCornerRadius, ClipRect};
 use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
 use values::computed::{GreaterThanOrEqualToOneNumber, NonNegativeAu, NonNegativeNumber};
 use values::computed::{PositiveInteger, PositiveIntegerOrAuto};
 use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
-use values::computed::length::NonNegativeLengthOrNumber;
+use values::computed::length::{NonNegativeLengthOrNumber, NonNegativeLengthOrPercentage};
 use values::generics::{SVGPaint, SVGPaintKind};
 use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
 
 /// A trait used to implement various procedures used during animation.
 pub trait Animatable: Sized {
     /// Performs a weighted sum of this value and |other|. This is used for
@@ -1228,16 +1228,36 @@ impl Animatable for LengthOrPercentage {
 impl ToAnimatedZero for LengthOrPercentage {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Ok(LengthOrPercentage::zero())
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
+impl Animatable for NonNegativeLengthOrPercentage {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        self.0.add_weighted(&other.0, self_portion, other_portion).map(NonNegativeLengthOrPercentage)
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        self.0.compute_distance(&other.0)
+    }
+}
+
+impl ToAnimatedZero for NonNegativeLengthOrPercentage {
+    #[inline]
+    fn to_animated_zero(&self) -> Result<Self, ()> {
+        Ok(NonNegativeLengthOrPercentage::zero())
+    }
+}
+
+/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
 impl Animatable for LengthOrPercentageOrAuto {
     #[inline]
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
         match (*self, *other) {
             (LengthOrPercentageOrAuto::Length(ref this),
              LengthOrPercentageOrAuto::Length(ref other)) => {
                 this.add_weighted(other, self_portion, other_portion)
                     .map(LengthOrPercentageOrAuto::Length)
--- a/servo/components/style/properties/longhand/padding.mako.rs
+++ b/servo/components/style/properties/longhand/padding.mako.rs
@@ -9,18 +9,17 @@
 // APPLIES_TO_PLACEHOLDER so we can set it in UA  stylesheets.  But we use a
 // !important value there, so pages can't set it.
 % for side in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
     %>
-    ${helpers.predefined_type("padding-%s" % side[0], "LengthOrPercentage",
-                              "computed::LengthOrPercentage::Length(Au(0))",
-                              "parse_non_negative",
+    ${helpers.predefined_type("padding-%s" % side[0], "NonNegativeLengthOrPercentage",
+                              "computed::NonNegativeLengthOrPercentage::zero()",
                               alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
-                              animation_value_type="ComputedValue",
+                              animation_value_type="NonNegativeLengthOrPercentage",
                               logical = side[1],
                               spec = spec,
                               flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_PLACEHOLDER",
                               allow_quirks=not side[1])}
 % endfor
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -244,21 +244,20 @@ macro_rules! impl_align_conversions {
                           "computed::Position::zero()",
                           products="gecko",
                           boxed="True",
                           spec="https://drafts.csswg.org/css-images-3/#the-object-position",
                           animation_value_type="ComputedValue")}
 
 % for kind in ["row", "column"]:
     ${helpers.predefined_type("grid-%s-gap" % kind,
-                              "LengthOrPercentage",
-                              "computed::LengthOrPercentage::Length(Au(0))",
-                              "parse_non_negative",
+                              "NonNegativeLengthOrPercentage",
+                              "computed::NonNegativeLengthOrPercentage::zero()",
                               spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
-                              animation_value_type="ComputedValue",
+                              animation_value_type="NonNegativeLengthOrPercentage",
                               products="gecko")}
 
     % for range in ["start", "end"]:
         ${helpers.predefined_type("grid-%s-%s" % (kind, range),
                                   "GridLine",
                                   "Default::default()",
                                   animation_value_type="discrete",
                                   spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
--- a/servo/components/style/properties/shorthand/padding.mako.rs
+++ b/servo/components/style/properties/shorthand/padding.mako.rs
@@ -1,9 +1,9 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
-${helpers.four_sides_shorthand("padding", "padding-%s", "specified::LengthOrPercentage::parse_non_negative",
+${helpers.four_sides_shorthand("padding", "padding-%s", "specified::NonNegativeLengthOrPercentage::parse",
                                spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
                                allow_quirks=True)}
--- a/servo/components/style/properties/shorthand/position.mako.rs
+++ b/servo/components/style/properties/shorthand/position.mako.rs
@@ -454,17 +454,17 @@
                     spec="https://drafts.csswg.org/css-grid/#propdef-grid"
                     disable_when_testing="True"
                     products="gecko">
     use parser::Parse;
     use properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
     use properties::longhands::grid_auto_flow::computed_value::{AutoFlow, T as SpecifiedAutoFlow};
     use values::{Either, None_};
     use values::generics::grid::{GridTemplateComponent, TrackListType};
-    use values::specified::{GenericGridTemplateComponent, LengthOrPercentage, TrackSize};
+    use values::specified::{GenericGridTemplateComponent, NonNegativeLengthOrPercentage, TrackSize};
 
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         let mut temp_rows = GridTemplateComponent::None;
         let mut temp_cols = GridTemplateComponent::None;
         let mut temp_areas = Either::Second(None_);
         let mut auto_rows = TrackSize::default();
         let mut auto_cols = TrackSize::default();
@@ -515,18 +515,18 @@
         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(),
+            grid_row_gap: NonNegativeLengthOrPercentage::zero(),
+            grid_column_gap: NonNegativeLengthOrPercentage::zero(),
         })
     }
 
     impl<'a> LonghandsToSerialize<'a> {
         /// Returns true if other sub properties except template-{rows,columns} are initial.
         fn is_grid_template(&self) -> bool {
             *self.grid_template_areas == Either::Second(None_) &&
             *self.grid_auto_rows == TrackSize::default() &&
@@ -534,18 +534,18 @@
             *self.grid_auto_flow == grid_auto_flow::get_initial_value()
         }
     }
 
     impl<'a> ToCss for LonghandsToSerialize<'a> {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             // `grid` shorthand resets these properties. If they are not zero, that means they
             // are changed by longhands and in that case we should fail serializing `grid`.
-            if *self.grid_row_gap != LengthOrPercentage::zero() ||
-               *self.grid_column_gap != LengthOrPercentage::zero() {
+            if *self.grid_row_gap != NonNegativeLengthOrPercentage::zero() ||
+               *self.grid_column_gap != NonNegativeLengthOrPercentage::zero() {
                 return Ok(());
             }
 
 
             if *self.grid_template_areas != Either::Second(None_) ||
                (*self.grid_template_rows != GridTemplateComponent::None &&
                    *self.grid_template_columns != GridTemplateComponent::None) ||
                self.is_grid_template() {
--- a/servo/components/style/values/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -9,16 +9,17 @@
 //! module's raison d'ĂȘtre is to ultimately contain all these types.
 
 use app_units::Au;
 use values::computed::Angle as ComputedAngle;
 use values::computed::NonNegativeAu;
 use values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
 use values::computed::GreaterThanOrEqualToOneNumber as ComputedGreaterThanOrEqualToOneNumber;
 use values::computed::PositiveInteger as ComputedPositiveInteger;
+use values::computed::NonNegativeLengthOrPercentage as ComputedNonNegativeLengthOrPercentage;
 use values::specified::url::SpecifiedUrl;
 
 pub mod effects;
 
 /// Conversion between computed values and intermediate values for animations.
 ///
 /// Notably, colors are represented as four floats during animations.
 pub trait ToAnimatedValue {
@@ -143,16 +144,41 @@ impl ToAnimatedValue for ComputedPositiv
     }
 
     #[inline]
     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
         animated.max(ComputedPositiveInteger(1))
     }
 }
 
+impl ToAnimatedValue for ComputedNonNegativeLengthOrPercentage {
+    type AnimatedValue = Self;
+
+    #[inline]
+    fn to_animated_value(self) -> Self {
+        self
+    }
+
+    #[inline]
+    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+        use values::computed::{LengthOrPercentage, Percentage};
+        match animated.0 {
+            LengthOrPercentage::Length(au) => {
+                ComputedNonNegativeLengthOrPercentage(
+                    LengthOrPercentage::Length(Au(::std::cmp::max(au.0, 0))))
+            },
+            LengthOrPercentage::Percentage(percentage) => {
+                ComputedNonNegativeLengthOrPercentage(
+                    LengthOrPercentage::Percentage(Percentage(percentage.0.max(0.))))
+            },
+            _ => animated
+        }
+    }
+}
+
 /// Returns a value similar to `self` that represents zero.
 pub trait ToAnimatedZero: Sized {
     /// Returns a value that, when added with an underlying value, will produce the underlying
     /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
     /// the zero value to the 'by' value, and then adds the result to the underlying value.
     ///
     /// This is not the necessarily the same as the initial value of a property. For example, the
     /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -547,16 +547,42 @@ impl ToComputedValue for specified::Leng
                 specified::LengthOrPercentageOrNone::Calc(
                     Box::new(ToComputedValue::from_computed_value(&calc))
                 )
             }
         }
     }
 }
 
+/// A wrapper of LengthOrPercentage, whose value must be >= 0.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, PartialEq, Debug, ToCss)]
+pub struct NonNegativeLengthOrPercentage(pub LengthOrPercentage);
+
+impl ToComputedValue for specified::length::NonNegativeLengthOrPercentage {
+    type ComputedValue = NonNegativeLengthOrPercentage;
+
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        NonNegativeLengthOrPercentage(self.0.to_computed_value(context))
+    }
+
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        specified::length::NonNegativeLengthOrPercentage(
+            ToComputedValue::from_computed_value(&computed.0))
+    }
+}
+
+impl NonNegativeLengthOrPercentage {
+    /// Get zero value.
+    #[inline]
+    pub fn zero() -> Self {
+        NonNegativeLengthOrPercentage(LengthOrPercentage::zero())
+    }
+}
+
 /// A computed `<length>` value.
 pub type Length = Au;
 
 /// Either a computed `<length>` or the `none` keyword.
 pub type LengthOrNone = Either<Length, None_>;
 
 /// Either a computed `<length>` or the `auto` keyword.
 pub type LengthOrAuto = Either<Length, Auto>;
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -38,16 +38,17 @@ pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use super::specified::{BorderStyle, UrlOrNone};
 pub use super::generics::grid::GridLine;
 pub use super::specified::url::SpecifiedUrl;
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength, Percentage};
+pub use self::length::NonNegativeLengthOrPercentage;
 pub use self::position::Position;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::transform::{TimingFunction, TransformOrigin};
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod color;
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -1193,16 +1193,53 @@ impl LengthOrPercentageOrNone {
 
 impl Parse for LengthOrPercentageOrNone {
     #[inline]
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         Self::parse_internal(context, input, AllowedLengthType::All, AllowQuirks::No)
     }
 }
 
+/// A wrapper of LengthOrPercentage, whose value must be >= 0.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
+pub struct NonNegativeLengthOrPercentage(pub LengthOrPercentage);
+
+impl From<NoCalcLength> for NonNegativeLengthOrPercentage {
+    #[inline]
+    fn from(len: NoCalcLength) -> Self {
+        NonNegativeLengthOrPercentage(LengthOrPercentage::Length(len))
+    }
+}
+
+impl Parse for NonNegativeLengthOrPercentage {
+    #[inline]
+    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        LengthOrPercentage::parse_non_negative(context, input).map(NonNegativeLengthOrPercentage)
+    }
+}
+
+impl NonNegativeLengthOrPercentage {
+    #[inline]
+    /// Returns a `zero` length.
+    pub fn zero() -> Self {
+        NonNegativeLengthOrPercentage(LengthOrPercentage::zero())
+    }
+
+    /// Parses a length or a percentage, allowing the unitless length quirk.
+    /// https://quirks.spec.whatwg.org/#the-unitless-length-quirk
+    #[inline]
+    pub fn parse_quirky<'i, 't>(context: &ParserContext,
+                                input: &mut Parser<'i, 't>,
+                                allow_quirks: AllowQuirks) -> Result<Self, ParseError<'i>> {
+        LengthOrPercentage::parse_non_negative_quirky(context, input, allow_quirks)
+            .map(NonNegativeLengthOrPercentage)
+    }
+}
+
 /// Either a `<length>` or the `none` keyword.
 pub type LengthOrNone = Either<Length, None_>;
 
 /// Either a `<length>` or the `normal` keyword.
 pub type LengthOrNormal = Either<Length, Normal>;
 
 /// Either a `<length>` or the `auto` keyword.
 pub type LengthOrAuto = Either<Length, Auto>;
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -37,16 +37,17 @@ pub use self::flex::FlexBasis;
 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::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
 pub use self::length::{FontRelativeLength, Length, LengthOrNone, LengthOrNumber};
 pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{NoCalcLength, Percentage, ViewportPercentageLength};
+pub use self::length::NonNegativeLengthOrPercentage;
 pub use self::rect::LengthOrNumberRect;
 pub use self::position::{Position, PositionComponent};
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::transform::{TimingFunction, TransformOrigin};
 pub use super::generics::grid::GridLine;
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 
 #[cfg(feature = "gecko")]