servo: Merge #17731 - style: Respect calc for percentages (from emilio:percentage-calc); r=nox
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 17 Jul 2017 01:29:11 -0700
changeset 369152 19fd4dedb30366c4c831aa34f8b3c621d3039e19
parent 369151 164b77a5a9d9a5753c7b83be98a0052fe03ebfff
child 369153 81572281c0737ee7597409fcd1fa86b39e458c0c
push id32192
push userkwierso@gmail.com
push dateTue, 18 Jul 2017 00:01:01 +0000
treeherdermozilla-central@efc0b1525edb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnox
milestone56.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
servo: Merge #17731 - style: Respect calc for percentages (from emilio:percentage-calc); r=nox Source-Repo: https://github.com/servo/servo Source-Revision: 2d37700cf819d901552cfb3954e948f1fbadcf78
servo/components/layout/display_list_builder.rs
servo/components/script/dom/element.rs
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/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/position.rs
servo/components/style/values/computed/transform.rs
servo/components/style/values/generics/flex.rs
servo/components/style/values/specified/basic_shape.rs
servo/components/style/values/specified/calc.rs
servo/components/style/values/specified/effects.rs
servo/components/style/values/specified/image.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/position.rs
servo/components/style/values/specified/transform.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/animated_properties.rs
servo/tests/unit/style/attr.rs
servo/tests/unit/style/properties/serialization.rs
servo/tests/unit/style/stylesheets.rs
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -51,27 +51,26 @@ use style::computed_values::{background_
 use style::computed_values::{background_repeat, border_style, cursor};
 use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility};
 use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
 use style::properties::{self, ServoComputedValues};
 use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
 use style::properties::style_structs;
 use style::servo::restyle_damage::REPAINT;
 use style::values::{Either, RGBA};
-use style::values::computed::{Angle, Gradient, GradientItem, LengthOrPercentage};
+use style::values::computed::{Angle, Gradient, GradientItem, LengthOrPercentage, Percentage};
 use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position};
 use style::values::computed::effects::SimpleShadow;
 use style::values::computed::image::{EndingShape, LineDirection};
 use style::values::generics::background::BackgroundSize;
 use style::values::generics::effects::Filter;
 use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape};
 use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
 use style::values::generics::image::{Image, ShapeExtent};
 use style::values::generics::image::PaintWorklet;
