Bug 1374233 - Part 3: Use NonNegativeAu as computed values for font-size related properties. draft
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 21 Jul 2017 15:38:55 +0800
changeset 614994 0f88e920e67170831229b6f4da8b80b65294a4dc
parent 614993 ee8ce69f73d524b6ae6004728bc209631353d2cc
child 614995 88d1a5c41b977f7e0b99b53f72b9ae1e9be517d3
push id70205
push userbmo:boris.chiou@gmail.com
push dateTue, 25 Jul 2017 08:53:17 +0000
bugs1374233
milestone56.0a1
Bug 1374233 - Part 3: Use NonNegativeAu as computed values for font-size related properties. For font-size and font-size-adjust. MozReview-Commit-ID: 5rrfVSzB7WF
servo/components/style/gecko/media_queries.rs
servo/components/style/matching.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values/computed/length.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/length.rs
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -62,17 +62,18 @@ unsafe impl Send for Device {}
 impl Device {
     /// Trivially constructs a new `Device`.
     pub fn new(pres_context: RawGeckoPresContextOwned) -> Self {
         assert!(!pres_context.is_null());
         Device {
             pres_context: pres_context,
             default_values: ComputedValues::default_values(unsafe { &*pres_context }),
             viewport_override: None,
-            root_font_size: AtomicIsize::new(font_size::get_initial_value().0 as isize), // FIXME(bz): Seems dubious?
+            // FIXME(bz): Seems dubious?
+            root_font_size: AtomicIsize::new(font_size::get_initial_value().value() as isize),
             used_root_font_size: AtomicBool::new(false),
         }
     }
 
     /// Tells the device that a new viewport rule has been found, and stores the
     /// relevant viewport constraints.
     pub fn account_for_viewport_rule(&mut self,
                                      constraints: &ViewportConstraints) {
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -483,17 +483,17 @@ pub trait MatchMethods : TElement {
 
         let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
         if self.is_root() && !self.is_native_anonymous() {
             let device = context.shared.stylist.device();
             let new_font_size = new_primary_style.get_font().clone_font_size();
 
             if old_styles.primary.as_ref().map_or(true, |s| s.get_font().clone_font_size() != new_font_size) {
                 debug_assert!(self.owner_doc_matches_for_testing(device));
-                device.set_root_font_size(new_font_size);
+                device.set_root_font_size(new_font_size.0);
                 // If the root font-size changed since last time, and something
                 // in the document did use rem units, ensure we recascade the
                 // entire tree.
                 if device.used_root_font_size() {
                     cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants;
                 }
             }
         }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -56,17 +56,17 @@ use properties::computed_value_flags::Co
 use properties::{longhands, FontComputationData, Importance, LonghandId};
 use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
 use rule_tree::StrongRuleNode;
 use selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem::{forget, uninitialized, transmute, zeroed};
 use std::{cmp, ops, ptr};
 use values::{Auto, CustomIdent, Either, KeyframesName};
-use values::computed::ToComputedValue;
+use values::computed::{NonNegativeAu, ToComputedValue};
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 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
@@ -685,17 +685,16 @@ def set_gecko_property(ffi_name, expr):
         % else:
         self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
         % endif
     }
 
 %if need_clone:
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
-        use values::computed::NonNegativeAu;
         NonNegativeAu(Au(self.gecko.${gecko_ffi_name}))
     }
 % endif
 </%def>
 
 <%def name="impl_split_style_coord(ident, gecko_ffi_name, index, need_clone=False)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
@@ -1856,46 +1855,46 @@ fn static_assert() {
             }).collect()
         )
     }
 
     // FIXME(bholley): Gecko has two different sizes, one of which (mSize) is the
     // actual computed size, and the other of which (mFont.size) is the 'display
     // size' which takes font zooming into account. We don't handle font zooming yet.
     pub fn set_font_size(&mut self, v: longhands::font_size::computed_value::T) {
-        self.gecko.mSize = v.0;
-        self.gecko.mScriptUnconstrainedSize = v.0;
+        self.gecko.mSize = v.value();
+        self.gecko.mScriptUnconstrainedSize = v.value();
     }
 
     /// Set font size, taking into account scriptminsize and scriptlevel
     /// Returns Some(size) if we have to recompute the script unconstrained size
     pub fn apply_font_size(&mut self, v: longhands::font_size::computed_value::T,
                            parent: &Self,
-                           device: &Device) -> Option<Au> {
+                           device: &Device) -> Option<NonNegativeAu> {
         let (adjusted_size, adjusted_unconstrained_size)
             = self.calculate_script_level_size(parent);
         // In this case, we have been unaffected by scriptminsize, ignore it
         if parent.gecko.mSize == parent.gecko.mScriptUnconstrainedSize &&
            adjusted_size == adjusted_unconstrained_size {
             self.set_font_size(v);
             self.fixup_font_min_size(device);
             None
         } else {
-            self.gecko.mSize = v.0;
+            self.gecko.mSize = v.value();
             self.fixup_font_min_size(device);
-            Some(Au(parent.gecko.mScriptUnconstrainedSize))
+            Some(NonNegativeAu(Au(parent.gecko.mScriptUnconstrainedSize)))
         }
     }
 
     pub fn fixup_font_min_size(&mut self, device: &Device) {
         unsafe { bindings::Gecko_nsStyleFont_FixupMinFontSize(&mut self.gecko, device.pres_context()) }
     }
 
