servo: Merge #19119 - style: Move font-size outside of mako (from CYBAI:font-size-out-of-mako); r=emilio
authorCYBAI <cyb.ai.815@gmail.com>
Wed, 08 Nov 2017 04:05:47 -0600
changeset 444037 76ef1d896013fb1097e55d0656f3f5fb3f0e395d
parent 444036 ab41b0060a87d88dce1d3019435788ad2ee8b149
child 444038 06538a745211663edaf19646b989095b36343bfa
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs19119, 19015, 19111
milestone58.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 #19119 - style: Move font-size outside of mako (from CYBAI:font-size-out-of-mako); r=emilio This is a sub-PR of #19015 r? emilio --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #19111 (github issue number if applicable). - [x] These changes do not require tests Source-Repo: https://github.com/servo/servo Source-Revision: fd8b4b59c2a624bb08f7db08b300c50718e1336d
servo/components/style/gecko/media_queries.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
servo/components/style/servo/media_queries.rs
servo/components/style/values/computed/font.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/font.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -15,28 +15,28 @@ use gecko_bindings::bindings;
 use gecko_bindings::structs;
 use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
 use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType};
 use gecko_bindings::structs::{nsPresContext, RawGeckoPresContextOwned};
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::ComputedValues;
-use properties::longhands::font_size;
 use servo_arc::Arc;
 use std::fmt::{self, Write};
 use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
 use str::starts_with_ignore_ascii_case;
 use string_cache::Atom;
 use style_traits::{CSSPixel, DevicePixel};
 use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::viewport::ViewportConstraints;
 use stylesheets::Origin;
 use values::{CSSFloat, CustomIdent, serialize_dimension};
 use values::computed::{self, ToComputedValue};
+use values::computed::font::FontSize;
 use values::specified::Length;
 
 /// The `Device` in Gecko wraps a pres context, has a default values computed,
 /// and contains all the viewport rule state.
 pub struct Device {
     /// NB: The pres context lifetime is tied to the styleset, who owns the
     /// stylist, and thus the `Device`, so having a raw pres context pointer
     /// here is fine.
@@ -70,17 +70,17 @@ 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 }),
             // FIXME(bz): Seems dubious?
-            root_font_size: AtomicIsize::new(font_size::get_initial_value().size().0 as isize),
+            root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
             body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize),
             used_root_font_size: AtomicBool::new(false),
             used_viewport_size: AtomicBool::new(false),
         }
     }
 
     /// Tells the device that a new viewport rule has been found, and stores the
     /// relevant viewport constraints.
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -54,16 +54,17 @@ use properties::{longhands, Importance, 
 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::{self, Auto, CustomIdent, Either, KeyframesName, None_};
 use values::computed::{NonNegativeLength, ToComputedValue, Percentage};
+use values::computed::font::FontSize;
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 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
 }
@@ -2083,17 +2084,17 @@ fn static_assert() {
     }
 
     pub fn unzoom_fonts(&mut self, device: &Device) {
         self.gecko.mSize = device.unzoom_text(Au(self.gecko.mSize)).0;
         self.gecko.mScriptUnconstrainedSize = device.unzoom_text(Au(self.gecko.mScriptUnconstrainedSize)).0;
         self.gecko.mFont.size = device.unzoom_text(Au(self.gecko.mFont.size)).0;
     }
 