-use style::values::specified::length::Percentage;
 use style::values::specified::position::{X, Y};
 use style_traits::CSSPixel;
 use style_traits::cursor::Cursor;
 use table_cell::CollapsedBordersForCell;
 use webrender_api::{ClipId, ColorF, ComplexClipRegion, GradientStop, LocalClip, RepeatMode};
 use webrender_api::{ScrollPolicy, TransformStyle};
 use webrender_helpers::{ToBorderRadius, ToMixBlendMode, ToRectF, ToTransformStyle};
 
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -108,17 +108,17 @@ use style::properties::{Importance, Prop
 use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
 use style::rule_tree::CascadeLevel;
 use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
 use style::selector_parser::extended_filtering;
 use style::shared_lock::{SharedRwLock, Locked};
 use style::stylearc::Arc;
 use style::thread_state;
 use style::values::{CSSFloat, Either};
-use style::values::specified;
+use style::values::{specified, computed};
 use stylesheet_loader::StylesheetOwner;
 
 // TODO: Update focus state when the top-level browsing context gains or loses system focus,
 // and when the element enters or leaves a browsing context container.
 // https://html.spec.whatwg.org/multipage/#selector-focus
 
 #[dom_struct]
 pub struct Element {
@@ -571,17 +571,17 @@ impl LayoutElementHelpers for LayoutJS<E
             LengthOrPercentageOrAuto::Auto
         };
 
         // FIXME(emilio): Use from_computed value here and below.
         match width {
             LengthOrPercentageOrAuto::Auto => {}
             LengthOrPercentageOrAuto::Percentage(percentage) => {
                 let width_value =
-                    specified::LengthOrPercentageOrAuto::Percentage(specified::Percentage(percentage));
+                    specified::LengthOrPercentageOrAuto::Percentage(computed::Percentage(percentage));
                 hints.push(from_declaration(
                     shared_lock,
                     PropertyDeclaration::Width(width_value)));
             }
             LengthOrPercentageOrAuto::Length(length) => {
                 let width_value = specified::LengthOrPercentageOrAuto::Length(
                     specified::NoCalcLength::Absolute(specified::AbsoluteLength::Px(length.to_f32_px())));
                 hints.push(from_declaration(
@@ -600,17 +600,17 @@ impl LayoutElementHelpers for LayoutJS<E
         } else {
             LengthOrPercentageOrAuto::Auto
         };
 
         match height {
             LengthOrPercentageOrAuto::Auto => {}
             LengthOrPercentageOrAuto::Percentage(percentage) => {
                 let height_value =
-                    specified::LengthOrPercentageOrAuto::Percentage(specified::Percentage(percentage));
+                    specified::LengthOrPercentageOrAuto::Percentage(computed::Percentage(percentage));
                 hints.push(from_declaration(
                     shared_lock,
                     PropertyDeclaration::Height(height_value)));
             }
             LengthOrPercentageOrAuto::Length(length) => {
                 let height_value = specified::LengthOrPercentageOrAuto::Length(
                     specified::NoCalcLength::Absolute(specified::AbsoluteLength::Px(length.to_f32_px())));
                 hints.push(from_declaration(
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -13,21 +13,20 @@ use gecko::values::{convert_rgba_to_nsco
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
 use gecko_bindings::structs::{nsCSSUnit, nsStyleCoord_CalcValue, nsStyleImage};
 use gecko_bindings::structs::{nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
 use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
-use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
+use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, Percentage};
 use values::generics::grid::TrackSize;
 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
 use values::generics::rect::Rect;
-use values::specified::length::Percentage;
 use values::specified::url::SpecifiedUrl;
 
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
         let has_percentage = other.percentage.is_some();
         nsStyleCoord_CalcValue {
             mLength: other.unclamped_length().0,
             mPercent: other.percentage.map_or(0., |p| p.0),
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -13,23 +13,22 @@ use gecko_bindings::structs::{CounterSty
 use gecko_bindings::structs::{StyleGridTrackBreadth, StyleShapeRadius};
 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};
-use values::computed::{MaxLength, MozLength};
+use values::computed::{MaxLength, MozLength, Percentage};
 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};
-use values::specified::Percentage;
 
 /// 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>;
 }
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -9,18 +9,17 @@ use gecko_bindings::bindings;
 use gecko_bindings::structs;
 use gecko_bindings::structs::{nsCSSValue, nsCSSUnit};
 use gecko_bindings::structs::{nsCSSValue_Array, nsCSSValueList, nscolor};
 use gecko_string_cache::Atom;
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::{Index, IndexMut};
 use std::slice;
-use values::computed::{Angle, LengthOrPercentage};
-use values::specified::length::Percentage;
+use values::computed::{Angle, LengthOrPercentage, Percentage};
 use values::specified::url::SpecifiedUrl;
 
 impl nsCSSValue {
     /// Create a CSSValue with null unit, useful to be used as a return value.
     #[inline]
     pub fn null() -> Self {
         unsafe { mem::zeroed() }
     }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -61,17 +61,17 @@ use rule_tree::StrongRuleNode;
 use std::fmt::{self, Debug};
 use std::mem::{forget, transmute, zeroed};
 use std::ptr;
 use stylearc::Arc;
 use std::cmp;
 use values::{Auto, CustomIdent, Either, KeyframesName};
 use values::computed::ToComputedValue;
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
-use values::specified::length::Percentage;
+use values::computed::length::Percentage;
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     % endfor
 }
 
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -36,22 +36,21 @@ use values::{CSSFloat, CustomIdent, Eith
 use values::animated::ToAnimatedValue;
 use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 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, ToComputedValue};
+use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
 use values::generics::{SVGPaint, SVGPaintKind};
 use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
-use values::specified::length::Percentage;
 
 
 /// A longhand property whose animation type is not "none".
 ///
 /// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is
 /// not animatable from CSS animations or Web Animations. CSS transitions also does not allow
 /// animating 'display', but for CSS transitions we have the separate TransitionProperty type.
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -1681,17 +1680,16 @@ fn add_weighted_transform_lists(from_lis
                 }
                 _ => {
                     // This should be unreachable due to the can_interpolate_list() call.
                     unreachable!();
                 }
             }
         }
     } else {
-        use values::specified::Percentage;
         let from_transform_list = TransformList(Some(from_list.to_vec()));
         let to_transform_list = TransformList(Some(to_list.to_vec()));
         result.push(
             TransformOperation::InterpolateMatrix { from_list: from_transform_list,
                                                     to_list: to_transform_list,
                                                     progress: Percentage(other_portion as f32) });
     }
 
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -273,17 +273,17 @@
     initial_value="computed::BorderImageSideWidth::one().into()",
     initial_specified_value="specified::BorderImageSideWidth::one().into()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
     animation_value_type="discrete",
     boxed=True)}
 
 ${helpers.predefined_type("border-image-slice", "BorderImageSlice",
     initial_value="computed::NumberOrPercentage::Percentage(computed::Percentage(1.)).into()",
-    initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage(1.)).into()",
+    initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage::new(1.)).into()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
     animation_value_type="discrete",
     boxed=True)}
 
 #[cfg(feature = "gecko")]
 impl ::values::computed::BorderImageWidth {
     pub fn to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides) {
         use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -731,28 +731,28 @@
 <%helpers:longhand name="transform" extra_prefixes="webkit"
                    animation_value_type="ComputedValue"
                    flags="CREATES_STACKING_CONTEXT FIXPOS_CB"
                    spec="https://drafts.csswg.org/css-transforms/#propdef-transform">
     use app_units::Au;
     use values::computed::{LengthOrPercentageOrNumber as ComputedLoPoNumber, LengthOrNumber as ComputedLoN};
     use values::computed::{LengthOrPercentage as ComputedLoP, Length as ComputedLength};
     use values::generics::transform::Matrix;
-    use values::specified::{Angle, Integer, Length, LengthOrPercentage, Percentage};
+    use values::specified::{Angle, Integer, Length, LengthOrPercentage};
     use values::specified::{LengthOrNumber, LengthOrPercentageOrNumber as LoPoNumber, Number};
     use style_traits::ToCss;
     use style_traits::values::Css;
 
     use std::fmt;
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
         use values::computed;
-        use values::computed::{Length, LengthOrPercentage, Percentage};
+        use values::computed::{Length, LengthOrPercentage};
 
         #[derive(Clone, Copy, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct ComputedMatrix {
             pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat,
             pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat,
             pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat,
             pub m41: CSSFloat, pub m42: CSSFloat, pub m43: CSSFloat, pub m44: CSSFloat,
@@ -812,17 +812,17 @@
             //                                    Scale(...) ],
             //                       to_list: [ AccumulateMatrix { from_list: ...,
             //                                                     to_list: [ InterpolateMatrix,
             //                                                                 ... ],
             //                                                     count: ... } ],
             //                       progress: ... } ]
             InterpolateMatrix { from_list: T,
                                 to_list: T,
-                                progress: Percentage },
+                                progress: computed::Percentage },
             // For accumulate operation of mismatched transform lists.
             AccumulateMatrix { from_list: T,
                                to_list: T,
                                count: computed::Integer },
         }
 
         #[derive(Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -899,17 +899,17 @@
         /// Part of CSS Transform Module Level 2 and defined at
         /// [ยง 13.1. 3D Transform Function](https://drafts.csswg.org/css-transforms-2/#funcdef-perspective).
         ///
         /// The value must be greater than or equal to zero.
         Perspective(specified::Length),
         /// A intermediate type for interpolation of mismatched transform lists.
         InterpolateMatrix { from_list: SpecifiedValue,
                             to_list: SpecifiedValue,
-                            progress: Percentage },
+                            progress: computed::Percentage },
         /// A intermediate type for accumulation of mismatched transform lists.
         AccumulateMatrix { from_list: SpecifiedValue,
                            to_list: SpecifiedValue,
                            count: Integer },
     }
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -561,18 +561,17 @@ macro_rules! impl_gecko_keyword_conversi
 <%helpers:longhand name="font-size" need_clone="True" animation_value_type="ComputedValue"
                    allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size">
     use app_units::Au;
     use properties::longhands::system_font::SystemFont;
     use properties::style_structs::Font;
     use std::fmt;
     use style_traits::{HasViewportPercentage, ToCss};
     use values::FONT_MEDIUM_PX;
-    use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage};
-    use values::specified::{NoCalcLength, Percentage};
+    use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage, NoCalcLength};
     use values::specified::length::FontBaseSize;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 SpecifiedValue::Length(ref lop) => lop.to_css(dest),
                 SpecifiedValue::Keyword(kw, _) => kw.to_css(dest),
                 SpecifiedValue::Smaller => dest.write_str("smaller"),
@@ -805,18 +804,18 @@ macro_rules! impl_gecko_keyword_conversi
                 }
                 SpecifiedValue::Length(LengthOrPercentage::Length(
                         NoCalcLength::ServoCharacterWidth(value))) => {
                     value.to_computed_value(base_size.resolve(context))
                 }
                 SpecifiedValue::Length(LengthOrPercentage::Length(ref l)) => {
                     l.to_computed_value(context)
                 }
