Bug 1530847 - Add a Zero trait that doesn't require Add, and use it in place of num_traits and IsZeroLength. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 28 Feb 2019 19:03:03 +0000
changeset 519694 1a48f163c0f24e07837ff258ca72f2bfe658a183
parent 519693 48bf7d6e2ea7be2eff20cafdbb4c79dfd3706e8e
child 519695 8829698bebbbeccea5f8e3e0657a5214c13cde9d
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1530847
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1530847 - Add a Zero trait that doesn't require Add, and use it in place of num_traits and IsZeroLength. r=heycam Use it to be consistent in InsetRect serialization and storage between Servo and Gecko. Differential Revision: https://phabricator.services.mozilla.com/D21493
servo/components/style/gecko/conversions.rs
servo/components/style/gecko/values.rs
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/lib.rs
servo/components/style/media_queries/media_feature_expression.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/animated/transform.rs
servo/components/style/values/computed/angle.rs
servo/components/style/values/computed/border.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/percentage.rs
servo/components/style/values/computed/position.rs
servo/components/style/values/computed/svg.rs
servo/components/style/values/computed/transform.rs
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/border.rs
servo/components/style/values/generics/length.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/size.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/specified/angle.rs
servo/components/style/values/specified/basic_shape.rs
servo/components/style/values/specified/border.rs
servo/components/style/values/specified/effects.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/position.rs
servo/components/style/values/specified/transform.rs
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -22,16 +22,17 @@ use crate::values::computed::transform::
 use crate::values::computed::url::ComputedImageUrl;
 use crate::values::computed::{Angle, Gradient, Image};
 use crate::values::computed::{Integer, LengthPercentage};
 use crate::values::computed::{Length, Percentage, TextAlign};
 use crate::values::generics::box_::VerticalAlign;
 use crate::values::generics::grid::{TrackListValue, TrackSize};
 use crate::values::generics::image::{CompatMode, GradientItem, Image as GenericImage};
 use crate::values::generics::rect::Rect;