-    pub fn set_font_size(&mut self, v: longhands::font_size::computed_value::T) {
+    pub fn set_font_size(&mut self, v: FontSize) {
         use values::specified::font::KeywordSize;
         self.gecko.mSize = v.size().0;
         self.gecko.mScriptUnconstrainedSize = v.size().0;
         if let Some(info) = v.keyword_info {
             self.gecko.mFontSizeKeyword = match info.kw {
                 KeywordSize::XXSmall => structs::NS_STYLE_FONT_SIZE_XXSMALL,
                 KeywordSize::XSmall => structs::NS_STYLE_FONT_SIZE_XSMALL,
                 KeywordSize::Small => structs::NS_STYLE_FONT_SIZE_SMALL,
@@ -2109,17 +2110,17 @@ fn static_assert() {
             self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8;
             self.gecko.mFontSizeFactor = 1.;
             self.gecko.mFontSizeOffset = 0;
         }
     }
 
     /// 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,
+    pub fn apply_font_size(&mut self, v: FontSize,
                            parent: &Self,
                            device: &Device) -> Option<NonNegativeLength> {
         let (adjusted_size, adjusted_unconstrained_size) =
             self.calculate_script_level_size(parent, device);
         // 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);
@@ -2292,38 +2293,38 @@ fn static_assert() {
             self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8;
             self.gecko.mFontSizeFactor = 1.;
             self.gecko.mFontSizeOffset = 0;
             self.gecko.mScriptUnconstrainedSize = parent.gecko.mScriptUnconstrainedSize;
         }
         self.fixup_font_min_size(device);
     }
 
-    pub fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
+    pub fn clone_font_size(&self) -> FontSize {
         use values::computed::font::KeywordInfo;
         use values::specified::font::KeywordSize;
         let size = Au(self.gecko.mSize).into();
         let kw = match self.gecko.mFontSizeKeyword as u32 {
             structs::NS_STYLE_FONT_SIZE_XXSMALL => KeywordSize::XXSmall,
             structs::NS_STYLE_FONT_SIZE_XSMALL => KeywordSize::XSmall,
             structs::NS_STYLE_FONT_SIZE_SMALL => KeywordSize::Small,
             structs::NS_STYLE_FONT_SIZE_MEDIUM => KeywordSize::Medium,
             structs::NS_STYLE_FONT_SIZE_LARGE => KeywordSize::Large,
             structs::NS_STYLE_FONT_SIZE_XLARGE => KeywordSize::XLarge,
             structs::NS_STYLE_FONT_SIZE_XXLARGE => KeywordSize::XXLarge,
             structs::NS_STYLE_FONT_SIZE_XXXLARGE => KeywordSize::XXXLarge,
             structs::NS_STYLE_FONT_SIZE_NO_KEYWORD => {
-                return longhands::font_size::computed_value::T {
+                return FontSize {
                     size: size,
                     keyword_info: None,
                 }
             }
             _ => unreachable!("mFontSizeKeyword should be an absolute keyword or NO_KEYWORD")
         };
-        longhands::font_size::computed_value::T {
+        FontSize {
             size: size,
             keyword_info: Some(KeywordInfo {
                 kw: kw,
                 factor: self.gecko.mFontSizeFactor,
                 offset: Au(self.gecko.mFontSizeOffset).into()
             })
         }
     }
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -351,47 +351,47 @@
                             context.builder.put_${data.current_style_struct.name_lower}(s);
                         % else:
                             % if property.boxed:
                             let computed = (**specified_value).to_computed_value(context);
                             % else:
                             let computed = specified_value.to_computed_value(context);
                             % endif
                             % if property.ident == "font_size":
-                                 longhands::font_size::cascade_specified_font_size(
+                                 specified::FontSize::cascade_specified_font_size(
                                      context,
                                      &specified_value,
                                      computed,
                                  );
                             % else:
                                 context.builder.set_${property.ident}(computed)
                             % endif
                         % endif
                     }
                     DeclaredValue::WithVariables(_) => unreachable!(),
                     DeclaredValue::CSSWideKeyword(keyword) => match keyword {
                         % if not data.current_style_struct.inherited:
                         CSSWideKeyword::Unset |
                         % endif
                         CSSWideKeyword::Initial => {
                             % if property.ident == "font_size":
-                                longhands::font_size::cascade_initial_font_size(context);
+                                computed::FontSize::cascade_initial_font_size(context);
                             % else:
                                 context.builder.reset_${property.ident}();
                             % endif
                         },
                         % if data.current_style_struct.inherited:
                         CSSWideKeyword::Unset |
                         % endif
                         CSSWideKeyword::Inherit => {
                             % if not property.style_struct.inherited:
                                 context.rule_cache_conditions.borrow_mut().set_uncacheable();
                             % endif
                             % if property.ident == "font_size":
-                                longhands::font_size::cascade_inherit_font_size(context);
+                                computed::FontSize::cascade_inherit_font_size(context);
                             % else:
                                 context.builder.inherit_${property.ident}();
                             % endif
                         }
                     }
                 }
 
                 % if property.custom_cascade:
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -619,147 +619,24 @@ macro_rules! impl_gecko_keyword_conversi
 ${helpers.predefined_type("font-weight",
                           "FontWeight",
                           initial_value="computed::FontWeight::normal()",
                           initial_specified_value="specified::FontWeight::Normal",
                           animation_value_type="ComputedValue",
                           flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight")}
 
-<%helpers:longhand name="font-size" animation_value_type="NonNegativeLength"
-                   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 values::specified::AllowQuirks;
-    use values::specified::length::FontBaseSize;
-    use values::specified::font::{FONT_MEDIUM_PX, KeywordSize};
-    use values::computed::font::{KeywordInfo};
-
-    pub mod computed_value {
-        use values::computed::font;
-        pub type T = font::FontSize;
-    }
-
-    pub use values::specified::font::FontSize as SpecifiedValue;
-
-    #[inline]
-    #[allow(missing_docs)]
-    pub fn get_initial_value() -> computed_value::T {
-        computed_value::T {
-            size: Au::from_px(FONT_MEDIUM_PX).into(),
-            keyword_info: Some(KeywordInfo::medium())
-        }
-    }
-
-    #[inline]
-    pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::Keyword(KeywordInfo::medium())
-    }
-
-
-    /// <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)
-    }
-
-    /// Parses a font-size, with quirks.
-    pub fn parse_quirky<'i, 't>(context: &ParserContext,
-                                input: &mut Parser<'i, 't>,
-                                allow_quirks: AllowQuirks)
-                                -> Result<SpecifiedValue, ParseError<'i>> {
-        use self::specified::LengthOrPercentage;
-        if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative_quirky(context, i, allow_quirks)) {
-            return Ok(SpecifiedValue::Length(lop))
-        }
-
-        if let Ok(kw) = input.try(KeywordSize::parse) {
-            return Ok(SpecifiedValue::Keyword(kw.into()))
-        }
-
-        try_match_ident_ignore_ascii_case! { input,
-            "smaller" => Ok(SpecifiedValue::Smaller),
-            "larger" => Ok(SpecifiedValue::Larger),
-        }
-    }
-
-    #[allow(unused_mut)]
-    pub fn cascade_specified_font_size(context: &mut Context,
-                                       specified_value: &SpecifiedValue,
-                                       mut computed: computed_value::T) {
-        // we could use clone_language and clone_font_family() here but that's
-        // expensive. Do it only in gecko mode for now.
-        % if product == "gecko":
-            // if the language or generic changed, we need to recalculate
-            // the font size from the stored font-size origin information.
-            if context.builder.get_font().gecko().mLanguage.mRawPtr !=
-               context.builder.get_parent_font().gecko().mLanguage.mRawPtr ||
-               context.builder.get_font().gecko().mGenericID !=
-               context.builder.get_parent_font().gecko().mGenericID {
-                if let Some(info) = computed.keyword_info {
-                    computed.size = info.to_computed_value(context);
-                }
-            }
-        % endif
-
-        let device = context.builder.device;
-        let mut font = context.builder.take_font();
-        let parent_unconstrained = {
-            let parent_font = context.builder.get_parent_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(Au::from(parent)));
-            context.builder
-                   .mutate_font()
-                   .apply_unconstrained_font_size(new_unconstrained.size);
-        }
-    }
-
-    /// FIXME(emilio): This is very complex. Also, it should move to
-    /// StyleBuilder.
-    pub fn cascade_inherit_font_size(context: &mut Context) {
-        // If inheriting, we must recompute font-size in case of language
-        // changes using the font_size_keyword. We also need to do this to
-        // handle mathml scriptlevel changes
-        let kw_inherited_size = context.builder.get_parent_font()
-                                       .clone_font_size()
-                                       .keyword_info.map(|info| {
-            SpecifiedValue::Keyword(info).to_computed_value(context).size
-        });
-        let mut font = context.builder.take_font();
-        font.inherit_font_size_from(context.builder.get_parent_font(),
-                                    kw_inherited_size,
-                                    context.builder.device);
-        context.builder.put_font(font);
-    }
-
-    /// Cascade the initial value for the `font-size` property.
-    ///
-    /// FIXME(emilio): This is the only function that is outside of the
-    /// `StyleBuilder`, and should really move inside!
-    ///
-    /// Can we move the font stuff there?
-    pub fn cascade_initial_font_size(context: &mut Context) {
-        // font-size's default ("medium") does not always
-        // compute to the same value and depends on the font
-        let computed =
-            longhands::font_size::get_initial_specified_value()
-                .to_computed_value(context);
-        context.builder.mutate_font().set_font_size(computed);
-        % if product == "gecko":
-            let device = context.builder.device;
-            context.builder.mutate_font().fixup_font_min_size(device);
-        % endif
-    }
-</%helpers:longhand>
+${helpers.predefined_type("font-size",
+                          "FontSize",
+                          initial_value="computed::FontSize::medium()",
+                          initial_specified_value="specified::FontSize::medium()",
+                          animation_value_type="NonNegativeLength",
+                          allow_quirks=True,
+                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
+                          spec="https://drafts.csswg.org/css-fonts/#propdef-font-size")}
 
 <%helpers:longhand products="gecko" name="font-size-adjust"
                    animation_value_type="longhands::font_size_adjust::computed_value::T"
                    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;
 
 
@@ -2046,16 +1923,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
 
         impl ToComputedValue for SystemFont {
             type ComputedValue = ComputedSystemFont;
 
             fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
                 use gecko_bindings::bindings;
                 use gecko_bindings::structs::{LookAndFeel_FontID, nsFont};
                 use std::mem;
+                use values::computed::font::FontSize;
 
                 let id = match *self {
                     % for font in system_fonts:
                         SystemFont::${to_camel_case(font)} => {
                             LookAndFeel_FontID::eFont_${to_camel_case(font.replace("-moz-", ""))}
                         }
                     % endfor
                 };
@@ -2071,17 +1949,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
                 }
                 let weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
                 let ret = ComputedSystemFont {
                     font_family: longhands::font_family::computed_value::T(
                         longhands::font_family::computed_value::FontFamilyList(
                             unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
                         )
                     ),
-                    font_size: longhands::font_size::computed_value::T {
+                    font_size: FontSize {
                             size: Au(system.size).into(),
                             keyword_info: None
                     },
                     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(
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -15,20 +15,21 @@
                                     ${'font-variant-ligatures' if product == 'gecko' else ''}
                                     ${'font-variant-numeric' if product == 'gecko' else ''}
                                     ${'font-variant-position' if product == 'gecko' else ''}
                                     ${'font-language-override' if product == 'gecko' else ''}
                                     ${'font-feature-settings' if product == 'gecko' else ''}"
                     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
     use parser::Parse;
     use properties::longhands::{font_family, font_style, font_weight, font_stretch};
-    use properties::longhands::{font_size, font_variant_caps};
+    use properties::longhands::font_variant_caps;
     #[cfg(feature = "gecko")]
     use properties::longhands::system_font::SystemFont;
     use values::specified::text::LineHeight;
+    use values::specified::FontSize;
 
     <%
         gecko_sub_properties = "kerning language_override size_adjust \
                                 variant_alternates variant_east_asian \
                                 variant_ligatures variant_numeric \
                                 variant_position feature_settings".split()
     %>
     % if product == "gecko":
@@ -45,17 +46,21 @@
         let mut variant_caps = None;
         let mut weight = None;
         let mut stretch = None;
         let size;
         % if product == "gecko":
             if let Ok(sys) = input.try(SystemFont::parse) {
                 return Ok(expanded! {
                      % for name in SYSTEM_FONT_LONGHANDS:
-                         ${name}: ${name}::SpecifiedValue::system_font(sys),
+                         % if name == "font_size":
+                             ${name}: FontSize::system_font(sys),
+                         % else:
+                             ${name}: ${name}::SpecifiedValue::system_font(sys),
+                         % endif
                      % endfor
                      // line-height is just reset to initial
                      line_height: LineHeight::normal(),
                  })
             }
         % endif
         loop {
             // Special-case 'normal' because it is valid in each of
@@ -84,37 +89,48 @@
                 }
             }
             if stretch.is_none() {
                 if let Ok(value) = input.try(|input| font_stretch::parse(context, input)) {
                     stretch = Some(value);
                     continue
                 }
             }
-            size = Some(font_size::parse(context, input)?);
+            size = Some(FontSize::parse(context, input)?);
             break
         }
-        #[inline]
-        fn count<T>(opt: &Option<T>) -> u8 {
-            if opt.is_some() { 1 } else { 0 }
-        }
-        if size.is_none() ||
-           (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals) > 4 {
-            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
-        }
+
+        let size = match size {
+            Some(s) => s,
+            None => {
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
+            }
+        };
+
         let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
             Some(LineHeight::parse(context, input)?)
         } else {
             None
         };
+
+        #[inline]
+        fn count<T>(opt: &Option<T>) -> u8 {
+            if opt.is_some() { 1 } else { 0 }
+        }
+
+        if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals) > 4 {
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
+        }
+
         let family = FontFamily::parse(input)?;
         Ok(expanded! {
-            % for name in "style weight stretch size variant_caps".split():
+            % for name in "style weight stretch variant_caps".split():
                 font_${name}: unwrap_or_initial!(font_${name}, ${name}),
             % endfor
+            font_size: size,
             line_height: line_height.unwrap_or(LineHeight::normal()),
             font_family: family,
             % if product == "gecko":
                 % for name in gecko_sub_properties:
                     font_${name}: font_${name}::get_initial_specified_value(),
                 % endfor
             % endif
         })
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -6,23 +6,23 @@
 
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{Parser, RGBA};
 use euclid::{ScaleFactor, Size2D, TypedSize2D};
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::ComputedValues;
-use properties::longhands::font_size;
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt;
 use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
 use style_traits::{CSSPixel, DevicePixel, ToCss, ParseError};
 use style_traits::viewport::ViewportConstraints;
 use values::computed::{self, ToComputedValue};