-                SpecifiedValue::Length(LengthOrPercentage::Percentage(Percentage(value))) => {
-                    base_size.resolve(context).scale_by(value)
+                SpecifiedValue::Length(LengthOrPercentage::Percentage(pc)) => {
+                    base_size.resolve(context).scale_by(pc.0)
                 }
                 SpecifiedValue::Length(LengthOrPercentage::Calc(ref calc)) => {
                     let calc = calc.to_computed_value(context);
                     calc.to_used_value(Some(base_size.resolve(context))).unwrap()
                 }
                 SpecifiedValue::Keyword(ref key, fraction) => {
                     key.to_computed_value(context).scale_by(fraction)
                 }
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -7,21 +7,62 @@
 use app_units::{Au, AU_PER_PX};
 use ordered_float::NotNaN;
 use std::fmt;
 use style_traits::ToCss;
 use style_traits::values::specified::AllowedLengthType;
 use super::{Number, ToComputedValue, Context};
 use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
 use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
-use values::specified::length::{Percentage, ViewportPercentageLength};
+use values::specified::length::ViewportPercentageLength;
 
 pub use super::image::Image;
 pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
 
+/// A computed `<percentage>` value.
+///
+/// FIXME(emilio): why is this in length.rs?
+#[derive(Clone, Copy, Debug, Default, PartialEq, HasViewportPercentage)]
+#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
+pub struct Percentage(pub CSSFloat);
+
+impl Percentage {
+    /// 0%
+    #[inline]
+    pub fn zero() -> Self {
+        Percentage(0.)
+    }
+
+    /// 100%
+    #[inline]
+    pub fn hundred() -> Self {
+        Percentage(1.)
+    }
+}
+
+impl ToCss for Percentage {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        write!(dest, "{}%", self.0 * 100.)
+    }
+}
+
+impl ToComputedValue for specified::Percentage {
+    type ComputedValue = Percentage;
+
+    #[inline]
+    fn to_computed_value(&self, _: &Context) -> Percentage {
+        Percentage(self.get())
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Percentage) -> Self {
+        specified::Percentage::new(computed.0)
+    }
+}
+
 impl ToComputedValue for specified::NoCalcLength {
     type ComputedValue = Au;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Au {
         match *self {
             specified::NoCalcLength::Absolute(length) =>
                 length.to_computed_value(context),
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -33,21 +33,21 @@ pub use self::effects::{BoxShadow, Filte
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 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, Percentage, UrlOrNone};
+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};
+pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength, Percentage};
 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/computed/position.rs
+++ b/servo/components/style/values/computed/position.rs
@@ -4,19 +4,18 @@
 
 //! CSS handling for the computed value of
 //! [`position`][position] values.
 //!
 //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
 
 use std::fmt;
 use style_traits::ToCss;
-use values::computed::LengthOrPercentage;
+use values::computed::{LengthOrPercentage, Percentage};
 use values::generics::position::Position as GenericPosition;
-use values::specified::length::Percentage;
 
 /// The computed value of a CSS `<position>`
 pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
 
 /// The computed value of a CSS horizontal position.
 pub type HorizontalPosition = LengthOrPercentage;
 
 /// The computed value of a CSS vertical position.
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -1,19 +1,18 @@
 /* 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 that are related to transformations.
 
 use properties::animated_properties::Animatable;
-use values::computed::{Length, LengthOrPercentage, Number};
+use values::computed::{Length, LengthOrPercentage, Number, Percentage};
 use values::generics::transform::TimingFunction as GenericTimingFunction;
 use values::generics::transform::TransformOrigin as GenericTransformOrigin;
-use values::specified::length::Percentage;
 
 /// The computed value of a CSS `<transform-origin>`
 pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
 
 /// A computed timing function.
 pub type TimingFunction = GenericTimingFunction<u32, Number>;
 
 impl TransformOrigin {
--- a/servo/components/style/values/generics/flex.rs
+++ b/servo/components/style/values/generics/flex.rs
@@ -1,15 +1,15 @@
 /* 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::specified::Percentage;
+use values::computed::Percentage;
 
 /// A generic value for the `flex-basis` property.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
 pub enum FlexBasis<LengthOrPercentage> {
     /// `auto`
     Auto,
     /// `content`
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -7,24 +7,25 @@
 //!
 //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::borrow::Cow;
 use std::fmt;
 use style_traits::{ToCss, ParseError, StyleParseError};
+use values::computed::Percentage;
 use values::generics::basic_shape::{Circle as GenericCircle};
 use values::generics::basic_shape::{ClippingShape as GenericClippingShape, Ellipse as GenericEllipse};
 use values::generics::basic_shape::{FillRule, BasicShape as GenericBasicShape};
 use values::generics::basic_shape::{FloatAreaShape as GenericFloatAreaShape, InsetRect as GenericInsetRect};
 use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
 use values::generics::basic_shape::{Polygon as GenericPolygon, ShapeRadius as GenericShapeRadius};
 use values::generics::rect::Rect;
-use values::specified::{LengthOrPercentage, Percentage};
+use values::specified::LengthOrPercentage;
 use values::specified::border::BorderRadius;
 use values::specified::position::{HorizontalPosition, Position, PositionComponent, Side, VerticalPosition};
 use values::specified::url::SpecifiedUrl;
 
 /// A specified clipping shape.
 pub type ClippingShape = GenericClippingShape<BasicShape>;
 
 /// A specified float area shape.
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -9,19 +9,19 @@
 use app_units::Au;
 use cssparser::{Parser, Token, BasicParseError};
 use parser::ParserContext;
 use std::ascii::AsciiExt;
 use std::fmt;
 use style_traits::{HasViewportPercentage, ToCss, ParseError, StyleParseError};
 use style_traits::values::specified::AllowedLengthType;
 use values::{CSSInteger, CSSFloat};
+use values::computed;
 use values::specified::{Angle, Time};
-use values::specified::length::{FontRelativeLength, NoCalcLength};
-use values::specified::length::{Percentage, ViewportPercentageLength};
+use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength};
 
 /// A node inside a `Calc` expression's AST.
 #[derive(Clone, Debug)]
 pub enum CalcNode {
     /// `<length>`
     Length(NoCalcLength),
     /// `<angle>`
     Angle(Angle),
@@ -47,16 +47,18 @@ pub enum CalcNode {
 #[derive(Clone, Copy, PartialEq)]
 pub enum CalcUnit {
     /// `<number>`
     Number,
     /// `<integer>`
     Integer,
     /// `<length>`
     Length,
+    /// `<percentage>`
+    Percentage,
     /// `<length> | <percentage>`
     LengthOrPercentage,
     /// `<angle>`
     Angle,
     /// `<time>`
     Time,
 }
 
@@ -70,17 +72,17 @@ pub struct CalcLengthOrPercentage {
     pub vw: Option<CSSFloat>,
     pub vh: Option<CSSFloat>,
     pub vmin: Option<CSSFloat>,
     pub vmax: Option<CSSFloat>,
     pub em: Option<CSSFloat>,
     pub ex: Option<CSSFloat>,
     pub ch: Option<CSSFloat>,
     pub rem: Option<CSSFloat>,
-    pub percentage: Option<Percentage>,
+    pub percentage: Option<computed::Percentage>,
     #[cfg(feature = "gecko")]
     pub mozmm: Option<CSSFloat>,
 }
 
 impl HasViewportPercentage for CalcLengthOrPercentage {
     fn has_viewport_percentage(&self) -> bool {
         self.vw.is_some() || self.vh.is_some() ||
         self.vmin.is_some() || self.vmax.is_some()
@@ -141,19 +143,18 @@ impl CalcNode {
     /// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
     /// `expected_unit`.
     ///
     /// May return a "complex" `CalcNode`, in the presence of a parenthesized
     /// expression, for example.
     fn parse_one<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
-        expected_unit: CalcUnit)
-        -> Result<Self, ParseError<'i>>
-    {
+        expected_unit: CalcUnit
+    ) -> Result<Self, ParseError<'i>> {
         match (input.next()?, expected_unit) {
             (Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)),
             (Token::Dimension { value, ref unit, .. }, CalcUnit::Length) |
             (Token::Dimension { value, ref unit, .. }, CalcUnit::LengthOrPercentage) => {
                 NoCalcLength::parse_dimension(context, value, unit)
                     .map(CalcNode::Length)
                     .map_err(|()| StyleParseError::UnspecifiedError.into())
             }
@@ -162,17 +163,18 @@ impl CalcNode {
                     .map(CalcNode::Angle)
                     .map_err(|()| StyleParseError::UnspecifiedError.into())
             }
             (Token::Dimension { value, ref unit, .. }, CalcUnit::Time) => {
                 Time::parse_dimension(value, unit, /* from_calc = */ true)
                     .map(CalcNode::Time)
                     .map_err(|()| StyleParseError::UnspecifiedError.into())
             }