-    pub fn apply_unconstrained_font_size(&mut self, v: Au) {
-        self.gecko.mScriptUnconstrainedSize = v.0;
+    pub fn apply_unconstrained_font_size(&mut self, v: NonNegativeAu) {
+        self.gecko.mScriptUnconstrainedSize = v.value();
     }
 
     /// Calculates the constrained and unconstrained font sizes to be inherited
     /// from the parent.
     ///
     /// See ComputeScriptLevelSize in Gecko's nsRuleNode.cpp
     ///
     /// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is
@@ -1992,17 +1991,17 @@ fn static_assert() {
     }
 
     /// This function will also handle scriptminsize and scriptlevel
     /// so should not be called when you just want the font sizes to be copied.
     /// Hence the different name.
     ///
     /// Returns true if the inherited keyword size was actually used
     pub fn inherit_font_size_from(&mut self, parent: &Self,
-                                  kw_inherited_size: Option<Au>,
+                                  kw_inherited_size: Option<NonNegativeAu>,
                                   device: &Device) -> bool {
         let (adjusted_size, adjusted_unconstrained_size)
             = self.calculate_script_level_size(parent);
         if adjusted_size.0 != parent.gecko.mSize ||
            adjusted_unconstrained_size.0 != parent.gecko.mScriptUnconstrainedSize {
             // This is incorrect. When there is both a keyword size being inherited
             // and a scriptlevel change, we must handle the keyword size the same
             // way we handle em units. This complicates things because we now have
@@ -2018,33 +2017,33 @@ fn static_assert() {
             // In the case that MathML has given us an adjusted size, apply it.
             // Keep track of the unconstrained adjusted size.
             self.gecko.mSize = adjusted_size.0;
             self.gecko.mScriptUnconstrainedSize = adjusted_unconstrained_size.0;
             self.fixup_font_min_size(device);
             false
         } else if let Some(size) = kw_inherited_size {
             // Parent element was a keyword-derived size.
-            self.gecko.mSize = size.0;
+            self.gecko.mSize = size.value();
             // MathML constraints didn't apply here, so we can ignore this.
-            self.gecko.mScriptUnconstrainedSize = size.0;
+            self.gecko.mScriptUnconstrainedSize = size.value();
             self.fixup_font_min_size(device);
             true
         } else {
             // MathML isn't affecting us, and our parent element does not
             // have a keyword-derived size. Set things normally.
             self.gecko.mSize = parent.gecko.mSize;
             self.gecko.mScriptUnconstrainedSize = parent.gecko.mScriptUnconstrainedSize;
             self.fixup_font_min_size(device);
             false
         }
     }
 
     pub fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
-        Au(self.gecko.mSize)
+        NonNegativeAu(Au(self.gecko.mSize))
     }
 
     pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) {
         self.gecko.mFont.weight = v.0;
     }
     ${impl_simple_copy('font_weight', 'mFont.weight')}
 
     pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -12,16 +12,17 @@ use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
 #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