+use crate::Zero;
 use app_units::Au;
 use std::f32::consts::PI;
 use style_traits::values::specified::AllowedNumericType;
 
 impl From<LengthPercentage> for nsStyleCoord_CalcValue {
     fn from(other: LengthPercentage) -> nsStyleCoord_CalcValue {
         debug_assert!(
             other.was_calc || !other.has_percentage || other.unclamped_length() == Length::zero()
@@ -698,17 +699,16 @@ pub mod basic_shape {
         fn from(other: &'a StyleBasicShape) -> Self {
             match other.mType {
                 StyleBasicShapeType::Inset => {
                     let t = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[0]);
                     let r = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
                     let b = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
                     let l = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
                     let round = other.mRadius;
-                    let round = if round.all_zero() { None } else { Some(round) };
                     let rect = Rect::new(
                         t.expect("inset() offset should be a length, percentage, or calc value"),
                         r.expect("inset() offset should be a length, percentage, or calc value"),
                         b.expect("inset() offset should be a length, percentage, or calc value"),
                         l.expect("inset() offset should be a length, percentage, or calc value"),
                     );
                     GenericBasicShape::Inset(InsetRect { rect, round })
                 },
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -14,17 +14,17 @@ use crate::values::computed::basic_shape
 use crate::values::computed::{Angle, Length, LengthPercentage};
 use crate::values::computed::{Number, NumberOrPercentage, Percentage};
 use crate::values::generics::basic_shape::ShapeRadius;
 use crate::values::generics::gecko::ScrollSnapPoint;
 use crate::values::generics::grid::{TrackBreadth, TrackKeyword};
 use crate::values::generics::length::LengthPercentageOrAuto;
 use crate::values::generics::{CounterStyleOrNone, NonNegative};
 use crate::values::{Auto, Either, None_, Normal};
-use crate::Atom;
+use crate::{Atom, Zero};
 use app_units::Au;
 use cssparser::RGBA;
 use nsstring::{nsACString, nsCStr};
 use std::cmp::max;
 
 /// A trait that defines an interface to convert from and to `nsStyleCoord`s.
 ///
 /// TODO(emilio): Almost everything that is in this file should be somehow
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -5,16 +5,17 @@
 //! Little helpers for `nsCSSValue`.
 
 use crate::gecko_bindings::bindings;
 use crate::gecko_bindings::structs;
 use crate::gecko_bindings::structs::{nsCSSUnit, nsCSSValue};
 use crate::gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array};
 use crate::gecko_string_cache::Atom;
 use crate::values::computed::{Angle, Length, LengthPercentage, Percentage};
+use crate::Zero;
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::{Index, IndexMut};
 use std::slice;
 
 impl nsCSSValue {
     /// Create a CSSValue with null unit, useful to be used as a return value.
     #[inline]
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -239,8 +239,31 @@ pub trait CaseSensitivityExt {
 impl CaseSensitivityExt for selectors::attr::CaseSensitivity {
     fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool {
         match self {
             selectors::attr::CaseSensitivity::CaseSensitive => a == b,
             selectors::attr::CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
         }
     }
 }
+
+/// A trait pretty much similar to num_traits::Zero, but without the need of
+/// implementing `Add`.
+pub trait Zero {
+    /// Returns the zero value.
+    fn zero() -> Self;
+
+    /// Returns whether this value is zero.
+    fn is_zero(&self) -> bool;
+}
+
+impl<T> Zero for T
+where
+    T: num_traits::Zero,
+{
+    fn zero() -> Self {
+        <Self as num_traits::Zero>::zero()
+    }
+
+    fn is_zero(&self) -> bool {
+        <Self as num_traits::Zero>::is_zero(self)
+    }
+}
--- a/servo/components/style/media_queries/media_feature_expression.rs
+++ b/servo/components/style/media_queries/media_feature_expression.rs
@@ -16,19 +16,18 @@ use crate::gecko_bindings::structs;
 use crate::parser::{Parse, ParserContext};
 #[cfg(feature = "servo")]
 use crate::servo::media_queries::MEDIA_FEATURES;
 use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
 use crate::stylesheets::Origin;
 use crate::values::computed::{self, ToComputedValue};
 use crate::values::specified::{Integer, Length, Number, Resolution};
 use crate::values::{serialize_atom_identifier, CSSFloat};
-use crate::Atom;
+use crate::{Atom, Zero};
 use cssparser::{Parser, Token};
-use num_traits::Zero;
 use std::cmp::{Ordering, PartialOrd};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
 /// An aspect ratio, with a numerator and denominator.
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
 pub struct AspectRatio(pub u32, pub u32);
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -4624,20 +4624,17 @@ fn set_style_svg_path(
                         shape.mCoordinates[0].leaky_set_null();
                         inset.rect.0.to_gecko_style_coord(&mut shape.mCoordinates[0]);
                         shape.mCoordinates[1].leaky_set_null();
                         inset.rect.1.to_gecko_style_coord(&mut shape.mCoordinates[1]);
                         shape.mCoordinates[2].leaky_set_null();
                         inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]);
                         shape.mCoordinates[3].leaky_set_null();
                         inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]);
-                        shape.mRadius = match inset.round {
-                            Some(radius) => radius,
-                            None => crate::values::computed::BorderRadius::zero(),
-                        };
+                        shape.mRadius = inset.round;
                     }
                     BasicShape::Circle(circ) => {
                         let shape = init_shape(${ident}, StyleBasicShapeType::Circle);
                         unsafe { shape.mCoordinates.set_len(1) };
                         shape.mCoordinates[0].leaky_set_null();
                         circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]);
 
                         shape.mPosition = circ.position.into();
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -14,16 +14,18 @@
     <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
         #[allow(unused_imports)]
         use app_units::Au;
         #[allow(unused_imports)]
         use cssparser::{Color as CSSParserColor, RGBA};
         #[allow(unused_imports)]
         use crate::values::specified::AllowQuirks;
         #[allow(unused_imports)]
+        use crate::Zero;
+        #[allow(unused_imports)]
         use smallvec::SmallVec;
         pub use crate::values::specified::${type} as SpecifiedValue;
         pub mod computed_value {
             % if computed_type:
             pub use ${computed_type} as T;
             % else:
             pub use crate::values::computed::${type} as T;
             % endif
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -37,16 +37,17 @@ use selectors::parser::SelectorParseErro
 use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
 use crate::values::generics::text::LineHeight;
 use crate::values::computed;
 use crate::values::computed::NonNegativeLength;
 use crate::values::serialize_atom_name;
 use crate::rule_tree::StrongRuleNode;
+use crate::Zero;
 use self::computed_value_flags::*;
 use crate::str::{CssString, CssStringBorrow, CssStringWriter};
 
 pub use self::declaration_block::*;
 pub use self::cascade::*;
 
 <%!
     from collections import defaultdict
@@ -2580,17 +2581,17 @@ pub mod style_structs {
                     }
                 % endif
             % endfor
             % if style_struct.name == "Border":
                 % for side in ["top", "right", "bottom", "left"]:
                     /// Whether the border-${side} property has nonzero width.
                     #[allow(non_snake_case)]
                     pub fn border_${side}_has_nonzero_width(&self) -> bool {
-                        self.border_${side}_width != NonNegativeLength::zero()
+                        !self.border_${side}_width.is_zero()
                     }
                 % endfor
             % elif style_struct.name == "Font":
                 /// Computes a font hash in order to be able to cache fonts
                 /// effectively in GFX and layout.
                 pub fn compute_font_hash(&mut self) {
                     // Corresponds to the fields in
                     // `gfx::font_template::FontTemplateDescriptor`.
@@ -2619,17 +2620,17 @@ pub mod style_structs {
                 /// (Servo does not handle MathML, so this does nothing)
                 pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) {
                 }
 
             % elif style_struct.name == "Outline":
                 /// Whether the outline-width property is non-zero.
                 #[inline]
                 pub fn outline_has_nonzero_width(&self) -> bool {
-                    self.outline_width != NonNegativeLength::zero()
+                    !self.outline_width.is_zero()
                 }
             % elif style_struct.name == "Text":
                 /// Whether the text decoration has an underline.
                 #[inline]
                 pub fn has_underline(&self) -> bool {
                     self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::UNDERLINE)
                 }
 
--- a/servo/components/style/values/animated/transform.rs
+++ b/servo/components/style/values/animated/transform.rs
@@ -17,17 +17,17 @@ use crate::values::computed::transform::
 use crate::values::computed::transform::{DirectionVector, Matrix, Matrix3D};
 use crate::values::computed::Angle;
 use crate::values::computed::{Length, LengthPercentage};
 use crate::values::computed::{Number, Percentage};
 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
 use crate::values::generics::transform::{self, Transform, TransformOperation};
 use crate::values::generics::transform::{Rotate, Scale, Translate};
 use crate::values::CSSFloat;
-use num_traits::Zero;
+use crate::Zero;
 use std::cmp;
 
 // ------------------------------------
 // Animations for Matrix/Matrix3D.
 // ------------------------------------
 /// A 2d matrix for interpolation.
 #[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
--- a/servo/components/style/values/computed/angle.rs
+++ b/servo/components/style/values/computed/angle.rs
@@ -1,17 +1,17 @@
 /* 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 https://mozilla.org/MPL/2.0/. */
 
 //! Computed angles.
 
 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
 use crate::values::CSSFloat;
-use num_traits::Zero;
+use crate::Zero;
 use std::f64::consts::PI;
 use std::fmt::{self, Write};
 use std::{f32, f64};
 use style_traits::{CssWriter, ToCss};
 
 /// A computed angle in degrees.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Add, Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToAnimatedZero)]
--- a/servo/components/style/values/computed/border.rs
+++ b/servo/components/style/values/computed/border.rs
@@ -9,16 +9,17 @@ use crate::values::computed::{NonNegativ
 use crate::values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
 use crate::values::generics::border::BorderImageSideWidth as GenericBorderImageSideWidth;
 use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice;
 use crate::values::generics::border::BorderRadius as GenericBorderRadius;
 use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
 use crate::values::generics::rect::Rect;
 use crate::values::generics::size::Size2D;
 use crate::values::generics::NonNegative;
+use crate::Zero;
 use app_units::Au;
 
 pub use crate::values::specified::border::BorderImageRepeat;
 
 /// A computed value for the `border-image-width` property.
 pub type BorderImageWidth = Rect<BorderImageSideWidth>;
 
 /// A computed value for a single side of a `border-image-width` property.
@@ -70,44 +71,8 @@ impl BorderSpacing {
         Au::from(*self.0.width())
     }
 
     /// Returns the vertical spacing.
     pub fn vertical(&self) -> Au {
         Au::from(*self.0.height())
     }
 }
-
-impl BorderCornerRadius {
-    /// Returns `0 0`.
-    pub fn zero() -> Self {
-        GenericBorderCornerRadius(Size2D::new(
-            NonNegativeLengthPercentage::zero(),
-            NonNegativeLengthPercentage::zero(),
-        ))
-    }
-}
-
-impl BorderRadius {
-    /// Returns a `0` border radius.
-    pub fn zero() -> Self {
-        Self {
-            top_left: BorderCornerRadius::zero(),
-            top_right: BorderCornerRadius::zero(),
-            bottom_right: BorderCornerRadius::zero(),
-            bottom_left: BorderCornerRadius::zero(),
-        }
-    }
-
-    /// Returns whether all the values are `0px`.
-    pub fn all_zero(&self) -> bool {
-        fn all(corner: &BorderCornerRadius) -> bool {
-            fn is_zero(l: &NonNegativeLengthPercentage) -> bool {
-                *l == NonNegativeLengthPercentage::zero()
-            }
-            is_zero(corner.0.width()) && is_zero(corner.0.height())
-        }
-        all(&self.top_left) &&
-            all(&self.top_right) &&
-            all(&self.bottom_left) &&
-            all(&self.bottom_right)
-    }
-}
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -7,21 +7,21 @@
 use super::{Context, Number, Percentage, ToComputedValue};
 use crate::values::animated::ToAnimatedValue;
 use crate::values::computed::NonNegativeNumber;
 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
 use crate::values::generics::length as generics;
 use crate::values::generics::length::{
     GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize,
 };
-use crate::values::generics::transform::IsZeroLength;
 use crate::values::generics::NonNegative;
 use crate::values::specified::length::ViewportPercentageLength;
 use crate::values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
 use crate::values::{specified, Auto, CSSFloat, Either, Normal};
+use crate::Zero;
 use app_units::Au;
 use ordered_float::NotNan;
 use std::fmt::{self, Write};
 use std::ops::{Add, Neg};
 use style_traits::values::specified::AllowedNumericType;
 use style_traits::{CssWriter, ToCss};
 
 pub use super::image::Image;
@@ -337,22 +337,16 @@ impl ToComputedValue for specified::Calc
             absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
             percentage: computed.specified_percentage(),
             ..Default::default()
         }
     }
 }
 
 impl LengthPercentage {
-    #[inline]
-    #[allow(missing_docs)]
-    pub fn zero() -> LengthPercentage {
-        LengthPercentage::new(Length::new(0.), None)
-    }
-
     /// 1px length value for SVG defaults
     #[inline]
     pub fn one() -> LengthPercentage {
         LengthPercentage::new(Length::new(1.), None)
     }
 
     /// Returns true if the computed value is absolute 0 or 0%.
     #[inline]
@@ -437,33 +431,31 @@ impl ToComputedValue for specified::Leng
                 &computed.length(),
             ));
         }
 
         specified::LengthPercentage::Calc(Box::new(ToComputedValue::from_computed_value(computed)))
     }
 }
 