-            (Token::Percentage { unit_value, .. }, CalcUnit::LengthOrPercentage) => {
+            (Token::Percentage { unit_value, .. }, CalcUnit::LengthOrPercentage) |
+            (Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
                 Ok(CalcNode::Percentage(unit_value))
             }
             (Token::ParenthesisBlock, _) => {
                 input.parse_nested_block(|i| {
                     CalcNode::parse(context, i, expected_unit)
                 })
             }
             (Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {
@@ -278,29 +280,67 @@ impl CalcNode {
         let mut ret = CalcLengthOrPercentage {
             clamping_mode: clamping_mode,
             .. Default::default()
         };
         self.add_length_or_percentage_to(&mut ret, 1.0)?;
         Ok(ret)
     }
 
+    /// Tries to simplify this expression into a `<percentage>` value.
+    fn to_percentage(&self) -> Result<CSSFloat, ()> {
+        Ok(match *self {
+            CalcNode::Percentage(percentage) => percentage,
+            CalcNode::Sub(ref a, ref b) => {
+                a.to_percentage()? - b.to_percentage()?
+            }
+            CalcNode::Sum(ref a, ref b) => {
+                a.to_percentage()? + b.to_percentage()?
+            }
+            CalcNode::Mul(ref a, ref b) => {
+                match a.to_percentage() {
+                    Ok(lhs) => {
+                        let rhs = b.to_number()?;
+                        lhs * rhs
+                    }
+                    Err(..) => {
+                        let lhs = a.to_number()?;
+                        let rhs = b.to_percentage()?;
+                        lhs * rhs
+                    }
+                }
+            }
+            CalcNode::Div(ref a, ref b) => {
+                let lhs = a.to_percentage()?;
+                let rhs = b.to_number()?;
+                if rhs == 0. {
+                    return Err(())
+                }
+                lhs / rhs
+            }
+            CalcNode::Number(..) |
+            CalcNode::Length(..) |
+            CalcNode::Angle(..) |
+            CalcNode::Time(..) => return Err(()),
+        })
+    }
+
     /// Puts this `<length>` or `<percentage>` into `ret`, or error.
     ///
     /// `factor` is the sign or multiplicative factor to account for the sign
     /// (this allows adding and substracting into the return value).
     fn add_length_or_percentage_to(
         &self,
         ret: &mut CalcLengthOrPercentage,
         factor: CSSFloat)
         -> Result<(), ()>
     {
         match *self {
             CalcNode::Percentage(pct) => {
-                ret.percentage = Some(Percentage(
+                ret.percentage = Some(computed::Percentage(
                     ret.percentage.map_or(0., |p| p.0) + pct * factor,
                 ));
             }
             CalcNode::Length(ref l) => {
                 match *l {
                     NoCalcLength::Absolute(abs) => {
                         ret.absolute = Some(
                             ret.absolute.unwrap_or(Au(0)) +
@@ -490,74 +530,78 @@ impl CalcNode {
             CalcNode::Angle(..) |
             CalcNode::Time(..) => return Err(()),
         })
     }
 
     /// Convenience parsing function for integers.
     pub fn parse_integer<'i, 't>(
         context: &ParserContext,
-        input: &mut Parser<'i, 't>)
-        -> Result<CSSInteger, ParseError<'i>>
-    {
+        input: &mut Parser<'i, 't>
+    ) -> Result<CSSInteger, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Integer)?
             .to_number()
             .map(|n| n as CSSInteger)
             .map_err(|()| StyleParseError::UnspecifiedError.into())
     }
 
     /// Convenience parsing function for `<length> | <percentage>`.
     pub fn parse_length_or_percentage<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
-        clamping_mode: AllowedLengthType)
-        -> Result<CalcLengthOrPercentage, ParseError<'i>>
-    {
+        clamping_mode: AllowedLengthType
+    ) -> Result<CalcLengthOrPercentage, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::LengthOrPercentage)?
             .to_length_or_percentage(clamping_mode)
             .map_err(|()| StyleParseError::UnspecifiedError.into())
     }
 
+    /// Convenience parsing function for percentages.
+    pub fn parse_percentage<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<CSSFloat, ParseError<'i>> {
+        Self::parse(context, input, CalcUnit::Percentage)?
+            .to_percentage()
+            .map_err(|()| StyleParseError::UnspecifiedError.into())
+    }
+
     /// Convenience parsing function for `<length>`.
     pub fn parse_length<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
-        clamping_mode: AllowedLengthType)
-        -> Result<CalcLengthOrPercentage, ParseError<'i>>
-    {
+        clamping_mode: AllowedLengthType
+    ) -> Result<CalcLengthOrPercentage, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Length)?
             .to_length_or_percentage(clamping_mode)
             .map_err(|()| StyleParseError::UnspecifiedError.into())
     }
 
     /// Convenience parsing function for `<number>`.
     pub fn parse_number<'i, 't>(
         context: &ParserContext,
-        input: &mut Parser<'i, 't>)
-        -> Result<CSSFloat, ParseError<'i>>
-    {
+        input: &mut Parser<'i, 't>
+    ) -> Result<CSSFloat, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Number)?
             .to_number()
             .map_err(|()| StyleParseError::UnspecifiedError.into())
     }
 
     /// Convenience parsing function for `<angle>`.
     pub fn parse_angle<'i, 't>(
         context: &ParserContext,
-        input: &mut Parser<'i, 't>)
-        -> Result<Angle, ParseError<'i>>
-    {
+        input: &mut Parser<'i, 't>
+    ) -> Result<Angle, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Angle)?
             .to_angle()
             .map_err(|()| StyleParseError::UnspecifiedError.into())
     }
 
     /// Convenience parsing function for `<time>`.
     pub fn parse_time<'i, 't>(
         context: &ParserContext,
-        input: &mut Parser<'i, 't>)
-        -> Result<Time, ParseError<'i>>
-    {
+        input: &mut Parser<'i, 't>
+    ) -> Result<Time, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Time)?
             .to_time()
             .map_err(|()| StyleParseError::UnspecifiedError.into())
     }
 }
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -1,26 +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/. */
 
 //! Specified types for CSS values related to effects.
 
-use cssparser::{BasicParseError, Parser, Token};
+use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use style_traits::{ParseError, StyleParseError};
 #[cfg(not(feature = "gecko"))]
 use values::Impossible;
 use values::computed::{Context, Number as ComputedNumber, ToComputedValue};
 use values::computed::effects::BoxShadow as ComputedBoxShadow;
 use values::computed::effects::SimpleShadow as ComputedSimpleShadow;
 use values::generics::effects::BoxShadow as GenericBoxShadow;
 use values::generics::effects::Filter as GenericFilter;
 use values::generics::effects::SimpleShadow as GenericSimpleShadow;
-use values::specified::{Angle, Percentage};
+use values::specified::{Angle, NumberOrPercentage};
 use values::specified::color::Color;
 use values::specified::length::Length;
 #[cfg(feature = "gecko")]
 use values::specified::url::SpecifiedUrl;
 
 /// A specified value for a single shadow of the `box-shadow` property.
 pub type BoxShadow = GenericBoxShadow<Option<Color>, Length, Option<Length>>;
 