+use properties::longhands::font_size_adjust::computed_value::T as FontSizeAdjust;
 use properties::longhands::font_stretch::computed_value::T as FontStretch;
 use properties::longhands::transform::computed_value::ComputedMatrix;
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
 use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
 use properties::longhands::visibility::computed_value::T as Visibility;
 #[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
 #[cfg(feature = "gecko")] use properties::{ShorthandId};
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -557,24 +557,25 @@ macro_rules! impl_gecko_keyword_conversi
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue::Weight(*computed)
         }
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="font-size" need_clone="True" animation_value_type="ComputedValue"
+<%helpers:longhand name="font-size" need_clone="True" animation_value_type="NonNegativeAu"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size">
     use app_units::Au;
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::{HasViewportPercentage, ToCss};
     use values::FONT_MEDIUM_PX;
+    use values::computed::NonNegativeAu;
     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),
@@ -612,18 +613,18 @@ macro_rules! impl_gecko_keyword_conversi
 
     impl From<specified::LengthOrPercentage> for SpecifiedValue {
         fn from(other: specified::LengthOrPercentage) -> Self {
             SpecifiedValue::Length(other)
         }
     }
 
     pub mod computed_value {
-        use app_units::Au;
-        pub type T = Au;
+        use values::computed::NonNegativeAu;
+        pub type T = NonNegativeAu;
     }
 
     /// CSS font keywords
     #[derive(Debug, Copy, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum KeywordSize {
         XXSmall = 1, // This is to enable the NonZero optimization
                      // which simplifies the representation of Option<KeywordSize>
@@ -688,41 +689,41 @@ macro_rules! impl_gecko_keyword_conversi
                                           specified values set via
                                           HTML presentation attributes"),
             })
         }
     }
 
     % if product == "servo":
         impl ToComputedValue for KeywordSize {
-            type ComputedValue = Au;
+            type ComputedValue = NonNegativeAu;
             #[inline]
             fn to_computed_value(&self, _: &Context) -> computed_value::T {
                 // https://drafts.csswg.org/css-fonts-3/#font-size-prop
                 use values::FONT_MEDIUM_PX;
-                match *self {
+                NonNegativeAu(match *self {
                     XXSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 5,
                     XSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 4,
                     Small => Au::from_px(FONT_MEDIUM_PX) * 8 / 9,
                     Medium => Au::from_px(FONT_MEDIUM_PX),
                     Large => Au::from_px(FONT_MEDIUM_PX) * 6 / 5,
                     XLarge => Au::from_px(FONT_MEDIUM_PX) * 3 / 2,
                     XXLarge => Au::from_px(FONT_MEDIUM_PX) * 2,
                     XXXLarge => Au::from_px(FONT_MEDIUM_PX) * 3,
-                }
+                })
             }
 
             #[inline]
             fn from_computed_value(_: &computed_value::T) -> Self {
                 unreachable!()
             }
         }
     % else:
         impl ToComputedValue for KeywordSize {
-            type ComputedValue = Au;
+            type ComputedValue = NonNegativeAu;
             #[inline]
             fn to_computed_value(&self, cx: &Context) -> computed_value::T {
                 use gecko_bindings::structs::nsIAtom;
                 use values::specified::length::au_to_int_px;
                 // Data from nsRuleNode.cpp in Gecko
                 // Mapping from base size and HTML size to pixels
                 // The first index is (base_size - 9), the second is the
                 // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
@@ -748,19 +749,19 @@ macro_rules! impl_gecko_keyword_conversi
                 let ref gecko_font = cx.style().get_font().gecko();
                 let base_size = unsafe { Atom::with(gecko_font.mLanguage.raw::<nsIAtom>(), |atom| {
                     cx.font_metrics_provider.get_size(atom, gecko_font.mGenericID).0
                 }) };
 
                 let base_size_px = au_to_int_px(base_size as f32);
                 let html_size = self.html_size() as usize;
                 if base_size_px >= 9 && base_size_px <= 16 {
-                    Au::from_px(FONT_SIZE_MAPPING[(base_size_px - 9) as usize][html_size])
+                    NonNegativeAu::from_px(FONT_SIZE_MAPPING[(base_size_px - 9) as usize][html_size])
                 } else {
-                    Au(FONT_SIZE_FACTORS[html_size] * base_size / 100)
+                    NonNegativeAu(Au(FONT_SIZE_FACTORS[html_size] * base_size / 100))
                 }
             }
 
             #[inline]
             fn from_computed_value(_: &computed_value::T) -> Self {
                 unreachable!()
             }
         }