+use values::computed::font::FontSize;
 use values::specified;
 
 /// A device is a structure that represents the current media a given document
 /// is displayed in.
 ///
 /// This is the struct against which media queries are evaluated.
 #[derive(MallocSizeOf)]
 pub struct Device {
@@ -59,17 +59,17 @@ impl Device {
         viewport_size: TypedSize2D<f32, CSSPixel>,
         device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>
     ) -> Device {
         Device {
             media_type,
             viewport_size,
             device_pixel_ratio,
             // FIXME(bz): Seems dubious?
-            root_font_size: AtomicIsize::new(font_size::get_initial_value().size().0 as isize),
+            root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
             used_root_font_size: AtomicBool::new(false),
             used_viewport_units: AtomicBool::new(false),
         }
     }
 
     /// Return the default computed values for this device.
     pub fn default_computed_values(&self) -> &ComputedValues {
         // FIXME(bz): This isn't really right, but it's no more wrong
--- a/servo/components/style/values/computed/font.rs
+++ b/servo/components/style/values/computed/font.rs
@@ -134,16 +134,60 @@ impl FontWeight {
     }
 }
 
 impl FontSize {
     /// The actual computed font size.
     pub fn size(self) -> Au {
         self.size.into()
     }
+
+    #[inline]
+    /// Get default value of font size.
+    pub fn medium() -> Self {
+        Self {
+            size: Au::from_px(specified::FONT_MEDIUM_PX).into(),
+            keyword_info: Some(KeywordInfo::medium())
+        }
+    }
+
+    /// FIXME(emilio): This is very complex. Also, it should move to
+    /// StyleBuilder.
+    pub fn cascade_inherit_font_size(context: &mut Context) {
+        // If inheriting, we must recompute font-size in case of language
+        // changes using the font_size_keyword. We also need to do this to
+        // handle mathml scriptlevel changes
+        let kw_inherited_size = context.builder.get_parent_font()
+                                       .clone_font_size()
+                                       .keyword_info.map(|info| {
+            specified::FontSize::Keyword(info).to_computed_value(context).size
+        });
+        let mut font = context.builder.take_font();
+        font.inherit_font_size_from(context.builder.get_parent_font(),
+                                    kw_inherited_size,
+                                    context.builder.device);
+        context.builder.put_font(font);
+    }
+
+    /// Cascade the initial value for the `font-size` property.
+    ///
+    /// FIXME(emilio): This is the only function that is outside of the
+    /// `StyleBuilder`, and should really move inside!
+    ///
+    /// Can we move the font stuff there?
+    pub fn cascade_initial_font_size(context: &mut Context) {
+        // font-size's default ("medium") does not always
+        // compute to the same value and depends on the font
+        let computed = specified::FontSize::medium().to_computed_value(context);
+        context.builder.mutate_font().set_font_size(computed);
+        #[cfg(feature = "gecko")] {
+            let device = context.builder.device;
+            context.builder.mutate_font().fixup_font_min_size(device);
+        }
+    }
 }
 
 impl ToCss for FontSize {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         self.size.to_css(dest)
     }
 }
 
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -31,17 +31,17 @@ use super::specified;
 pub use app_units::Au;
 pub use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::angle::Angle;
 pub use self::background::{BackgroundSize, BackgroundRepeat};
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
 pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing};