-impl IsZeroLength for LengthPercentage {
+impl Zero for LengthPercentage {
+    fn zero() -> Self {
+        LengthPercentage::new(Length::zero(), None)
+    }
+
     #[inline]
-    fn is_zero_length(&self) -> bool {
+    fn is_zero(&self) -> bool {
         self.is_definitely_zero()
     }
 }
 
 /// Some boilerplate to share between negative and non-negative
 /// length-percentage or auto.
 macro_rules! computed_length_percentage_or_auto {
     ($inner:ty) => {
-        /// Returns the `0` value.
-        #[inline]
-        pub fn zero() -> Self {
-            generics::LengthPercentageOrAuto::LengthPercentage(<$inner>::zero())
-        }
-
         /// Returns the used value.
         #[inline]
         pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
             match *self {
                 generics::GenericLengthPercentageOrAuto::Auto => None,
                 generics::GenericLengthPercentageOrAuto::LengthPercentage(ref lp) => {
                     Some(lp.to_used_value(percentage_basis))
                 }
@@ -542,22 +534,16 @@ impl From<LengthPercentage> for NonNegat
 impl From<Au> for LengthPercentage {
     #[inline]
     fn from(length: Au) -> Self {
         LengthPercentage::new(length.into(), None)
     }
 }
 
 impl NonNegativeLengthPercentage {
-    /// Get zero value.
-    #[inline]
-    pub fn zero() -> Self {
-        NonNegative(LengthPercentage::zero())
-    }
-
     /// Returns true if the computed value is absolute 0 or 0%.
     #[inline]
     pub fn is_definitely_zero(&self) -> bool {
         self.0.is_definitely_zero()
     }
 
     /// Returns the used value.
     #[inline]
@@ -651,22 +637,26 @@ impl CSSPixelLength {
         CSSPixelLength::new(self.0.abs())
     }
 
     /// Return the clamped value of this length.
     #[inline]
     pub fn clamp_to_non_negative(self) -> Self {
         CSSPixelLength::new(self.0.max(0.))
     }
+}
 
-    /// Zero value
-    #[inline]
-    pub fn zero() -> Self {
+impl Zero for CSSPixelLength {
+    fn zero() -> Self {
         CSSPixelLength::new(0.)
     }
+
+    fn is_zero(&self) -> bool {
+        self.px() == 0.
+    }
 }
 
 impl ToCss for CSSPixelLength {
     #[inline]
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
@@ -738,22 +728,16 @@ impl ToAnimatedValue for NonNegativeLeng
 
 impl NonNegativeLength {
     /// Create a NonNegativeLength.
     #[inline]
     pub fn new(px: CSSFloat) -> Self {
         NonNegative(Length::new(px.max(0.)))
     }
 
-    /// Return a zero value.
-    #[inline]
-    pub fn zero() -> Self {
-        Self::new(0.)
-    }
-
     /// Return the pixel value of |NonNegativeLength|.
     #[inline]
     pub fn px(&self) -> CSSFloat {
         self.0.px()
     }
 
     #[inline]
     /// Ensures it is non negative
--- a/servo/components/style/values/computed/percentage.rs
+++ b/servo/components/style/values/computed/percentage.rs
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Computed percentages.
 
 use crate::values::animated::ToAnimatedValue;
 use crate::values::generics::NonNegative;
 use crate::values::{serialize_percentage, CSSFloat};
+use crate::Zero;
 use std::fmt;
 use style_traits::{CssWriter, ToCss};
 
 /// A computed percentage.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(
     Animate,
     Clone,
@@ -26,22 +27,16 @@ use style_traits::{CssWriter, ToCss};
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
 )]
 #[repr(C)]
 pub struct Percentage(pub CSSFloat);
 
 impl Percentage {
-    /// 0%
-    #[inline]
-    pub fn zero() -> Self {
-        Percentage(0.)
-    }
-
     /// 100%
     #[inline]
     pub fn hundred() -> Self {
         Percentage(1.)
     }
 
     /// Returns the absolute value for this percentage.
     #[inline]
@@ -51,35 +46,39 @@ impl Percentage {
 
     /// Clamps this percentage to a non-negative percentage.
     #[inline]
     pub fn clamp_to_non_negative(self) -> Self {
         Percentage(self.0.max(0.))
     }
 }
 
+impl Zero for Percentage {
+    fn zero() -> Self {
+        Percentage(0.)
+    }
+
+    fn is_zero(&self) -> bool {
+        self.0 == 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 wrapper over a `Percentage`, whose value should be clamped to 0.
 pub type NonNegativePercentage = NonNegative<Percentage>;
 
 impl NonNegativePercentage {
-    /// 0%
-    #[inline]
-    pub fn zero() -> Self {
-        NonNegative(Percentage::zero())
-    }
-
     /// 100%
     #[inline]
     pub fn hundred() -> Self {
         NonNegative(Percentage::hundred())
     }
 }
 
 impl ToAnimatedValue for NonNegativePercentage {
--- a/servo/components/style/values/computed/position.rs
+++ b/servo/components/style/values/computed/position.rs
@@ -6,16 +6,17 @@
 //! [`position`][position] values.
 //!
 //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
 
 use crate::values::computed::{Integer, LengthPercentage, Percentage};
 use crate::values::generics::position::Position as GenericPosition;
 use crate::values::generics::position::ZIndex as GenericZIndex;
 pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
+use crate::Zero;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 
 /// The computed value of a CSS `<position>`
 pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
 
 /// The computed value of a CSS horizontal position.
 pub type HorizontalPosition = LengthPercentage;
--- a/servo/components/style/values/computed/svg.rs
+++ b/servo/components/style/values/computed/svg.rs
@@ -5,16 +5,17 @@
 //! Computed types for SVG properties.
 
 use crate::values::computed::color::Color;
 use crate::values::computed::url::ComputedUrl;
 use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage};
 use crate::values::computed::{NonNegativeNumber, Number, Opacity};
 use crate::values::generics::svg as generic;
 use crate::values::RGBA;
+use crate::Zero;
 
 pub use crate::values::specified::SVGPaintOrder;
 
 pub use crate::values::specified::MozContextProperties;
 
 /// Computed SVG Paint value
 pub type SVGPaint = generic::SVGPaint<Color, ComputedUrl>;
 /// Computed SVG Paint Kind value
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -4,18 +4,18 @@
 
 //! Computed types for CSS values that are related to transformations.
 
 use super::CSSFloat;
 use crate::values::animated::transform::{Perspective, Scale3D, Translate3D};
 use crate::values::animated::ToAnimatedZero;
 use crate::values::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage};
 use crate::values::generics::transform as generic;
+use crate::Zero;
 use euclid::{Transform3D, Vector3D};
-use num_traits::Zero;
 
 pub use crate::values::generics::transform::TransformStyle;
 
 /// A single operation in a computed CSS `transform`
 pub type TransformOperation =
     generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
 /// A computed CSS `transform`
 pub type Transform = generic::Transform<TransformOperation>;
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -6,16 +6,17 @@
 //! types that are generic over their `ToCss` implementations.
 
 use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
 use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
 use crate::values::generics::border::BorderRadius;
 use crate::values::generics::position::Position;
 use crate::values::generics::rect::Rect;
 use crate::values::specified::SVGPathData;
+use crate::Zero;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 
 /// A clipping shape, for `clip-path`.
 pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
 
 /// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
 #[allow(missing_docs)]
@@ -122,17 +123,17 @@ pub enum BasicShape<H, V, LengthPercenta
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToComputedValue,
 )]
 pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> {
     pub rect: Rect<LengthPercentage>,
-    pub round: Option<BorderRadius<NonNegativeLengthPercentage>>,
+    pub round: BorderRadius<NonNegativeLengthPercentage>,
 }
 
 /// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
 #[allow(missing_docs)]
 #[css(function)]
 #[derive(
     Animate,
     Clone,
@@ -306,27 +307,27 @@ impl<B, T, U> ToAnimatedZero for ShapeSo
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Err(())
     }
 }
 
 impl<Length, NonNegativeLength> ToCss for InsetRect<Length, NonNegativeLength>
 where
     Length: ToCss + PartialEq,
-    NonNegativeLength: ToCss + PartialEq,
+    NonNegativeLength: ToCss + PartialEq + Zero,
 {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         dest.write_str("inset(")?;
         self.rect.to_css(dest)?;
-        if let Some(ref radius) = self.round {
+        if !self.round.is_zero() {
             dest.write_str(" round ")?;
-            radius.to_css(dest)?;
+            self.round.to_css(dest)?;
         }
         dest.write_str(")")
     }
 }
 
 impl<L> Default for ShapeRadius<L> {
     #[inline]
     fn default() -> Self {
--- a/servo/components/style/values/generics/border.rs
+++ b/servo/components/style/values/generics/border.rs
@@ -1,16 +1,17 @@
 /* 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 https://mozilla.org/MPL/2.0/. */
 
 //! Generic types for CSS values related to borders.
 
 use crate::values::generics::rect::Rect;
 use crate::values::generics::size::Size2D;
+use crate::Zero;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 
 /// A generic value for a single side of a `border-image-width` property.
 #[derive(
     Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,
 )]
 pub enum BorderImageSideWidth<LengthPercentage, Number> {
@@ -60,16 +61,26 @@ pub use self::GenericBorderCornerRadius 
 
 impl<L> BorderCornerRadius<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderCornerRadius(Size2D::new(w, h))
     }
 }
 
+impl<L: Zero> Zero for BorderCornerRadius<L> {
+    fn zero() -> Self {
+        BorderCornerRadius(Size2D::zero())
+    }
+
+    fn is_zero(&self) -> bool {
+        self.0.is_zero()
+    }
+}
+
 /// A generic value for the `border-spacing` property.
 #[derive(
     Animate,
     Clone,
     ComputeSquaredDistance,
     Copy,
     Debug,
     MallocSizeOf,
@@ -130,41 +141,55 @@ impl<L> BorderRadius<L> {
     ) -> Self {
         BorderRadius {
             top_left: tl,
             top_right: tr,
             bottom_right: br,
             bottom_left: bl,
         }
     }
-}
 
-impl<L> BorderRadius<L>
-where
-    L: PartialEq + ToCss,
-{
     /// Serialises two given rects following the syntax of the `border-radius``
     /// property.
     pub fn serialize_rects<W>(
         widths: Rect<&L>,
         heights: Rect<&L>,
         dest: &mut CssWriter<W>,
     ) -> fmt::Result
     where
+        L: PartialEq + ToCss,
         W: Write,
     {
         widths.to_css(dest)?;
         if widths != heights {
             dest.write_str(" / ")?;
             heights.to_css(dest)?;
         }
         Ok(())
     }
 }
 
+impl<L: Zero> Zero for BorderRadius<L> {
+    fn zero() -> Self {
+        Self::new(
+            BorderCornerRadius::<L>::zero(),
+            BorderCornerRadius::<L>::zero(),
+            BorderCornerRadius::<L>::zero(),
+            BorderCornerRadius::<L>::zero(),
+        )
+    }
+
+    fn is_zero(&self) -> bool {
+        self.top_left.is_zero() &&
+            self.top_right.is_zero() &&
+            self.bottom_right.is_zero() &&
+            self.bottom_left.is_zero()
+    }
+}
+
 impl<L> ToCss for BorderRadius<L>
 where
     L: PartialEq + ToCss,
 {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
--- a/servo/components/style/values/generics/length.rs
+++ b/servo/components/style/values/generics/length.rs
@@ -2,18 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Generic types for CSS values related to length.
 
 use crate::parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use crate::values::computed::ExtremumLength;
+use crate::Zero;
 use cssparser::Parser;
-use num_traits::Zero;
 use style_traits::ParseError;
 
 /// A `<length-percentage> | auto` value.
 #[allow(missing_docs)]
 #[derive(
     Animate,
     Clone,
     ComputeSquaredDistance,
@@ -62,16 +62,29 @@ impl<LengthPercentage> LengthPercentageO
         }
 
         Ok(LengthPercentageOrAuto::LengthPercentage(parser(
             context, input,
         )?))
     }
 }
 
+impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {
+    fn zero() -> Self {
+        LengthPercentageOrAuto::LengthPercentage(Zero::zero())
+    }
+
+    fn is_zero(&self) -> bool {
+        match *self {
+            LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(),
+            LengthPercentageOrAuto::Auto => false,
+        }
+    }
+}
+
 impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_with(context, input, LengthPercentage::parse)
     }
 }
@@ -181,17 +194,20 @@ pub enum GenericLengthOrNumber<L, N> {
     /// first, since `0` should be a number, not the `0px` length.
     Number(N),
     /// A length.
     Length(L),
 }
 
 pub use self::GenericLengthOrNumber as LengthOrNumber;
 
-impl<L, N> LengthOrNumber<L, N> {
-    /// Returns `0`.
-    pub fn zero() -> Self
-    where
-        N: Zero,
-    {
-        LengthOrNumber::Number(num_traits::Zero::zero())
+impl<L, N: Zero> Zero for LengthOrNumber<L, N> {
+    fn zero() -> Self {
+        LengthOrNumber::Number(Zero::zero())
+    }
+
+    fn is_zero(&self) -> bool {
+        match *self {
+            LengthOrNumber::Number(ref n) => n.is_zero(),
+            LengthOrNumber::Length(..) => false,
+        }
     }
 }
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -3,18 +3,18 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Generic types that share their serialization implementations
 //! for both specified and computed values.
 
 use super::CustomIdent;
 use crate::counter_style::{parse_counter_style_name, Symbols};
 use crate::parser::{Parse, ParserContext};
+use crate::Zero;
 use cssparser::Parser;
-use num_traits::Zero;
 use std::ops::Add;
 use style_traits::{KeywordsCollectFn, ParseError};
 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind};
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 #[path = "box.rs"]
--- a/servo/components/style/values/generics/size.rs
+++ b/servo/components/style/values/generics/size.rs
@@ -1,15 +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 https://mozilla.org/MPL/2.0/. */
 
 //! Generic type for CSS properties that are composed by two dimensions.
 
 use crate::parser::ParserContext;
+use crate::Zero;
 use cssparser::Parser;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, ToCss};
 
 /// A generic size, for `border-*-radius` longhand properties, or
 /// `border-spacing`.
 #[derive(
     Animate,