@@ -809,62 +810,63 @@ macro_rules! impl_gecko_keyword_conversi
                 }
                 SpecifiedValue::Larger => Some(LARGER_FONT_SIZE_RATIO),
                 SpecifiedValue::Smaller => Some(1. / LARGER_FONT_SIZE_RATIO),
                 _ => None,
             }
         }
 
         /// Compute it against a given base font size
-        pub fn to_computed_value_against(&self, context: &Context, base_size: FontBaseSize) -> Au {
+        pub fn to_computed_value_against(&self, context: &Context, base_size: FontBaseSize)
+                                         -> NonNegativeAu {
             use values::specified::length::FontRelativeLength;
             match *self {
                 SpecifiedValue::Length(LengthOrPercentage::Length(
                         NoCalcLength::FontRelative(value))) => {
-                    value.to_computed_value(context, base_size)
+                    NonNegativeAu(value.to_computed_value(context, base_size))
                 }
                 SpecifiedValue::Length(LengthOrPercentage::Length(
                         NoCalcLength::ServoCharacterWidth(value))) => {
-                    value.to_computed_value(base_size.resolve(context))
+                    NonNegativeAu(value.to_computed_value(base_size.resolve(context)))
                 }
                 SpecifiedValue::Length(LengthOrPercentage::Length(ref l)) => {
-                    l.to_computed_value(context)
+                    NonNegativeAu(l.to_computed_value(context))
                 }
                 SpecifiedValue::Length(LengthOrPercentage::Percentage(pc)) => {
-                    base_size.resolve(context).scale_by(pc.0)
+                    NonNegativeAu(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()
+                    NonNegativeAu(calc.to_used_value(Some(base_size.resolve(context))).unwrap())
                 }
                 SpecifiedValue::Keyword(ref key, fraction) => {
                     key.to_computed_value(context).scale_by(fraction)
                 }
                 SpecifiedValue::Smaller => {
-                    FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
-                        .to_computed_value(context, base_size)
+                    NonNegativeAu(FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
+                        .to_computed_value(context, base_size))
                 }
                 SpecifiedValue::Larger => {
-                    FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO)
-                        .to_computed_value(context, base_size)
+                    NonNegativeAu(FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO)
+                        .to_computed_value(context, base_size))
                 }
 
                 SpecifiedValue::System(_) => {
                     <%self:nongecko_unreachable>
                         context.cached_system_font.as_ref().unwrap().font_size
                     </%self:nongecko_unreachable>
                 }
             }
         }
     }
 
     #[inline]
     #[allow(missing_docs)]
     pub fn get_initial_value() -> computed_value::T {
-        Au::from_px(FONT_MEDIUM_PX)
+        NonNegativeAu::from_px(FONT_MEDIUM_PX)
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::Keyword(Medium, 1.)
     }
 
 
@@ -874,17 +876,17 @@ macro_rules! impl_gecko_keyword_conversi
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             self.to_computed_value_against(context, FontBaseSize::InheritedStyle)
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue::Length(LengthOrPercentage::Length(
-                ToComputedValue::from_computed_value(computed)
+                ToComputedValue::from_computed_value(&computed.0)
             ))
         }
     }
 
     /// <length> | <percentage> | <absolute-size> | <relative-size>
     pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         parse_quirky(context, input, AllowQuirks::No)