-pub use self::font::{FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom};
+pub use self::font::{FontSize, FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom};
 pub use self::box_::{AnimationIterationCount, AnimationName, ScrollSnapType, VerticalAlign};
 pub use self::color::{Color, ColorPropertyValue, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 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;
--- a/servo/components/style/values/specified/font.rs
+++ b/servo/components/style/values/specified/font.rs
@@ -8,17 +8,17 @@
 use Atom;
 use app_units::Au;
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use properties::longhands::system_font::SystemFont;
 use std::fmt;
 use style_traits::{ToCss, StyleParseErrorKind, ParseError};
 use values::computed::{font as computed, Context, Length, NonNegativeLength, ToComputedValue};
-use values::specified::{LengthOrPercentage, NoCalcLength};
+use values::specified::{AllowQuirks, LengthOrPercentage, NoCalcLength};
 use values::specified::length::{AU_PER_PT, AU_PER_PX, FontBaseSize};
 
 const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
 
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
 /// A specified font-weight value
 pub enum FontWeight {
     /// Normal variant
@@ -461,16 +461,88 @@ impl FontSize {
     /// Obtain the system font, if any
     pub fn get_system(&self) -> Option<SystemFont> {
         if let FontSize::System(s) = *self {
             Some(s)
         } else {
             None
         }
     }
+
+    #[inline]
+    /// Get initial value for specified font size.
+    pub fn medium() -> Self {
+        FontSize::Keyword(computed::KeywordInfo::medium())
+    }
+
+    /// Parses a font-size, with quirks.
+    pub fn parse_quirky<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+        allow_quirks: AllowQuirks
+    ) -> Result<FontSize, ParseError<'i>> {
+        if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative_quirky(context, i, allow_quirks)) {
+            return Ok(FontSize::Length(lop))
+        }
+
+        if let Ok(kw) = input.try(KeywordSize::parse) {
+            return Ok(FontSize::Keyword(kw.into()))
+        }
+
+        try_match_ident_ignore_ascii_case! { input,
+            "smaller" => Ok(FontSize::Smaller),
+            "larger" => Ok(FontSize::Larger),
+        }
+    }
+
+    #[allow(unused_mut)]
+    /// Cascade `font-size` with specified value
+    pub fn cascade_specified_font_size(
+        context: &mut Context,
+        specified_value: &FontSize,
+        mut computed: computed::FontSize
+    ) {
+        // we could use clone_language and clone_font_family() here but that's
+        // expensive. Do it only in gecko mode for now.
+        #[cfg(feature = "gecko")] {
+            // if the language or generic changed, we need to recalculate
+            // the font size from the stored font-size origin information.
+            if context.builder.get_font().gecko().mLanguage.mRawPtr !=
+               context.builder.get_parent_font().gecko().mLanguage.mRawPtr ||
+               context.builder.get_font().gecko().mGenericID !=
+               context.builder.get_parent_font().gecko().mGenericID {
+                if let Some(info) = computed.keyword_info {
+                    computed.size = info.to_computed_value(context);
+                }
+            }
+        }
+
+        let device = context.builder.device;
+        let mut font = context.builder.take_font();
+        let parent_unconstrained = {
+            let parent_font = context.builder.get_parent_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(Au::from(parent)));
+            context.builder
+                   .mutate_font()
+                   .apply_unconstrained_font_size(new_unconstrained.size);
+        }
+    }
+}
+
+impl Parse for FontSize {
+    /// <length> | <percentage> | <absolute-size> | <relative-size>
+    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<FontSize, ParseError<'i>> {
+        FontSize::parse_quirky(context, input, AllowQuirks::No)
+    }
 }
 
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
 /// text-zoom. Enable if true, disable if false
 pub struct XTextZoom(pub bool);
 
 impl Parse for XTextZoom {
     fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<XTextZoom, ParseError<'i>> {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -25,17 +25,17 @@ use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
 pub use self::angle::Angle;
 #[cfg(feature = "gecko")]
 pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::background::{BackgroundRepeat, BackgroundSize};
 pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
 pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing};
-pub use self::font::{FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom};
+pub use self::font::{FontSize, FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom};
 pub use self::box_::{AnimationIterationCount, AnimationName, ScrollSnapType, VerticalAlign};
 pub use self::color::{Color, ColorPropertyValue, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};