@@ -79,8 +80,18 @@ where
         if self.height != self.width {
             dest.write_str(" ")?;
             self.height.to_css(dest)?;
         }
 
         Ok(())
     }
 }
+
+impl<L: Zero> Zero for Size2D<L> {
+    fn zero() -> Self {
+        Self::new(L::zero(), L::zero())
+    }
+
+    fn is_zero(&self) -> bool {
+        self.width.is_zero() && self.height.is_zero()
+    }
+}
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -5,19 +5,19 @@
 //! Generic types for CSS values that are related to transformations.
 
 use crate::values::computed::length::Length as ComputedLength;
 use crate::values::computed::length::LengthPercentage as ComputedLengthPercentage;
 use crate::values::specified::angle::Angle as SpecifiedAngle;
 use crate::values::specified::length::Length as SpecifiedLength;
 use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
 use crate::values::{computed, CSSFloat};
+use crate::Zero;
 use app_units::Au;
 use euclid::{self, Rect, Transform3D};
-use num_traits::Zero;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 
 /// A generic 2D transformation matrix.
 #[allow(missing_docs)]
 #[derive(
     Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,
 )]
@@ -638,26 +638,17 @@ pub enum Translate<LengthPercentage, Len
     /// 'none'
     None,
     /// '<length-percentage>' or '<length-percentage> <length-percentage>'
     Translate(LengthPercentage, LengthPercentage),
     /// '<length-percentage> <length-percentage> <length>'
     Translate3D(LengthPercentage, LengthPercentage, Length),
 }
 