@@ -28,25 +28,46 @@ pub type BoxShadow = GenericBoxShadow<Op
 #[cfg(feature = "gecko")]
 pub type Filter = GenericFilter<Angle, Factor, Length, SimpleShadow>;
 
 /// A specified value for a single `filter`.
 #[cfg(not(feature = "gecko"))]
 pub type Filter = GenericFilter<Angle, Factor, Length, Impossible>;
 
 /// A value for the `<factor>` parts in `Filter`.
-///
-/// FIXME: Should be `NumberOrPercentage`, but Gecko doesn't support that yet.
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
-pub enum Factor {
-    /// Literal number.
-    Number(ComputedNumber),
-    /// Literal percentage.
-    Percentage(Percentage),
+pub struct Factor(NumberOrPercentage);
+
+impl Parse for Factor {
+    #[inline]
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        NumberOrPercentage::parse_non_negative(context, input).map(Factor)
+    }
+}
+
+impl ToComputedValue for Factor {
+    type ComputedValue = ComputedNumber;
+
+    #[inline]
+    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+        use values::computed::NumberOrPercentage;
+        match self.0.to_computed_value(context) {
+            NumberOrPercentage::Number(n) => n,
+            NumberOrPercentage::Percentage(p) => p.0,
+        }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+        Factor(NumberOrPercentage::Number(ToComputedValue::from_computed_value(computed)))
+    }
 }
 
 /// A specified value for the `drop-shadow()` filter.
 pub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<Length>>;
 
 impl Parse for BoxShadow {
     fn parse<'i, 't>(
         context: &ParserContext,
@@ -151,53 +172,16 @@ impl Parse for Filter {
                 "saturate" => Ok(GenericFilter::Saturate(Factor::parse(context, i)?)),
                 "sepia" => Ok(GenericFilter::Sepia(Factor::parse(context, i)?)),
                 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
             }
         })
     }
 }
 
-impl Parse for Factor {
-    #[inline]
-    fn parse<'i, 't>(
-        _context: &ParserContext,
-        input: &mut Parser<'i, 't>
-    ) -> Result<Self, ParseError<'i>> {
-        match input.next()? {
-            Token::Number { value, .. } if value.is_sign_positive() => {
-                Ok(Factor::Number(value))
-            },
-            Token::Percentage { unit_value, .. } if unit_value.is_sign_positive() => {
-                Ok(Factor::Percentage(Percentage(unit_value)))
-            },
-            other => Err(BasicParseError::UnexpectedToken(other).into()),
-        }
-    }
-}
-
-impl ToComputedValue for Factor {
-    /// This should actually be `ComputedNumberOrPercentage`, but layout uses
-    /// `computed::effects::Filter` directly in `StackingContext`.
-    type ComputedValue = ComputedNumber;
-
-    #[inline]
-    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
-        match *self {
-            Factor::Number(number) => number,
-            Factor::Percentage(percentage) => percentage.0,
-        }
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-        Factor::Number(*computed)
-    }
-}
-
 impl Parse for SimpleShadow {
     #[inline]
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<Self, ParseError<'i>> {
         let color = input.try(|i| Color::parse(context, i)).ok();
         let horizontal = Length::parse(context, input)?;
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -325,20 +325,20 @@ impl Gradient {
                     Ok(Self::new(x, y))
                 })
             }
         }
 
         impl<S: Side> From<Component<S>> for NumberOrPercentage {
             fn from(component: Component<S>) -> Self {
                 match component {
-                    Component::Center => NumberOrPercentage::Percentage(Percentage(0.5)),
+                    Component::Center => NumberOrPercentage::Percentage(Percentage::new(0.5)),
                     Component::Number(number) => number,
                     Component::Side(side) => {
-                        let p = Percentage(if side.is_start() { 0. } else { 1. });
+                        let p = if side.is_start() { Percentage::zero() } else { Percentage::hundred() };
                         NumberOrPercentage::Percentage(p)
                     },
                 }
             }
         }
 
         impl<S: Side> From<Component<S>> for PositionComponent<S> {
             fn from(component: Component<S>) -> Self {
@@ -358,17 +358,17 @@ impl Gradient {
                 }
             }
         }
 
         impl<S: Copy + Side> Component<S> {
             fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
                 match (NumberOrPercentage::from(*self), NumberOrPercentage::from(*other)) {
                     (NumberOrPercentage::Percentage(a), NumberOrPercentage::Percentage(b)) => {
-                        a.0.partial_cmp(&b.0)
+                        a.get().partial_cmp(&b.get())
                     },
                     (NumberOrPercentage::Number(a), NumberOrPercentage::Number(b)) => {
                         a.value.partial_cmp(&b.value)
                     },
                     (_, _) => {
                         None
                     }
                 }
@@ -439,51 +439,51 @@ impl Gradient {
         let mut items = input.try(|i| {
             i.expect_comma()?;
             i.parse_comma_separated(|i| {
                 let function = i.expect_function()?;
                 let (color, mut p) = i.parse_nested_block(|i| {
                     let p = match_ignore_ascii_case! { &function,
                         "color-stop" => {
                             let p = match NumberOrPercentage::parse(context, i)? {
-                                NumberOrPercentage::Number(number) => number.value,
-                                NumberOrPercentage::Percentage(p) => p.0,
+                                NumberOrPercentage::Number(number) => Percentage::new(number.value),
+                                NumberOrPercentage::Percentage(p) => p,
                             };
                             i.expect_comma()?;
                             p
                         },
-                        "from" => 0.,
-                        "to" => 1.,
+                        "from" => Percentage::zero(),
+                        "to" => Percentage::hundred(),
                         _ => return Err(StyleParseError::UnexpectedFunction(function.clone()).into()),
                     };
                     let color = Color::parse(context, i)?;
                     if color == Color::CurrentColor {
                         return Err(StyleParseError::UnspecifiedError.into());
                     }
                     Ok((color.into(), p))
                 })?;
                 if reverse_stops {
-                    p = 1. - p;
+                    p.reverse();
                 }
                 Ok(GenericGradientItem::ColorStop(GenericColorStop {
                     color: color,
-                    position: Some(LengthOrPercentage::Percentage(Percentage(p))),
+                    position: Some(p.into()),
                 }))
             })
         }).unwrap_or(vec![]);
 
         if items.is_empty() {
             items = vec![
                 GenericGradientItem::ColorStop(GenericColorStop {
                     color: Color::transparent().into(),
-                    position: Some(Percentage(0.).into()),
+                    position: Some(Percentage::zero().into()),
                 }),
                 GenericGradientItem::ColorStop(GenericColorStop {
                     color: Color::transparent().into(),
-                    position: Some(Percentage(1.).into()),
+                    position: Some(Percentage::hundred().into()),
                 }),
             ];
         } else if items.len() == 1 {
             let first = items[0].clone();
             items.push(first);
         } else {
             items.sort_by(|a, b| {
                 match (a, b) {
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -15,17 +15,17 @@ use std::{cmp, fmt, mem};
 use std::ascii::AsciiExt;
 use std::ops::Mul;
 use style_traits::{HasViewportPercentage, ToCss, ParseError, StyleParseError};
 use style_traits::values::specified::{AllowedLengthType, AllowedNumericType};
 use stylesheets::CssRuleType;
 use super::{AllowQuirks, Number, ToComputedValue};
 use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, None_, Normal};
 use values::ExtremumLength;
-use values::computed::{ComputedValueAsSpecified, Context};
+use values::computed::{self, Context};
 use values::specified::calc::CalcNode;
 
 pub use values::specified::calc::CalcLengthOrPercentage;
 pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use super::image::{GradientKind, Image};
 
 /// Number of app units per pixel
 pub const AU_PER_PX: CSSFloat = 60.;
@@ -696,90 +696,139 @@ impl<T: Parse> Either<Length, T> {
         if let Ok(v) = input.try(|input| T::parse(context, input)) {
             return Ok(Either::Second(v));
         }
         Length::parse_internal(context, input, AllowedLengthType::NonNegative, AllowQuirks::No).map(Either::First)
     }
 }
 
 /// A percentage value.
-///
-/// [0 .. 100%] maps to [0.0 .. 1.0]
-///
-/// FIXME(emilio): There's no standard property that requires a `<percentage>`
-/// without requiring also a `<length>`. If such a property existed, we'd need
-/// to add special handling for `calc()` and percentages in here in the same way
-/// as for `Angle` and `Time`, but the lack of this this is otherwise
-/// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
-///
-/// As of today, only `-moz-image-rect` supports percentages without length.
-/// This is not a regression, and that's a non-standard extension anyway, so I'm
-/// not implementing it for now.
-#[derive(Clone, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
-pub struct Percentage(pub CSSFloat);
+#[derive(Clone, Copy, Debug, Default, PartialEq)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Percentage {
+    /// The percentage value as a float.
+    ///
+    /// [0 .. 100%] maps to [0.0 .. 1.0]
+    value: CSSFloat,
+    /// If this percentage came from a calc() expression, this tells how
+    /// clamping should be done on the value.
+    calc_clamping_mode: Option<AllowedNumericType>,
+}
+
+no_viewport_percentage!(Percentage);
 
 impl ToCss for Percentage {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
-        write!(dest, "{}%", self.0 * 100.)
+        if self.calc_clamping_mode.is_some() {
+            dest.write_str("calc(")?;
+        }
+
+        write!(dest, "{}%", self.value * 100.)?;
+
+        if self.calc_clamping_mode.is_some() {
+            dest.write_str(")")?;
+        }
+        Ok(())
     }
 }
 
 impl Percentage {
+    /// Create a percentage from a numeric value.
+    pub fn new(value: CSSFloat) -> Self {
+        Self {
+            value,
+            calc_clamping_mode: None,
+        }
+    }
+
+    /// Get the underlying value for this float.
+    pub fn get(&self) -> CSSFloat {
+        self.calc_clamping_mode.map_or(self.value, |mode| mode.clamp(self.value))
+    }
+
+    /// Reverse this percentage, preserving calc-ness.
+    ///
+    /// For example: If it was 20%, convert it into 80%.
+    pub fn reverse(&mut self) {
+        let new_value = 1. - self.value;
+        self.value = new_value;
+    }
+
+
     /// Parse a specific kind of percentage.
-    pub fn parse_with_clamping_mode<'i, 't>(context: &ParserContext,
-                                            input: &mut Parser<'i, 't>,
-                                            num_context: AllowedNumericType)
-                                            -> Result<Self, ParseError<'i>> {
+    pub fn parse_with_clamping_mode<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+        num_context: AllowedNumericType,
+    ) -> Result<Self, ParseError<'i>> {
         match input.next()? {
             Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
-                Ok(Percentage(unit_value))
+                Ok(Percentage::new(unit_value))
+            }
+            Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
+                let result = input.parse_nested_block(|i| {
+                    CalcNode::parse_percentage(context, i)
+                })?;
+
+                // TODO(emilio): -moz-image-rect is the only thing that uses
+                // the clamping mode... I guess we could disallow it...
+                Ok(Percentage {
+                    value: result,
+                    calc_clamping_mode: Some(num_context),
+                })
             }
             t => Err(BasicParseError::UnexpectedToken(t).into())
         }
     }
 
     /// Parses a percentage token, but rejects it if it's negative.
     pub fn parse_non_negative<'i, 't>(context: &ParserContext,
                                       input: &mut Parser<'i, 't>)
                                       -> Result<Self, ParseError<'i>> {
         Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
     }
 
     /// 0%
     #[inline]
     pub fn zero() -> Self {
-        Percentage(0.)
+        Percentage {
+            value: 0.,
+            calc_clamping_mode: None,
+        }
     }
 
     /// 100%
     #[inline]
     pub fn hundred() -> Self {
-        Percentage(1.)
+        Percentage {
+            value: 1.,
+            calc_clamping_mode: None,
+        }
     }
 }
 
 impl Parse for Percentage {
     #[inline]
-    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
         Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
     }
 }
 