@@ -921,17 +923,17 @@ macro_rules! impl_gecko_keyword_conversi
                 None
             }
         }
     }
 
     #[allow(unused_mut)]
     pub fn cascade_specified_font_size(context: &mut Context,
                                        specified_value: &SpecifiedValue,
-                                       mut computed: Au) {
+                                       mut computed: NonNegativeAu) {
         if let SpecifiedValue::Keyword(kw, fraction) = *specified_value {
             context.builder.font_size_keyword = Some((kw, fraction));
         } else if let Some(ratio) = specified_value.as_font_ratio() {
             // In case a font-size-relative value was applied to a keyword
             // value, we must preserve this fact in case the generic font family
             // changes. relative values (em and %) applied to keywords must be
             // recomputed from the base size for the keyword and the relative size.
             //
@@ -968,17 +970,17 @@ macro_rules! impl_gecko_keyword_conversi
             let parent_font = parent_style.get_font();
             font.apply_font_size(computed, parent_font, device)
         };
         context.builder.put_font(font);
 
         if let Some(parent) = parent_unconstrained {
             let new_unconstrained =
                 specified_value
-                    .to_computed_value_against(context, FontBaseSize::Custom(parent));
+                    .to_computed_value_against(context, FontBaseSize::Custom(parent.0));
             context.builder
                    .mutate_font()
                    .apply_unconstrained_font_size(new_unconstrained);
         }
     }
 
     /// FIXME(emilio): This is very complex. Also, it should move to
     /// StyleBuilder.
@@ -1019,17 +1021,17 @@ macro_rules! impl_gecko_keyword_conversi
         % if product == "gecko":
             let device = context.builder.device;
             context.builder.mutate_font().fixup_font_min_size(device);
         % endif
         context.builder.font_size_keyword = Some((Default::default(), 1.));
     }
 </%helpers:longhand>
 
-<%helpers:longhand products="gecko" name="font-size-adjust" animation_value_type="ComputedValue"
+<%helpers:longhand products="gecko" name="font-size-adjust" animation_value_type="FontSizeAdjust"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
     use properties::longhands::system_font::SystemFont;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -1073,17 +1075,17 @@ macro_rules! impl_gecko_keyword_conversi
                 None
             }
         }
     }
 
     pub mod computed_value {
         use properties::animated_properties::Animatable;
         use values::CSSFloat;
-        use values::animated::ToAnimatedZero;
+        use values::animated::{ToAnimatedValue, ToAnimatedZero};
 
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Copy, Clone, Debug, PartialEq, ToCss)]
         pub enum T {
             None,
             Number(CSSFloat),
         }
 
@@ -1116,16 +1118,33 @@ macro_rules! impl_gecko_keyword_conversi
                 }
             }
         }
 
         impl ToAnimatedZero for T {
             #[inline]
             fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
         }
+
+        impl ToAnimatedValue for T {
+            type AnimatedValue = Self;
+
+            #[inline]
+            fn to_animated_value(self) -> Self {
+                self
+            }
+
+            #[inline]
+            fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+                match animated {
+                    T::Number(number) => T::Number(number.max(0.)),
+                    _ => animated
+                }
+            }
+        }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::None
     }
 
     #[inline]
@@ -2396,17 +2415,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
         //! whenever a font longhand on the same element needs the system font.
 
         use app_units::Au;
         use cssparser::{Parser, ToCss};
         use properties::longhands;
         use std::fmt;
         use std::hash::{Hash, Hasher};
         use style_traits::ParseError;
-        use values::computed::{ToComputedValue, Context};
+        use values::computed::{ToComputedValue, Context, NonNegativeAu};
         <%
             system_fonts = """caption icon menu message-box small-caption status-bar
                               -moz-window -moz-document -moz-workspace -moz-desktop
                               -moz-info -moz-dialog -moz-button -moz-pull-down-menu
                               -moz-list -moz-field""".split()
             kw_font_props = """font_style font_variant_caps font_stretch
                                font_kerning font_variant_position font_variant_ligatures
                                font_variant_east_asian font_variant_numeric""".split()