-/// A trait to check if this is a zero length.
-/// An alternative way is use num_traits::Zero. However, in order to implement num_traits::Zero,
-/// we also have to implement Add, which may be complicated for LengthPercentage::Calc.
-/// We could do this if other types also need it. If so, we could drop this trait.
-pub trait IsZeroLength {
-    /// Returns true if this is a zero length.
-    fn is_zero_length(&self) -> bool;
-}
-
-impl<LoP: ToCss + IsZeroLength, L: ToCss> ToCss for Translate<LoP, L> {
+impl<LoP: ToCss + Zero, L: ToCss> ToCss for Translate<LoP, L> {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
         // The spec says:
         // 1. If a 2d translation is specified, the property must serialize with only one or two
         //    values (per usual, if the second value is 0px, the default, it must be omitted when
         //    serializing).
@@ -665,17 +656,17 @@ impl<LoP: ToCss + IsZeroLength, L: ToCss
         // https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization
         //
         // We don't omit the 3rd component even if it is 0px for now, and the related
         // spec issue is https://github.com/w3c/csswg-drafts/issues/3305
         match *self {
             Translate::None => dest.write_str("none"),
             Translate::Translate(ref x, ref y) => {
                 x.to_css(dest)?;
-                if !y.is_zero_length() {
+                if !y.is_zero() {
                     dest.write_char(' ')?;
                     y.to_css(dest)?;
                 }
                 Ok(())
             },
             Translate::Translate3D(ref x, ref y, ref z) => {
                 x.to_css(dest)?;
                 dest.write_char(' ')?;
--- a/servo/components/style/values/specified/angle.rs
+++ b/servo/components/style/values/specified/angle.rs
@@ -96,34 +96,33 @@ impl Angle {
     #[inline]
     pub fn from_degrees(value: CSSFloat, was_calc: bool) -> Self {
         Angle {
             value: AngleDimension::Deg(value),
             was_calc,
         }
     }
 
+    /// Return `0deg`.
+    pub fn zero() -> Self {
+        Self::from_degrees(0.0, false)
+    }
+
     /// Returns the value of the angle in degrees, mostly for `calc()`.
     #[inline]
     pub fn degrees(&self) -> CSSFloat {
         self.value.degrees()
     }
 
     /// Whether this specified angle came from a `calc()` expression.
     #[inline]
     pub fn was_calc(&self) -> bool {
         self.was_calc
     }
 
-    /// Returns `0deg`.
-    #[inline]
-    pub fn zero() -> Self {
-        Self::from_degrees(0.0, false)
-    }
-
     /// Returns an `Angle` parsed from a `calc()` expression.
     pub fn from_calc(degrees: CSSFloat) -> Self {
         Angle {
             value: AngleDimension::Deg(degrees),
             was_calc: true,
         }
     }
 }
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -13,16 +13,17 @@ use crate::values::generics::basic_shape
 use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
 use crate::values::generics::rect::Rect;
 use crate::values::specified::border::BorderRadius;
 use crate::values::specified::image::Image;
 use crate::values::specified::position::{HorizontalPosition, Position, VerticalPosition};
 use crate::values::specified::url::SpecifiedUrl;
 use crate::values::specified::SVGPathData;
 use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage};
+use crate::Zero;
 use cssparser::Parser;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
 /// A specified alias for FillRule.
 pub use crate::values::generics::basic_shape::FillRule;
 
 /// A specified clipping shape.
@@ -197,19 +198,19 @@ impl Parse for InsetRect {
 impl InsetRect {
     /// Parse the inner function arguments of `inset()`
     fn parse_function_arguments<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         let rect = Rect::parse_with(context, input, LengthPercentage::parse)?;
         let round = if input.try(|i| i.expect_ident_matching("round")).is_ok() {
-            Some(BorderRadius::parse(context, input)?)
+            BorderRadius::parse(context, input)?
         } else {
-            None
+            BorderRadius::zero()
         };
         Ok(generic::InsetRect { rect, round })
     }
 }
 
 impl Parse for Circle {
     fn parse<'i, 't>(
         context: &ParserContext,
--- a/servo/components/style/values/specified/border.rs
+++ b/servo/components/style/values/specified/border.rs
@@ -10,16 +10,17 @@ use crate::values::generics::border::Bor
 use crate::values::generics::border::BorderImageSideWidth as GenericBorderImageSideWidth;
 use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice;
 use crate::values::generics::border::BorderRadius as GenericBorderRadius;
 use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
 use crate::values::generics::rect::Rect;
 use crate::values::generics::size::Size2D;
 use crate::values::specified::length::{NonNegativeLength, NonNegativeLengthPercentage};
 use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
+use crate::Zero;
 use cssparser::Parser;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, ToCss};
 
 /// A specified value for a single side of a `border-style` property.
 ///
 /// The order here corresponds to the integer values from the border conflict
 /// resolution rules in CSS 2.1 ยง 17.6.2.1. Higher values override lower values.
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -15,16 +15,17 @@ use crate::values::generics::effects::Si
 use crate::values::generics::NonNegative;
 use crate::values::specified::color::Color;
 use crate::values::specified::length::{Length, NonNegativeLength};
 #[cfg(feature = "gecko")]
 use crate::values::specified::url::SpecifiedUrl;
 use crate::values::specified::{Angle, NumberOrPercentage};
 #[cfg(not(feature = "gecko"))]
 use crate::values::Impossible;
+use crate::Zero;
 use cssparser::{self, BasicParseErrorKind, Parser, Token};
 use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
 
 /// A specified value for a single shadow of the `box-shadow` property.
 pub type BoxShadow =
     GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;
 
 /// A specified value for a single `filter`.
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -9,21 +9,21 @@
 use super::{AllowQuirks, Number, Percentage, ToComputedValue};
 use crate::font_metrics::FontMetricsQueryResult;
 use crate::parser::{Parse, ParserContext};
 use crate::values::computed::{self, CSSPixelLength, Context};
 use crate::values::generics::length as generics;
 use crate::values::generics::length::{
     GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize,
 };
-use crate::values::generics::transform::IsZeroLength;
 use crate::values::generics::NonNegative;
 use crate::values::specified::calc::CalcNode;
 use crate::values::specified::NonNegativeNumber;
 use crate::values::{Auto, CSSFloat, Either, Normal};
+use crate::Zero;
 use app_units::Au;
 use cssparser::{Parser, Token};
 use euclid::Size2D;
 use std::cmp;
 use std::ops::{Add, Mul};
 use style_traits::values::specified::AllowedNumericType;
 use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
 
@@ -472,31 +472,16 @@ impl NoCalcLength {
             }
             "vmax" if !context.in_page_rule() => {
                 NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))
             }
             _ => return Err(())
         })
     }
 