-impl ComputedValueAsSpecified for Percentage {}
-
 /// A length or a percentage value.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum LengthOrPercentage {
     Length(NoCalcLength),
-    Percentage(Percentage),
+    Percentage(computed::Percentage),
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<Length> for LengthOrPercentage {
     fn from(len: Length) -> LengthOrPercentage {
         match len {
             Length::NoCalc(l) => LengthOrPercentage::Length(l),
             Length::Calc(l) => LengthOrPercentage::Calc(l),
@@ -792,16 +841,30 @@ impl From<NoCalcLength> for LengthOrPerc
     fn from(len: NoCalcLength) -> Self {
         LengthOrPercentage::Length(len)
     }
 }
 
 impl From<Percentage> for LengthOrPercentage {
     #[inline]
     fn from(pc: Percentage) -> Self {
+        if pc.calc_clamping_mode.is_some() {
+            LengthOrPercentage::Calc(Box::new(CalcLengthOrPercentage {
+                percentage: Some(computed::Percentage(pc.get())),
+                .. Default::default()
+            }))
+        } else {
+            LengthOrPercentage::Percentage(computed::Percentage(pc.get()))
+        }
+    }
+}
+
+impl From<computed::Percentage> for LengthOrPercentage {
+    #[inline]
+    fn from(pc: computed::Percentage) -> Self {
         LengthOrPercentage::Percentage(pc)
     }
 }
 
 impl LengthOrPercentage {
     #[inline]
     /// Returns a `zero` length.
     pub fn zero() -> LengthOrPercentage {
@@ -815,17 +878,17 @@ impl LengthOrPercentage {
                               -> Result<LengthOrPercentage, ParseError<'i>>
     {
         let token = input.next()?;
         match token {
             Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                 NoCalcLength::parse_dimension(context, value, unit).map(LengthOrPercentage::Length)
             }
             Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
-                return Ok(LengthOrPercentage::Percentage(Percentage(unit_value)))
+                return Ok(LengthOrPercentage::Percentage(computed::Percentage(unit_value)))
             }
             Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                 if value != 0. &&
                    !context.parsing_mode.allows_unitless_lengths() &&
                    !allow_quirks.allowed(context.quirks_mode) {
                     Err(())
                 } else {
                     return Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value)))
@@ -920,48 +983,48 @@ impl LengthOrPercentage {
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `auto` keyword.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum LengthOrPercentageOrAuto {
     Length(NoCalcLength),
-    Percentage(Percentage),
+    Percentage(computed::Percentage),
     Auto,
     Calc(Box<CalcLengthOrPercentage>),
 }
 
 impl From<NoCalcLength> for LengthOrPercentageOrAuto {
     #[inline]
     fn from(len: NoCalcLength) -> Self {
         LengthOrPercentageOrAuto::Length(len)
     }
 }
 
-impl From<Percentage> for LengthOrPercentageOrAuto {
+impl From<computed::Percentage> for LengthOrPercentageOrAuto {
     #[inline]
-    fn from(pc: Percentage) -> Self {
+    fn from(pc: computed::Percentage) -> Self {
         LengthOrPercentageOrAuto::Percentage(pc)
     }
 }
 
 impl LengthOrPercentageOrAuto {
     fn parse_internal<'i, 't>(context: &ParserContext,
                               input: &mut Parser<'i, 't>,
                               num_context: AllowedLengthType,
                               allow_quirks: AllowQuirks)
                               -> Result<Self, ParseError<'i>> {
         let token = input.next()?;
         match token {
             Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                 NoCalcLength::parse_dimension(context, value, unit).map(LengthOrPercentageOrAuto::Length)
             }
             Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
-                Ok(LengthOrPercentageOrAuto::Percentage(Percentage(unit_value)))
+                Ok(LengthOrPercentageOrAuto::Percentage(computed::Percentage(unit_value)))
             }
             Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                 if value != 0. &&
                    !context.parsing_mode.allows_unitless_lengths() &&
                    !allow_quirks.allowed(context.quirks_mode) {
                     return Err(StyleParseError::UnspecifiedError.into())
                 }
                 Ok(LengthOrPercentageOrAuto::Length(
@@ -1004,17 +1067,17 @@ impl LengthOrPercentageOrAuto {
 
     /// Returns a value representing a `0` length.
     pub fn zero() -> Self {
         LengthOrPercentageOrAuto::Length(NoCalcLength::zero())
     }
 
     /// Returns a value representing `0%`.
     pub fn zero_percent() -> Self {
-        LengthOrPercentageOrAuto::Percentage(Percentage::zero())
+        LengthOrPercentageOrAuto::Percentage(computed::Percentage::zero())
     }
 }
 
 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)
     }
@@ -1032,17 +1095,17 @@ impl LengthOrPercentageOrAuto {
 }
 
 /// Either a `<length>`, a `<percentage>`, or the `none` keyword.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 #[allow(missing_docs)]
 pub enum LengthOrPercentageOrNone {
     Length(NoCalcLength),
-    Percentage(Percentage),
+    Percentage(computed::Percentage),
     Calc(Box<CalcLengthOrPercentage>),
     None,
 }
 
 impl LengthOrPercentageOrNone {
     fn parse_internal<'i, 't>(context: &ParserContext,
                               input: &mut Parser<'i, 't>,
                               num_context: AllowedLengthType,
@@ -1050,17 +1113,17 @@ impl LengthOrPercentageOrNone {
                               -> Result<LengthOrPercentageOrNone, ParseError<'i>>
     {
         let token = input.next()?;
         match token {
             Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                 NoCalcLength::parse_dimension(context, value, unit).map(LengthOrPercentageOrNone::Length)
             }
             Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
-                Ok(LengthOrPercentageOrNone::Percentage(Percentage(unit_value)))
+                Ok(LengthOrPercentageOrNone::Percentage(computed::Percentage(unit_value)))
             }
             Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                 if value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
                    !allow_quirks.allowed(context.quirks_mode) {
                     return Err(StyleParseError::UnspecifiedError.into())
                 }
                 Ok(LengthOrPercentageOrNone::Length(
                     NoCalcLength::Absolute(AbsoluteLength::Px(value))
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -499,33 +499,37 @@ impl ToCss for Number {
         self.value.to_css(dest)?;
         if self.calc_clamping_mode.is_some() {
             dest.write_str(")")?;
         }
         Ok(())
     }
 }
 
-/// <number-percentage>
+/// <number> | <percentage>
+///
 /// Accepts only non-negative numbers.
+///
+/// FIXME(emilio): Should probably use Either.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 no_viewport_percentage!(NumberOrPercentage);
 
 impl NumberOrPercentage {
-    fn parse_with_clamping_mode<'i, 't>(context: &ParserContext,
-                                        input: &mut Parser<'i, 't>,
-                                        type_: AllowedNumericType)
-                                        -> Result<Self, ParseError<'i>> {
+    fn parse_with_clamping_mode<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+        type_: AllowedNumericType
+    ) -> Result<Self, ParseError<'i>> {
         if let Ok(per) = input.try(|i| Percentage::parse_with_clamping_mode(context, i, type_)) {
             return Ok(NumberOrPercentage::Percentage(per));
         }
 
         parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
     }
 
     /// Parse a non-negative number or percentage.
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -7,19 +7,19 @@
 //!
 //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
 use style_traits::{HasViewportPercentage, ToCss, ParseError};
 use values::computed::{CalcLengthOrPercentage, LengthOrPercentage as ComputedLengthOrPercentage};
-use values::computed::{Context, ToComputedValue};
+use values::computed::{Context, Percentage, ToComputedValue};
 use values::generics::position::Position as GenericPosition;
-use values::specified::{AllowQuirks, LengthOrPercentage, Percentage};
+use values::specified::{AllowQuirks, LengthOrPercentage};
 use values::specified::transform::OriginComponent;
 
 /// The specified value of a CSS `<position>`
 pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
 
 /// The specified value of a horizontal position.
 pub type HorizontalPosition = PositionComponent<X>;
 
@@ -190,17 +190,17 @@ impl<S: Parse> PositionComponent<S> {
         let lop = input.try(|i| LengthOrPercentage::parse_quirky(context, i, allow_quirks)).ok();
         Ok(PositionComponent::Side(keyword, lop))
     }
 }
 
 impl<S> PositionComponent<S> {
     /// `0%`
     pub fn zero() -> Self {
-        PositionComponent::Length(LengthOrPercentage::Percentage(Percentage(0.)))
+        PositionComponent::Length(LengthOrPercentage::Percentage(Percentage::zero()))
     }
 }
 
 impl<S: Side> ToComputedValue for PositionComponent<S> {
     type ComputedValue = ComputedLengthOrPercentage;
 
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
@@ -209,17 +209,18 @@ impl<S: Side> ToComputedValue for Positi
             },
             PositionComponent::Side(ref keyword, None) => {
                 let p = Percentage(if keyword.is_start() { 0. } else { 1. });
                 ComputedLengthOrPercentage::Percentage(p)
             },
             PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
                 match length.to_computed_value(context) {
                     ComputedLengthOrPercentage::Length(length) => {
-                        ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-length, Some(Percentage(1.0))))
+                        ComputedLengthOrPercentage::Calc(
+                            CalcLengthOrPercentage::new(-length, Some(Percentage::hundred())))
                     },
                     ComputedLengthOrPercentage::Percentage(p) => {
                         ComputedLengthOrPercentage::Percentage(Percentage(1.0 - p.0))
                     },
                     ComputedLengthOrPercentage::Calc(calc) => {
                         let p = Percentage(1. - calc.percentage.map_or(0., |p| p.0));
                         ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-calc.unclamped_length(), Some(p)))
                     },
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -4,22 +4,23 @@
 
 //! Specified types for CSS values that are related to transformations.
 
 use cssparser::Parser;
 use euclid::Point2D;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseError;
 use style_traits::{ParseError, StyleParseError};
-use values::computed::{LengthOrPercentage as ComputedLengthOrPercentage, Context, ToComputedValue};
+use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage};
+use values::computed::{Percentage as ComputedPercentage, ToComputedValue};
 use values::computed::transform::TimingFunction as ComputedTimingFunction;
 use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction};
 use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin};
 use values::specified::{Integer, Number};
-use values::specified::length::{Length, LengthOrPercentage, Percentage};
+use values::specified::length::{Length, LengthOrPercentage};
 use values::specified::position::{Side, X, Y};
 
 /// The specified value of a CSS `<transform-origin>`
 pub type TransformOrigin = GenericTransformOrigin<OriginComponent<X>, OriginComponent<Y>, Length>;
 
 /// The specified value of a component of a CSS `<transform-origin>`.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
@@ -102,37 +103,37 @@ impl<S> Parse for OriginComponent<S>
 impl<S> ToComputedValue for OriginComponent<S>
     where S: Side,
 {
     type ComputedValue = ComputedLengthOrPercentage;
 
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
             OriginComponent::Center => {
-                ComputedLengthOrPercentage::Percentage(Percentage(0.5))
+                ComputedLengthOrPercentage::Percentage(ComputedPercentage(0.5))
             },
             OriginComponent::Length(ref length) => {
                 length.to_computed_value(context)
             },
             OriginComponent::Side(ref keyword) => {
-                let p = Percentage(if keyword.is_start() { 0. } else { 1. });
+                let p = ComputedPercentage(if keyword.is_start() { 0. } else { 1. });
                 ComputedLengthOrPercentage::Percentage(p)
             },
         }
     }
 
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         OriginComponent::Length(ToComputedValue::from_computed_value(computed))
     }
 }
 
 impl<S> OriginComponent<S> {
     /// `0%`
     pub fn zero() -> Self {
-        OriginComponent::Length(LengthOrPercentage::Percentage(Percentage(0.)))
+        OriginComponent::Length(LengthOrPercentage::Percentage(ComputedPercentage::zero()))
     }
 }
 
 #[cfg(feature = "gecko")]
 #[inline]
 fn allow_frames_timing() -> bool {
     use gecko_bindings::bindings;
     unsafe { bindings::Gecko_IsFramesTimingEnabled() }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -2478,18 +2478,19 @@ pub extern "C" fn Servo_DeclarationBlock
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(declarations:
                                                          RawServoDeclarationBlockBorrowed,
                                                          property: nsCSSPropertyID,
                                                          value: f32) {
     use style::properties::{PropertyDeclaration, LonghandId};
     use style::properties::longhands::height::SpecifiedValue as Height;
     use style::properties::longhands::width::SpecifiedValue as Width;
+    use style::values::computed::Percentage;
     use style::values::specified::MozLength;
-    use style::values::specified::length::{LengthOrPercentage, Percentage};
+    use style::values::specified::length::LengthOrPercentage;
 
     let long = get_longhand_from_id!(property);
     let pc = Percentage(value);
 
     let prop = match_wrap_declared! { long,
         Height => Height(MozLength::LengthOrPercentageOrAuto(pc.into())),
         Width => Width(MozLength::LengthOrPercentageOrAuto(pc.into())),
         MarginTop => pc.into(),
--- a/servo/tests/unit/style/animated_properties.rs
+++ b/servo/tests/unit/style/animated_properties.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use cssparser::RGBA;
 use style::properties::animated_properties::{Animatable, IntermediateRGBA};
 use style::properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use style::properties::longhands::transform::computed_value::T as TransformList;
 use style::values::animated::ToAnimatedValue;
-use style::values::specified::length::Percentage;
+use style::values::computed::length::Percentage;
 
 fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
     let from = from.to_animated_value();
     let to = to.to_animated_value();
     RGBA::from_animated_value(from.interpolate(&to, progress).unwrap())
 }
 
 // Color
--- a/servo/tests/unit/style/attr.rs
+++ b/servo/tests/unit/style/attr.rs
@@ -1,16 +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/. */
 
 use app_units::Au;
 use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_length};
 use style::values::computed::CalcLengthOrPercentage;
-use style::values::specified::length::Percentage;
+use style::values::computed::length::Percentage;
 
 #[test]
 fn test_length_calc() {
     let calc = CalcLengthOrPercentage::new(Au(10), Some(Percentage(0.2)));
     assert_eq!(calc.to_used_value(Some(Au(10))), Some(Au(12)));
     assert_eq!(calc.to_used_value(Some(Au(0))), Some(Au(10)));
     assert_eq!(calc.to_used_value(None), None);
 
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -353,26 +353,26 @@ mod shorthand_serialization {
 
         use style::values::specified::BorderCornerRadius;
         use style::values::specified::length::Percentage;
 
         #[test]
         fn border_radius_should_serialize_correctly() {
             let mut properties = Vec::new();
             properties.push(PropertyDeclaration::BorderTopLeftRadius(Box::new(BorderCornerRadius::new(
-                Percentage(0.01).into(), Percentage(0.05).into()
+                Percentage::new(0.01).into(), Percentage::new(0.05).into()
             ))));
             properties.push(PropertyDeclaration::BorderTopRightRadius(Box::new(BorderCornerRadius::new(
-                Percentage(0.02).into(), Percentage(0.06).into()
+                Percentage::new(0.02).into(), Percentage::new(0.06).into()
             ))));
             properties.push(PropertyDeclaration::BorderBottomRightRadius(Box::new(BorderCornerRadius::new(
-                Percentage(0.03).into(), Percentage(0.07).into()
+                Percentage::new(0.03).into(), Percentage::new(0.07).into()
             ))));
             properties.push(PropertyDeclaration::BorderBottomLeftRadius(Box::new(BorderCornerRadius::new(
-                Percentage(0.04).into(), Percentage(0.08).into()
+                Percentage::new(0.04).into(), Percentage::new(0.08).into()
             ))));
 
             let serialization = shorthand_properties_to_string(properties);
             assert_eq!(serialization, "border-radius: 1% 2% 3% 4% / 5% 6% 7% 8%;");
         }
     }
 
 
@@ -558,17 +558,17 @@ mod shorthand_serialization {
     fn flex_should_serialize_all_available_properties() {
         use style::values::specified::{Number, Percentage};
 
         let mut properties = Vec::new();
 
         let grow = Number::new(2f32);
         let shrink = Number::new(3f32);
         let basis =
-            FlexBasis::Length(Percentage(0.5f32).into());
+            FlexBasis::Length(Percentage::new(0.5f32).into());
 
         properties.push(PropertyDeclaration::FlexGrow(grow));
         properties.push(PropertyDeclaration::FlexShrink(shrink));
         properties.push(PropertyDeclaration::FlexBasis(basis));
 
         let serialization = shorthand_properties_to_string(properties);
         assert_eq!(serialization, "flex: 2 3 50%;");
     }
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -21,17 +21,18 @@ use style::properties::{CSSWideKeyword, 
 use style::properties::longhands;
 use style::properties::longhands::animation_play_state;
 use style::shared_lock::SharedRwLock;
 use style::stylearc::Arc;
 use style::stylesheets::{Origin, Namespaces};
 use style::stylesheets::{Stylesheet, StylesheetContents, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage};
 use style::values::{KeyframesName, CustomIdent};
-use style::values::specified::{LengthOrPercentageOrAuto, Percentage, PositionComponent};
+use style::values::computed::Percentage;
+use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent};
 
 pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
 where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
     let mut block = PropertyDeclarationBlock::new();
     for (d, i) in iterable {
         block.push(d, i)
     }
     block