@@ -2468,17 +2487,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
                     FontFamily::FamilyName(FamilyName {
                         name: (&*font.mName).into(),
                         quoted: true
                     })
                 }).collect::<Vec<_>>();
                 let weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
                 let ret = ComputedSystemFont {
                     font_family: longhands::font_family::computed_value::T(family),
-                    font_size: Au(system.size),
+                    font_size: NonNegativeAu(Au(system.size)),
                     font_weight: weight,
                     font_size_adjust: longhands::font_size_adjust::computed_value
                                                ::T::from_gecko_adjust(system.sizeAdjust),
                     % for kwprop in kw_font_props:
                         ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
                             system.${to_camel_case_lower(kwprop.replace('font_', ''))}
                             % if kwprop in kw_cast:
                                 as u32
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1750,30 +1750,31 @@ pub mod style_structs {
                     hasher.write_u16(self.font_weight.0);
                     self.font_stretch.hash(&mut hasher);
                     self.font_family.hash(&mut hasher);
                     self.hash = hasher.finish()
                 }
 
                 /// (Servo does not handle MathML, so this just calls copy_font_size_from)
                 pub fn inherit_font_size_from(&mut self, parent: &Self,
-                                              _: Option<Au>, _: &Device) -> bool {
+                                              _: Option<computed::NonNegativeAu>,
+                                              _: &Device) -> bool {
                     self.copy_font_size_from(parent);
                     false
                 }
                 /// (Servo does not handle MathML, so this just calls set_font_size)
                 pub fn apply_font_size(&mut self,
                                        v: longhands::font_size::computed_value::T,
                                        _: &Self,
-                                       _: &Device) -> Option<Au> {
+                                       _: &Device) -> Option<computed::NonNegativeAu> {
                     self.set_font_size(v);
                     None
                 }
                 /// (Servo does not handle MathML, so this does nothing)
-                pub fn apply_unconstrained_font_size(&mut self, _: Au) {
+                pub fn apply_unconstrained_font_size(&mut self, _: computed::NonNegativeAu) {
                 }
 
             % 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 != ::app_units::Au(0)
                 }
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -66,17 +66,17 @@ impl ToComputedValue for specified::NoCa
         match *self {
             specified::NoCalcLength::Absolute(length) =>
                 length.to_computed_value(context),
             specified::NoCalcLength::FontRelative(length) =>
                 length.to_computed_value(context, FontBaseSize::CurrentStyle),
             specified::NoCalcLength::ViewportPercentage(length) =>
                 length.to_computed_value(context.viewport_size()),
             specified::NoCalcLength::ServoCharacterWidth(length) =>
-                length.to_computed_value(context.style().get_font().clone_font_size()),
+                length.to_computed_value(context.style().get_font().clone_font_size().0),
             #[cfg(feature = "gecko")]
             specified::NoCalcLength::Physical(length) =>
                 length.to_computed_value(context),
         }
     }
 
     #[inline]
     fn from_computed_value(computed: &Au) -> Self {
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -612,9 +612,15 @@ impl NonNegativeAu {
         (self.0).0
     }
 
     /// Returns the maximum of |self| and |other|.
     #[inline]
     pub fn max(self, other: Self) -> Self {
         NonNegativeAu(Au(::std::cmp::max(self.value(), other.value())))
     }
+
+    /// Scale this NonNegativeAu.
+    #[inline]
+    pub fn scale_by(self, factor: f32) -> Self {
+        NonNegativeAu(self.0.scale_by(factor))
+    }
 }
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -87,18 +87,18 @@ pub enum FontBaseSize {
     Custom(Au),
 }
 
 impl FontBaseSize {
     /// Calculate the actual size for a given context
     pub fn resolve(&self, context: &Context) -> Au {
         match *self {
             FontBaseSize::Custom(size) => size,
-            FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size(),
-            FontBaseSize::InheritedStyle => context.inherited_style().get_font().clone_font_size(),
+            FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().0,
+            FontBaseSize::InheritedStyle => context.inherited_style().get_font().clone_font_size().0,
         }
     }
 }
 
 impl FontRelativeLength {
     /// Computes the font-relative length. We use the base_size
     /// flag to pass a different size for computing font-size and unconstrained font-size
     pub fn to_computed_value(&self, context: &Context, base_size: FontBaseSize) -> Au {