-    #[inline]
-    /// Returns a `zero` length.
-    pub fn zero() -> NoCalcLength {
-        NoCalcLength::Absolute(AbsoluteLength::Px(0.))
-    }
-
-    #[inline]
-    /// Checks whether the length value is zero.
-    pub fn is_zero(&self) -> bool {
-        match *self {
-            NoCalcLength::Absolute(length) => length.is_zero(),
-            _ => false,
-        }
-    }
-
     /// Get a px value without context.
     #[inline]
     pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
         match *self {
             NoCalcLength::Absolute(len) => Ok(len.to_px()),
             _ => Err(()),
         }
     }
@@ -505,19 +490,22 @@ impl NoCalcLength {
     #[inline]
     pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
     }
 }
 
 impl SpecifiedValueInfo for NoCalcLength {}
 
-impl IsZeroLength for NoCalcLength {
-    #[inline]
-    fn is_zero_length(&self) -> bool {
+impl Zero for NoCalcLength {
+    fn zero() -> Self {
+        NoCalcLength::Absolute(AbsoluteLength::Px(0.))
+    }
+
+    fn is_zero(&self) -> bool {
         match *self {
             NoCalcLength::Absolute(v) => v.is_zero(),
             NoCalcLength::FontRelative(v) => v.is_zero(),
             NoCalcLength::ViewportPercentage(v) => v.is_zero(),
             NoCalcLength::ServoCharacterWidth(v) => v.0 == 0,
         }
     }
 }
@@ -580,22 +568,16 @@ impl Mul<CSSFloat> for ViewportPercentag
             ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar),
             ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar),
         }
     }
 }
 
 impl Length {
     #[inline]
-    /// Returns a `zero` length.
-    pub fn zero() -> Length {
-        Length::NoCalc(NoCalcLength::zero())
-    }
-
-    #[inline]
     fn parse_internal<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         num_context: AllowedNumericType,
         allow_quirks: AllowQuirks,
     ) -> Result<Self, ParseError<'i>> {
         // FIXME: remove early returns when lifetimes are non-lexical
         {
@@ -665,16 +647,31 @@ impl Parse for Length {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
+impl Zero for Length {
+    fn zero() -> Self {
+        Length::NoCalc(NoCalcLength::zero())
+    }
+
+    fn is_zero(&self) -> bool {
+        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
+        // non-zero here?
+        match *self {
+            Length::NoCalc(ref l) => l.is_zero(),
+            Length::Calc(..) => false,
+        }
+    }
+}
+
 impl Length {
     /// Parses a length, with quirks.
     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)
@@ -704,22 +701,16 @@ impl From<NoCalcLength> for NonNegativeL
 impl From<Length> for NonNegativeLength {
     #[inline]
     fn from(len: Length) -> Self {
         NonNegative::<Length>(len)
     }
 }
 
 impl NonNegativeLength {
-    /// Returns a `zero` length.
-    #[inline]
-    pub fn zero() -> Self {
-        Length::zero().into()
-    }
-
     /// Get an absolute length from a px value.
     #[inline]
     pub fn from_px(px_value: CSSFloat) -> Self {
         Length::from_px(px_value.max(0.)).into()
     }
 
     /// Parses a non-negative length, optionally with quirks.
     #[inline]
@@ -795,22 +786,16 @@ impl Parse for LengthPercentage {
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
 impl LengthPercentage {
     #[inline]
-    /// Returns a `zero` length.
-    pub fn zero() -> LengthPercentage {
-        LengthPercentage::Length(NoCalcLength::zero())
-    }
-
-    #[inline]
     /// Returns a `0%` value.
     pub fn zero_percent() -> LengthPercentage {
         LengthPercentage::Percentage(computed::Percentage::zero())
     }
 
     fn parse_internal<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -891,36 +876,34 @@ impl LengthPercentage {
             context,
             input,
             AllowedNumericType::NonNegative,
             allow_quirks,
         )
     }
 }
 
-impl IsZeroLength for LengthPercentage {
-    #[inline]
-    fn is_zero_length(&self) -> bool {
+impl Zero for LengthPercentage {
+    fn zero() -> Self {
+        LengthPercentage::Length(NoCalcLength::zero())
+    }
+
+    fn is_zero(&self) -> bool {
         match *self {
-            LengthPercentage::Length(l) => l.is_zero_length(),
+            LengthPercentage::Length(l) => l.is_zero(),
             LengthPercentage::Percentage(p) => p.0 == 0.0,
             LengthPercentage::Calc(_) => false,
         }
     }
 }
 
 /// A specified type for `<length-percentage> | auto`.
 pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
 
 impl LengthPercentageOrAuto {
-    /// Returns a value representing a `0` length.
-    pub fn zero() -> Self {
-        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero())
-    }
-
     /// Returns a value representing `0%`.
     #[inline]
     pub fn zero_percent() -> Self {
         generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
     }
 
     /// Parses a length or a percentage, allowing the unitless length quirk.
     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
@@ -936,21 +919,16 @@ impl LengthPercentageOrAuto {
     }
 }
 
 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
 pub type NonNegativeLengthPercentageOrAuto =
     generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
 
 impl NonNegativeLengthPercentageOrAuto {
-    /// Returns a value representing a `0` length.
-    pub fn zero() -> Self {
-        generics::LengthPercentageOrAuto::LengthPercentage(NonNegativeLengthPercentage::zero())
-    }
-
     /// Returns a value representing `0%`.
     #[inline]
     pub fn zero_percent() -> Self {
         generics::LengthPercentageOrAuto::LengthPercentage(
             NonNegativeLengthPercentage::zero_percent(),
         )
     }
 
@@ -991,22 +969,16 @@ impl Parse for NonNegativeLengthPercenta
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Self::parse_quirky(context, input, AllowQuirks::No)
     }
 }
 
 impl NonNegativeLengthPercentage {
     #[inline]
-    /// Returns a `zero` length.
-    pub fn zero() -> Self {
-        NonNegative(LengthPercentage::zero())
-    }
-
-    #[inline]
     /// Returns a `0%` value.
     pub fn zero_percent() -> Self {
         NonNegative(LengthPercentage::zero_percent())
     }
 
     /// Parses a length or a percentage, allowing the unitless length quirk.
     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
     #[inline]
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -12,16 +12,17 @@ use crate::parser::{Parse, ParserContext
 use crate::str::HTML_SPACE_CHARACTERS;
 use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
 use crate::values::computed::{Context, Percentage, ToComputedValue};
 use crate::values::generics::position::Position as GenericPosition;
 use crate::values::generics::position::ZIndex as GenericZIndex;
 use crate::values::specified::transform::OriginComponent;
 use crate::values::specified::{AllowQuirks, Integer, LengthPercentage};
 use crate::values::{Either, None_};
+use crate::Zero;
 use cssparser::Parser;
 use selectors::parser::SelectorParseErrorKind;
 use servo_arc::Arc;
 use std::fmt::{self, Write};
 use std::ops::Range;
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
 /// The specified value of a CSS `<position>`
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -6,16 +6,17 @@
 
 use crate::parser::{Parse, ParserContext};
 use crate::values::computed::{Context, LengthPercentage as ComputedLengthPercentage};
 use crate::values::computed::{Percentage as ComputedPercentage, ToComputedValue};
 use crate::values::generics::transform as generic;
 use crate::values::generics::transform::{Matrix, Matrix3D};
 use crate::values::specified::position::{Side, X, Y};
 use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number};
+use crate::Zero;
 use cssparser::Parser;
 use style_traits::{ParseError, StyleParseErrorKind};
 
 pub use crate::values::generics::transform::TransformStyle;
 
 /// A single operation in a specified CSS `transform`
 pub type TransformOperation =
     generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;