servo: Merge #16564 - stylo: Support system fonts (from Manishearth:stylo-sys-2); r=xidorn
authorManish Goregaokar <manishsmail@gmail.com>
Fri, 21 Apr 2017 17:03:28 -0500
changeset 354412 5f8fc4f5a873e55d10f422cadc98bd3617f2320a
parent 354411 b6b8098abd34275a8709790066c8255c8c0b91ad
child 354413 fb358c9dd073fe4c4a1e341d78361b1f9a5e9b50
push id31696
push userarchaeopteryx@coole-files.de
push dateSat, 22 Apr 2017 08:43:37 +0000
treeherdermozilla-central@4ad23bc8d5c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersxidorn
milestone55.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 #16564 - stylo: Support system fonts (from Manishearth:stylo-sys-2); r=xidorn r=xidorn https://bugzilla.mozilla.org/show_bug.cgi?id=1349417 (take 2, we backed out the last one due to heap hazards) Source-Repo: https://github.com/servo/servo Source-Revision: 97c14f05df33fe4ce118f51bb23abf39000b4217
servo/components/script/dom/element.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_string_cache/mod.rs
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -463,17 +463,17 @@ impl LayoutElementHelpers for LayoutJS<E
         } else {
             None
         };
 
         if let Some(font_family) = font_family {
             hints.push(from_declaration(
                 shared_lock,
                 PropertyDeclaration::FontFamily(
-                        font_family::computed_value::T(vec![
+                        font_family::SpecifiedValue::Values(vec![
                             font_family::computed_value::FontFamily::from_atom(
                                 font_family)]))));
         }
 
         let font_size = self.downcast::<HTMLFontElement>().and_then(|this| this.get_size());
 
         if let Some(font_size) = font_size {
             hints.push(from_declaration(
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -691,16 +691,19 @@ extern "C" {
                                        aProperty: nsCSSPropertyID)
      -> RawServoAnimationValueBorrowedOrNull;
 }
 extern "C" {
     pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32)
      -> *mut nsIAtom;
 }
 extern "C" {
+    pub fn Gecko_Atomize16(aString: *const nsAString) -> *mut nsIAtom;
+}
+extern "C" {
     pub fn Gecko_AddRefAtom(aAtom: *mut nsIAtom);
 }
 extern "C" {
     pub fn Gecko_ReleaseAtom(aAtom: *mut nsIAtom);
 }
 extern "C" {
     pub fn Gecko_GetAtomAsUTF16(aAtom: *mut nsIAtom, aLength: *mut u32)
      -> *const u16;
@@ -727,16 +730,24 @@ extern "C" {
 extern "C" {
     pub fn Gecko_FontFamilyList_AppendGeneric(list: *mut FontFamilyList,
                                               familyType: FontFamilyType);
 }
 extern "C" {
     pub fn Gecko_CopyFontFamilyFrom(dst: *mut nsFont, src: *const nsFont);
 }
 extern "C" {
+    pub fn Gecko_nsFont_InitSystem(dst: *mut nsFont, font_id: i32,
+                                   font: *const nsStyleFont,
+                                   pres_context: RawGeckoPresContextBorrowed);
+}
+extern "C" {
+    pub fn Gecko_nsFont_Destroy(dst: *mut nsFont);
+}
+extern "C" {
     pub fn Gecko_SetImageOrientation(aVisibility: *mut nsStyleVisibility,
                                      aRadians: f64, aFlip: bool);
 }
 extern "C" {
     pub fn Gecko_SetImageOrientationAsFromImage(aVisibility:
                                                     *mut nsStyleVisibility);
 }
 extern "C" {
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -3,18 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![allow(unsafe_code)]
 
 //! A drop-in replacement for string_cache, but backed by Gecko `nsIAtom`s.
 
 use gecko_bindings::bindings::Gecko_AddRefAtom;
 use gecko_bindings::bindings::Gecko_Atomize;
+use gecko_bindings::bindings::Gecko_Atomize16;
 use gecko_bindings::bindings::Gecko_ReleaseAtom;
 use gecko_bindings::structs::nsIAtom;
+use nsstring::nsAString;
 use precomputed_hash::PrecomputedHash;
 use std::borrow::{Cow, Borrow};
 use std::char::{self, DecodeUtf16};
 use std::fmt::{self, Write};
 use std::hash::{Hash, Hasher};
 use std::iter::Cloned;
 use std::mem;
 use std::ops::Deref;
@@ -276,16 +278,27 @@ impl<'a> From<&'a str> for Atom {
         unsafe {
             Atom(WeakAtom::new(
                 Gecko_Atomize(string.as_ptr() as *const _, string.len() as u32)
             ))
         }
     }
 }
 
+impl<'a> From<&'a nsAString> for Atom {
+    #[inline]
+    fn from(string: &nsAString) -> Atom {
+        unsafe {
+            Atom(WeakAtom::new(
+                Gecko_Atomize16(string)
+            ))
+        }
+    }
+}
+
 impl<'a> From<Cow<'a, str>> for Atom {
     #[inline]
     fn from(string: Cow<'a, str>) -> Atom {
         Atom::from(&*string)
     }
 }
 
 impl From<String> for Atom {
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -8,16 +8,23 @@ PHYSICAL_SIDES = ["top", "left", "bottom
 LOGICAL_SIDES = ["block-start", "block-end", "inline-start", "inline-end"]
 PHYSICAL_SIZES = ["width", "height"]
 LOGICAL_SIZES = ["block-size", "inline-size"]
 
 # bool is True when logical
 ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [(side, True) for side in LOGICAL_SIDES]
 ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [(size, True) for size in LOGICAL_SIZES]
 
+SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
+                           font_variant_caps font_stretch font_kerning
+                           font_variant_position font_weight
+                           font_size_adjust font_variant_alternates
+                           font_variant_ligatures font_variant_east_asian
+                           font_variant_numeric font_language_override""".split()
+
 
 def maybe_moz_logical_alias(product, side, prop):
     if product == "gecko" and side[1]:
         axis, dir = side[0].split("-")
         if axis == "inline":
             return prop % dir
     return None
 
@@ -28,16 +35,21 @@ def to_rust_ident(name):
         name += "_"
     return name
 
 
 def to_camel_case(ident):
     return re.sub("(^|_|-)([a-z])", lambda m: m.group(2).upper(), ident.strip("_").strip("-"))
 
 
+def to_camel_case_lower(ident):
+    camel = to_camel_case(ident)
+    return camel[0].lower() + camel[1:]
+
+
 def parse_aliases(value):
     aliases = {}
     for pair in value.split():
         [a, v] = pair.split("=")
         aliases[a] = v
     return aliases
 
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -90,25 +90,28 @@ pub struct ComputedValues {
     /// from a keyword value or a keyword value on some ancestor with only
     /// font-size-relative keywords and regular inheritance in between. The
     /// integer stores the final ratio of the chain of font size relative values.
     /// and is 1 when there was just a keyword and no relative values.
     ///
     /// When this is Some, we compute font sizes by computing the keyword against
     /// the generic font, and then multiplying it by the ratio.
     pub font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
+    /// The cached system font. See longhand/font.mako.rs
+    pub cached_system_font: Option<longhands::system_font::ComputedSystemFont>,
 }
 
 impl ComputedValues {
     pub fn inherit_from(parent: &Self, default: &Self) -> Arc<Self> {
         Arc::new(ComputedValues {
             custom_properties: parent.custom_properties.clone(),
             writing_mode: parent.writing_mode,
             root_font_size: parent.root_font_size,
             font_size_keyword: parent.font_size_keyword,
+            cached_system_font: None,
             % for style_struct in data.style_structs:
             % if style_struct.inherited:
             ${style_struct.ident}: parent.${style_struct.ident}.clone(),
             % else:
             ${style_struct.ident}: default.${style_struct.ident}.clone(),
             % endif
             % endfor
         })
@@ -121,29 +124,31 @@ impl ComputedValues {
             % for style_struct in data.style_structs:
            ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
             % endfor
     ) -> Self {
         ComputedValues {
             custom_properties: custom_properties,
             writing_mode: writing_mode,
             root_font_size: root_font_size,
+            cached_system_font: None,
             font_size_keyword: font_size_keyword,
             % for style_struct in data.style_structs:
             ${style_struct.ident}: ${style_struct.ident},
             % endfor
         }
     }
 
     pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
         Arc::new(ComputedValues {
             custom_properties: None,
             writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious
             root_font_size: longhands::font_size::get_initial_value(), // FIXME(bz): Also seems dubious?
             font_size_keyword: Some((Default::default(), 1.)),
+            cached_system_font: None,
             % for style_struct in data.style_structs:
                 ${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context),
             % endfor
         })
     }
 
     #[inline]
     pub fn is_display_contents(&self) -> bool {
@@ -289,37 +294,16 @@ def set_gecko_property(ffi_name, expr):
             % endfor
             % if keyword.gecko_inexhaustive:
             x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x),
             % endif
         }
     }
 </%def>
 
-<%def name="impl_bitflags_setter(ident, gecko_ffi_name, bit_map, gecko_bit_prefix, cast_type='u8')">
-    #[allow(non_snake_case)]
-    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
-        % for gecko_bit in bit_map.values():
-        use gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
-        % endfor
-
-        let mut bits: ${cast_type} = 0;
-        // FIXME: if we ensure that the Servo bitflags storage is the same
-        // as Gecko's one, we can just copy it.
-        % for servo_bit, gecko_bit in bit_map.iteritems():
-        if v.contains(longhands::${ident}::${servo_bit}) {
-            bits |= ${gecko_bit_prefix}${gecko_bit} as ${cast_type};
-        }
-        % endfor
-
-        self.gecko.${gecko_ffi_name} = bits as ${cast_type};
-    }
-</%def>
-
-
 /// Convert a Servo color into an nscolor; with currentColor as 0
 ///
 /// Call sites will need to be updated after https://bugzilla.mozilla.org/show_bug.cgi?id=760345
 fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor {
     match color {
         Color::RGBA(rgba) => {
             convert_rgba_to_nscolor(&rgba)
         },
@@ -1585,84 +1569,43 @@ fn static_assert() {
         }
     }
 
     pub fn set_font_language_override(&mut self, v: longhands::font_language_override::computed_value::T) {
         self.gecko.mFont.languageOverride = v.0;
     }
     ${impl_simple_copy('font_language_override', 'mFont.languageOverride')}
 
-    <% font_variant_alternates_map = { "HISTORICAL_FORMS": "HISTORICAL",
-                                       "STYLISTIC": "STYLISTIC",
-                                       "STYLESET": "STYLESET",
-                                       "CHARACTER_VARIANT": "CHARACTER_VARIANT",
-                                       "SWASH": "SWASH",
-                                       "ORNAMENTS": "ORNAMENTS",
-                                       "ANNOTATION": "ANNOTATION" } %>
-    // FIXME: Set alternateValues as well.
-    // self.gecko.mFont.alternateValues = xxx;
-    ${impl_bitflags_setter('font_variant_alternates',
-                           'mFont.variantAlternates',
-                           font_variant_alternates_map,
-                           'NS_FONT_VARIANT_ALTERNATES_',
-                           cast_type='u16')}
+    pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T) {
+        self.gecko.mFont.variantAlternates = v.to_gecko_keyword()
+    }
+
     #[allow(non_snake_case)]
     pub fn copy_font_variant_alternates_from(&mut self, other: &Self) {
         self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates;
         // FIXME: Copy alternateValues as well.
         // self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues;
     }
 
-    //                                 servo_bit: gecko_bit
-    <% font_variant_ligatures_map = { "NONE": "NONE",
-                                      "COMMON_LIGATURES": "COMMON",
-                                      "NO_COMMON_LIGATURES": "NO_COMMON",
-                                      "DISCRETIONARY_LIGATURES": "DISCRETIONARY",
-                                      "NO_DISCRETIONARY_LIGATURES": "NO_DISCRETIONARY",
-                                      "HISTORICAL_LIGATURES": "HISTORICAL",
-                                      "NO_HISTORICAL_LIGATURES": "NO_HISTORICAL",
-                                      "CONTEXTUAL": "CONTEXTUAL",
-                                      "NO_CONTEXTUAL": "NO_CONTEXTUAL" } %>
-    ${impl_bitflags_setter('font_variant_ligatures',
-                           'mFont.variantLigatures',
-                           font_variant_ligatures_map,
-                           'NS_FONT_VARIANT_LIGATURES_',
-                           cast_type='u16')}
+    pub fn set_font_variant_ligatures(&mut self, v: longhands::font_variant_ligatures::computed_value::T) {
+        self.gecko.mFont.variantLigatures = v.to_gecko_keyword()
+    }
+
     ${impl_simple_copy('font_variant_ligatures', 'mFont.variantLigatures')}
 
-    //                                 servo_bit: gecko_bit
-    <% font_variant_east_asian_map = { "JIS78": "JIS78",
-                                       "JIS83": "JIS83",
-                                       "JIS90": "JIS90",
-                                       "JIS04": "JIS04",
-                                       "SIMPLIFIED": "SIMPLIFIED",
-                                       "TRADITIONAL": "TRADITIONAL",
-                                       "FULL_WIDTH": "FULL_WIDTH",
-                                       "PROPORTIONAL_WIDTH": "PROP_WIDTH",
-                                       "RUBY": "RUBY" } %>
-    ${impl_bitflags_setter('font_variant_east_asian',
-                           'mFont.variantEastAsian',
-                           font_variant_east_asian_map,
-                           'NS_FONT_VARIANT_EAST_ASIAN_',
-                           cast_type='u16')}
+    pub fn set_font_variant_east_asian(&mut self, v: longhands::font_variant_east_asian::computed_value::T) {
+        self.gecko.mFont.variantEastAsian = v.to_gecko_keyword()
+    }
+
     ${impl_simple_copy('font_variant_east_asian', 'mFont.variantEastAsian')}
 
-    //                              servo_bit: gecko_bit
-    <% font_variant_numeric_map = { "LINING_NUMS": "LINING",
-                                    "OLDSTYLE_NUMS": "OLDSTYLE",
-                                    "PROPORTIONAL_NUMS": "PROPORTIONAL",
-                                    "TABULAR_NUMS": "TABULAR",
-                                    "DIAGONAL_FRACTIONS": "DIAGONAL_FRACTIONS",
-                                    "STACKED_FRACTIONS": "STACKED_FRACTIONS",
-                                    "SLASHED_ZERO": "SLASHZERO",
-                                    "ORDINAL": "ORDINAL" } %>
-    ${impl_bitflags_setter('font_variant_numeric',
-                           'mFont.variantNumeric',
-                           font_variant_numeric_map,
-                           'NS_FONT_VARIANT_NUMERIC_')}
+    pub fn set_font_variant_numeric(&mut self, v: longhands::font_variant_numeric::computed_value::T) {
+        self.gecko.mFont.variantNumeric = v.to_gecko_keyword()
+    }
+
     ${impl_simple_copy('font_variant_numeric', 'mFont.variantNumeric')}
 </%self:impl_trait>
 
 <%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
         unsafe { self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len()) };
 
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -1,13 +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/. */
 
-<%! from data import Keyword, to_rust_ident, to_camel_case, LOGICAL_SIDES, PHYSICAL_SIDES, LOGICAL_SIZES %>
+<%!
+    from data import Keyword, to_rust_ident, to_camel_case
+    from data import LOGICAL_SIDES, PHYSICAL_SIDES, LOGICAL_SIZES, SYSTEM_FONT_LONGHANDS
+%>
 
 <%def name="predefined_type(name, type, initial_value, parse_method='parse',
             needs_context=True, vector=False, initial_specified_value=None, **kwargs)">
     <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
         #[allow(unused_imports)]
         use app_units::Au;
         use cssparser::{Color as CSSParserColor, RGBA};
         pub use values::specified::${type} as SpecifiedValue;
@@ -207,17 +210,16 @@
                                        .collect())
                 }
             }
         % else:
             ${caller.body()}
         % endif
     </%call>
 </%def>
-
 <%def name="longhand(*args, **kwargs)">
     <%
         property = data.declare_longhand(*args, **kwargs)
         if property is None:
             return ""
     %>
     /// ${property.spec}
     pub mod ${property.ident} {
@@ -273,16 +275,21 @@
                                                              &value);
                         }
                         % if property.logical:
                             let wm = context.style.writing_mode;
                         % endif
                         <% maybe_wm = ", wm" if property.logical else "" %>
                         match *value {
                             DeclaredValue::Value(ref specified_value) => {
+                                % if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
+                                    if let Some(sf) = specified_value.get_system() {
+                                        longhands::system_font::resolve_system_font(sf, context);
+                                    }
+                                % endif
                                 let computed = specified_value.to_computed_value(context);
                                 % if property.ident == "font_size":
                                     longhands::font_size::cascade_specified_font_size(context,
                                                                                       specified_value,
                                                                                       computed,
                                                                                       inherited_style.get_font());
                                 % else:
                                     % if property.has_uncacheable_values:
@@ -383,16 +390,120 @@
                         specified.map(|s| PropertyDeclaration::${property.camel_case}(s))
                     }
                 }
             }
         % endif
     }
 </%def>
 
+<%def name="single_keyword_system(name, values, **kwargs)">
+    <%
+        keyword_kwargs = {a: kwargs.pop(a, None) for a in [
+            'gecko_constant_prefix', 'gecko_enum_prefix',
+            'extra_gecko_values', 'extra_servo_values',
+            'custom_consts', 'gecko_inexhaustive',
+        ]}
+        keyword = keyword=Keyword(name, values, **keyword_kwargs)
+    %>
+    <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
+        use values::HasViewportPercentage;
+        use properties::longhands::system_font::SystemFont;
+        use std::fmt;
+        use style_traits::ToCss;
+        no_viewport_percentage!(SpecifiedValue);
+
+        pub mod computed_value {
+            use cssparser::Parser;
+            use parser::{Parse, ParserContext};
+
+            use style_traits::ToCss;
+            define_css_keyword_enum! { T:
+                % for value in keyword.values_for(product):
+                    "${value}" => ${to_rust_ident(value)},
+                % endfor
+            }
+
+            impl Parse for T {
+                fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+                    T::parse(input)
+                }
+            }
+
+            ${gecko_keyword_conversion(keyword, keyword.values_for(product), type="T", cast_to="i32")}
+        }
+
+        #[derive(Debug, Clone, PartialEq, Eq, Copy)]
+        pub enum SpecifiedValue {
+            Keyword(computed_value::T),
+            System(SystemFont),
+        }
+
+        impl ToCss for SpecifiedValue {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+                match *self {
+                    SpecifiedValue::Keyword(k) => k.to_css(dest),
+                    SpecifiedValue::System(_) => Ok(())
+                }
+            }
+        }
+
+        pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+            Ok(SpecifiedValue::Keyword(computed_value::T::parse(input)?))
+        }
+
+        impl ToComputedValue for SpecifiedValue {
+            type ComputedValue = computed_value::T;
+            fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue {
+                match *self {
+                    SpecifiedValue::Keyword(v) => v,
+                    SpecifiedValue::System(_) => {
+                        % if product == "gecko":
+                            _cx.style.cached_system_font.as_ref().unwrap().${to_rust_ident(name)}
+                        % else:
+                            unreachable!()
+                        % endif
+                    }
+                }
+            }
+            fn from_computed_value(other: &computed_value::T) -> Self {
+                SpecifiedValue::Keyword(*other)
+            }
+        }
+
+        impl From<computed_value::T> for SpecifiedValue {
+            fn from(other: computed_value::T) -> Self {
+                SpecifiedValue::Keyword(other)
+            }
+        }
+
+        #[inline]
+        pub fn get_initial_value() -> computed_value::T {
+            computed_value::T::${to_rust_ident(values.split()[0])}
+        }
+        #[inline]
+        pub fn get_initial_specified_value() -> SpecifiedValue {
+            SpecifiedValue::Keyword(computed_value::T::${to_rust_ident(values.split()[0])})
+        }
+
+        impl SpecifiedValue {
+            pub fn system_font(f: SystemFont) -> Self {
+                SpecifiedValue::System(f)
+            }
+            pub fn get_system(&self) -> Option<SystemFont> {
+                if let SpecifiedValue::System(s) = *self {
+                    Some(s)
+                } else {
+                    None
+                }
+            }
+        }
+    </%call>
+</%def>
+
 <%def name="single_keyword(name, values, vector=False, **kwargs)">
     <%call expr="single_keyword_computed(name, values, vector, **kwargs)">
         % if not "extra_specified" in kwargs and ("aliases" in kwargs or (("extra_%s_aliases" % product) in kwargs)):
             impl ToComputedValue for SpecifiedValue {
                 type ComputedValue = computed_value::T;
 
                 #[inline]
                 fn to_computed_value(&self, _context: &Context) -> computed_value::T {
@@ -416,48 +527,79 @@
             impl ComputedValueAsSpecified for SpecifiedValue {}
         % endif
 
         use values::HasViewportPercentage;
         no_viewport_percentage!(SpecifiedValue);
     </%call>
 </%def>
 
-<%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue')">
+<%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
     <%
         if not values:
             values = keyword.values_for(product)
+        maybe_cast = "as %s" % cast_to if cast_to else ""
+        const_type = cast_to if cast_to else "u32"
     %>
     #[cfg(feature = "gecko")]
     impl ${type} {
         /// Obtain a specified value from a Gecko keyword value
         ///
         /// Intended for use with presentation attributes, not style structs
         pub fn from_gecko_keyword(kw: u32) -> Self {
             use gecko_bindings::structs;
-            % if keyword.gecko_enum_prefix:
+            % for value in values:
+                // We can't match on enum values if we're matching on a u32
+                const ${to_rust_ident(value).upper()}: ${const_type}
+                    = structs::${keyword.gecko_constant(value)} as ${const_type};
+            % endfor
+            match kw ${maybe_cast} {
                 % for value in values:
-                    // We can't match on enum values if we're matching on a u32
-                    const ${to_rust_ident(value).upper()}: u32
-                        = structs::${keyword.gecko_enum_prefix}::${to_camel_case(value)} as u32;
+                    ${to_rust_ident(value).upper()} => ${type}::${to_rust_ident(value)},
                 % endfor
-                match kw {
-                    % for value in values:
-                        ${to_rust_ident(value).upper()} => ${type}::${to_rust_ident(value)},
-                    % endfor
-                    x => panic!("Found unexpected value in style struct for ${keyword.name} property: {:?}", x),
+                x => panic!("Found unexpected value in style struct for ${keyword.name} property: {:?}", x),
+            }
+        }
+    }
+</%def>
+
+<%def name="gecko_bitflags_conversion(bit_map, gecko_bit_prefix, type, kw_type='u8')">
+    #[cfg(feature = "gecko")]
+    impl ${type} {
+        /// Obtain a specified value from a Gecko keyword value
+        ///
+        /// Intended for use with presentation attributes, not style structs
+        pub fn from_gecko_keyword(kw: ${kw_type}) -> Self {
+            % for gecko_bit in bit_map.values():
+            use gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
+            % endfor
+
+            let mut bits = ${type}::empty();
+            % for servo_bit, gecko_bit in bit_map.iteritems():
+                if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 {
+                    bits |= ${servo_bit};
                 }
-            % else:
-                match kw {
-                    % for value in values:
-                        structs::${keyword.gecko_constant(value)} => ${type}::${to_rust_ident(value)},
-                    % endfor
-                    x => panic!("Found unexpected value in style struct for ${keyword.name} property: {:?}", x),
+            % endfor
+            bits
+        }
+
+        pub fn to_gecko_keyword(self) -> ${kw_type} {
+            % for gecko_bit in bit_map.values():
+            use gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
+            % endfor
+
+            let mut bits: ${kw_type} = 0;
+            // FIXME: if we ensure that the Servo bitflags storage is the same
+            // as Gecko's one, we can just copy it.
+            % for servo_bit, gecko_bit in bit_map.iteritems():
+                if self.contains(${servo_bit}) {
+                    bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type};
                 }
-            % endif
+            % endfor
+            bits
         }
     }
 </%def>
 
 <%def name="single_keyword_computed(name, values, vector=False,
             extra_specified=None, needs_conversion=False, **kwargs)">
     <%
         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1,14 +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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
+<% from data import SYSTEM_FONT_LONGHANDS %>
+
 use app_units::Au;
 use cssparser::{Color as CSSParserColor, Parser, RGBA};
 use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
@@ -404,25 +406,31 @@ impl AnimationValue {
                             % endif
                     }
                 % endif
             % endfor
         }
     }
 
     /// Construct an AnimationValue from a property declaration
-    pub fn from_declaration(decl: &PropertyDeclaration, context: &Context, initial: &ComputedValues) -> Option<Self> {
+    pub fn from_declaration(decl: &PropertyDeclaration, context: &mut Context,
+                            initial: &ComputedValues) -> Option<Self> {
         use error_reporting::StdoutErrorReporter;
         use properties::LonghandId;
         use properties::DeclaredValue;
 
         match *decl {
             % for prop in data.longhands:
             % if prop.animatable:
             PropertyDeclaration::${prop.camel_case}(ref val) => {
+            % if prop.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
+                if let Some(sf) = val.get_system() {
+                    longhands::system_font::resolve_system_font(sf, context);
+                }
+            % endif
                 Some(AnimationValue::${prop.camel_case}(val.to_computed_value(context)))
             },
             % endif
             % endfor
             PropertyDeclaration::CSSWideKeyword(id, keyword) => {
                 match id {
                     // We put all the animatable properties first in the hopes
                     // that it might increase match locality.
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -88,17 +88,18 @@
                                    _error_reporter: &ParseErrorReporter) {
             longhands::_servo_display_for_hypothetical_box::derive_from_display(context);
             longhands::_servo_text_decorations_in_effect::derive_from_display(context);
             longhands::_servo_under_display_none::derive_from_display(context);
         }
     % endif
 
     ${helpers.gecko_keyword_conversion(Keyword('display', ' '.join(values),
-                                               gecko_enum_prefix='StyleDisplay'))}
+                                               gecko_enum_prefix='StyleDisplay',
+                                               gecko_strip_moz_prefix=False))}
 
 </%helpers:longhand>
 
 ${helpers.single_keyword("-moz-top-layer", "none top",
                          gecko_constant_prefix="NS_STYLE_TOP_LAYER",
                          gecko_ffi_name="mTopLayer", need_clone=True,
                          products="gecko", animation_type="none", internal=True,
                          spec="Internal (not web-exposed)")}
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -1,29 +1,86 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
+<% from data import Method, to_camel_case, to_rust_ident, to_camel_case_lower, SYSTEM_FONT_LONGHANDS %>
 
 <% data.new_style_struct("Font",
                          inherited=True) %>
-<%helpers:longhand name="font-family" animation_type="none" need_index="True"
+
+<%def name="nongecko_unreachable()">
+    %if product == "gecko":
+        ${caller.body()}
+    %else:
+        unreachable!()
+    %endif
+</%def>
+
+// Define ToComputedValue, ToCss, and other boilerplate for a specified value
+// which is of the form `enum SpecifiedValue {Value(..), System(SystemFont)}`
+<%def name="simple_system_boilerplate(name)">
+    impl ToCss for SpecifiedValue {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            match *self {
+                SpecifiedValue::Value(v) => v.to_css(dest),
+                SpecifiedValue::System(_) => Ok(())
+            }
+        }
+    }
+
+
+    impl SpecifiedValue {
+        pub fn system_font(f: SystemFont) -> Self {
+            SpecifiedValue::System(f)
+        }
+        pub fn get_system(&self) -> Option<SystemFont> {
+            if let SpecifiedValue::System(s) = *self {
+                Some(s)
+            } else {
+                None
+            }
+        }
+    }
+
+    impl ToComputedValue for SpecifiedValue {
+        type ComputedValue = computed_value::T;
+
+        fn to_computed_value(&self, _context: &Context) -> computed_value::T {
+            match *self {
+                SpecifiedValue::Value(v) => v,
+                SpecifiedValue::System(_) => {
+                    <%self:nongecko_unreachable>
+                        _context.style.cached_system_font.as_ref().unwrap().${name}
+                    </%self:nongecko_unreachable>
+                }
+            }
+        }
+
+        fn from_computed_value(other: &computed_value::T) -> Self {
+            SpecifiedValue::Value(*other)
+        }
+    }
+</%def>
+
+<%helpers:longhand name="font-family" animation_type="none" need_index="True"  boxed="${product == 'gecko'}"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
+    use properties::longhands::system_font::SystemFont;
     use self::computed_value::{FontFamily, FamilyName};
+    use std::fmt;
+    use style_traits::ToCss;
     use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
-    pub use self::computed_value::T as SpecifiedValue;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser::{CssStringWriter, Parser, serialize_identifier};
+        use properties::longhands::system_font::SystemFont;
         use std::fmt::{self, Write};
         use Atom;
         use style_traits::ToCss;
         pub use self::FontFamily as SingleComputedValue;
 
         #[derive(Debug, PartialEq, Eq, Clone, Hash)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum FontFamily {
@@ -195,93 +252,147 @@
 
     /// <family-name>#
     /// <family-name> = <string> | [ <ident>+ ]
     /// TODO: <generic-family>
     pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         SpecifiedValue::parse(input)
     }
 
+    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+    pub enum SpecifiedValue {
+        Values(Vec<FontFamily>),
+        System(SystemFont),
+    }
+
+    impl ToComputedValue for SpecifiedValue {
+        type ComputedValue = computed_value::T;
+        fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue {
+            match *self {
+                SpecifiedValue::Values(ref v) => computed_value::T(v.clone()),
+                SpecifiedValue::System(_) => {
+                    <%self:nongecko_unreachable>
+                        _cx.style.cached_system_font.as_ref().unwrap().font_family.clone()
+                    </%self:nongecko_unreachable>
+                }
+            }
+        }
+        fn from_computed_value(other: &computed_value::T) -> Self {
+            SpecifiedValue::Values(other.0.clone())
+        }
+    }
+
     impl SpecifiedValue {
+        pub fn system_font(f: SystemFont) -> Self {
+            SpecifiedValue::System(f)
+        }
+        pub fn get_system(&self) -> Option<SystemFont> {
+            if let SpecifiedValue::System(s) = *self {
+                Some(s)
+            } else {
+                None
+            }
+        }
+
         pub fn parse(input: &mut Parser) -> Result<Self, ()> {
-            input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue)
+            input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue::Values)
+        }
+    }
+
+
+    impl ToCss for SpecifiedValue {
+        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            match *self {
+                SpecifiedValue::Values(ref v) => {
+                    let mut iter = v.iter();
+                    iter.next().unwrap().to_css(dest)?;
+                    for family in iter {
+                        dest.write_str(", ")?;
+                        family.to_css(dest)?;
+                    }
+                    Ok(())
+                }
+                _ => Ok(())
+            }
         }
     }
 
     /// `FamilyName::parse` is based on `FontFamily::parse` and not the other way around
     /// because we want the former to exclude generic family keywords.
     impl Parse for FamilyName {
         fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
             match FontFamily::parse(input) {
                 Ok(FontFamily::FamilyName(name)) => Ok(name),
                 Ok(FontFamily::Generic(_)) |
                 Err(()) => Err(())
             }
         }
     }
 </%helpers:longhand>
 
-
-${helpers.single_keyword("font-style",
-                         "normal italic oblique",
-                         gecko_constant_prefix="NS_FONT_STYLE",
-                         gecko_ffi_name="mFont.style",
-                         spec="https://drafts.csswg.org/css-fonts/#propdef-font-style",
-                         animation_type="none",
-                         needs_conversion=True)}
+${helpers.single_keyword_system("font-style",
+                                "normal italic oblique",
+                                gecko_constant_prefix="NS_FONT_STYLE",
+                                gecko_ffi_name="mFont.style",
+                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-style",
+                                animation_type="none")}
 
 
 <% font_variant_caps_custom_consts= { "small-caps": "SMALLCAPS",
                                       "all-small": "ALLSMALL",
                                       "petite-caps": "PETITECAPS",
                                       "all-petite": "ALLPETITE",
                                       "titling-caps": "TITLING" } %>
 
-${helpers.single_keyword("font-variant-caps",
-                         "normal small-caps",
-                         extra_gecko_values="all-small petite-caps unicase titling-caps",
-                         gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
-                         gecko_ffi_name="mFont.variantCaps",
-                         spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
-                         custom_consts=font_variant_caps_custom_consts,
-                         animation_type="none")}
+${helpers.single_keyword_system("font-variant-caps",
+                               "normal small-caps",
+                               extra_gecko_values="all-small petite-caps unicase titling-caps",
+                               gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
+                               gecko_ffi_name="mFont.variantCaps",
+                               spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
+                               custom_consts=font_variant_caps_custom_consts,
+                               animation_type="none")}
 
 <%helpers:longhand name="font-weight" need_clone="True" animation_type="normal"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
+    use properties::longhands::system_font::SystemFont;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq, Eq, Copy)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         Bold,
         Bolder,
         Lighter,
         % for weight in range(100, 901, 100):
             Weight${weight},
         % endfor
+        System(SystemFont),
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 SpecifiedValue::Normal => dest.write_str("normal"),
                 SpecifiedValue::Bold => dest.write_str("bold"),
                 SpecifiedValue::Bolder => dest.write_str("bolder"),
                 SpecifiedValue::Lighter => dest.write_str("lighter"),
                 % for weight in range(100, 901, 100):
                     SpecifiedValue::Weight${weight} => dest.write_str("${weight}"),
                 % endfor
+                SpecifiedValue::System(_) => Ok(())
             }
         }
     }
+
     /// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         input.try(|input| {
             match_ignore_ascii_case! { &try!(input.expect_ident()),
                 "normal" => Ok(SpecifiedValue::Normal),
                 "bold" => Ok(SpecifiedValue::Bold),
                 "bolder" => Ok(SpecifiedValue::Bolder),
                 "lighter" => Ok(SpecifiedValue::Lighter),
@@ -303,27 +414,41 @@
         }
 
         pub fn from_gecko_keyword(kw: u32) -> Self {
             Self::from_int(kw as i32).expect("Found unexpected value in style
                                               struct for font-weight property")
         }
     }
 
+    impl SpecifiedValue {
+        pub fn system_font(f: SystemFont) -> Self {
+            SpecifiedValue::System(f)
+        }
+        pub fn get_system(&self) -> Option<SystemFont> {
+            if let SpecifiedValue::System(s) = *self {
+                Some(s)
+            } else {
+                None
+            }
+        }
+    }
+
     /// Used in @font-face, where relative keywords are not allowed.
     impl Parse for computed_value::T {
         fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
             match parse(context, input)? {
                 % for weight in range(100, 901, 100):
                     SpecifiedValue::Weight${weight} => Ok(computed_value::T::Weight${weight}),
                 % endfor
                 SpecifiedValue::Normal => Ok(computed_value::T::Weight400),
                 SpecifiedValue::Bold => Ok(computed_value::T::Weight700),
                 SpecifiedValue::Bolder |
-                SpecifiedValue::Lighter => Err(())
+                SpecifiedValue::Lighter => Err(()),
+                SpecifiedValue::System(..) => unreachable!(),
             }
         }
     }
 
     pub mod computed_value {
         use std::fmt;
         #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
@@ -337,16 +462,25 @@
             #[inline]
             pub fn is_bold(self) -> bool {
                 match self {
                     T::Weight900 | T::Weight800 |
                     T::Weight700 | T::Weight600 => true,
                     _ => false
                 }
             }
+
+            /// Obtain a Servo computed value from a Gecko computed font-weight
+            pub unsafe fn from_gecko_weight(weight: u16) -> Self {
+                use std::mem::transmute;
+                debug_assert!(weight >= 100);
+                debug_assert!(weight <= 900);
+                debug_assert!(weight % 10 == 0);
+                transmute(weight)
+            }
         }
     }
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 % for weight in range(100, 901, 100):
                     computed_value::T::Weight${weight} => dest.write_str("${weight}"),
                 % endfor
@@ -391,16 +525,21 @@
                     computed_value::T::Weight300 => computed_value::T::Weight100,
                     computed_value::T::Weight400 => computed_value::T::Weight100,
                     computed_value::T::Weight500 => computed_value::T::Weight100,
                     computed_value::T::Weight600 => computed_value::T::Weight400,
                     computed_value::T::Weight700 => computed_value::T::Weight400,
                     computed_value::T::Weight800 => computed_value::T::Weight700,
                     computed_value::T::Weight900 => computed_value::T::Weight700,
                 },
+                SpecifiedValue::System(_) => {
+                    <%self:nongecko_unreachable>
+                        context.style.cached_system_font.as_ref().unwrap().font_weight.clone()
+                    </%self:nongecko_unreachable>
+                }
             }
         }
 
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             match *computed {
                 % for weight in range(100, 901, 100):
                     computed_value::T::Weight${weight} => SpecifiedValue::Weight${weight},
@@ -408,31 +547,33 @@
             }
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-size" need_clone="True" animation_type="normal"
                    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::ToCss;
     use values::{FONT_MEDIUM_PX, HasViewportPercentage};
     use values::specified::{FontRelativeLength, LengthOrPercentage, Length};
     use values::specified::{NoCalcLength, Percentage};
     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"),
                 SpecifiedValue::Larger => dest.write_str("larger"),
+                SpecifiedValue::System(_) => Ok(()),
             }
         }
     }
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             match *self {
                 SpecifiedValue::Length(ref lop) => lop.has_viewport_percentage(),
@@ -449,16 +590,17 @@
         /// The ratio in any specified keyword value
         /// will be 1, but we cascade keywordness even
         /// after font-relative (percent and em) values
         /// have been applied, which is where the keyword
         /// comes in. See bug 1355707
         Keyword(KeywordSize, f32),
         Smaller,
         Larger,
+        System(SystemFont)
     }
 
     impl From<specified::LengthOrPercentage> for SpecifiedValue {
         fn from(other: specified::LengthOrPercentage) -> Self {
             SpecifiedValue::Length(other)
         }
     }
 
@@ -659,16 +801,22 @@
                 SpecifiedValue::Smaller => {
                     FontRelativeLength::Em(0.85)
                         .to_computed_value(context, base_size)
                 }
                 SpecifiedValue::Larger => {
                     FontRelativeLength::Em(1.2)
                         .to_computed_value(context, base_size)
                 }
+
+                SpecifiedValue::System(_) => {
+                    <%self:nongecko_unreachable>
+                        context.style.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)
@@ -707,16 +855,29 @@
 
         match_ignore_ascii_case! {&*input.expect_ident()?,
             "smaller" => Ok(SpecifiedValue::Smaller),
             "larger" => Ok(SpecifiedValue::Larger),
             _ => Err(())
         }
     }
 
+    impl SpecifiedValue {
+        pub fn system_font(f: SystemFont) -> Self {
+            SpecifiedValue::System(f)
+        }
+        pub fn get_system(&self) -> Option<SystemFont> {
+            if let SpecifiedValue::System(s) = *self {
+                Some(s)
+            } else {
+                None
+            }
+        }
+    }
+
     pub fn cascade_specified_font_size(context: &mut Context,
                                       specified_value: &SpecifiedValue,
                                       computed: Au,
                                       parent: &Font) {
         if let SpecifiedValue::Keyword(kw, fraction)
                             = *specified_value {
             context.mutate_style().font_size_keyword = Some((kw, fraction));
         } else if let Some(ratio) = specified_value.as_font_ratio() {
@@ -770,58 +931,79 @@
         context.mutate_style().mutate_${data.current_style_struct.name_lower}()
                .set_font_size(computed);
         context.mutate_style().font_size_keyword = Some((Default::default(), 1.));
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust" animation_type="normal"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
+    use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Copy, Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         None,
         Number(specified::Number),
+        System(SystemFont),
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result
             where W: fmt::Write,
         {
             match *self {
                 SpecifiedValue::None => dest.write_str("none"),
                 SpecifiedValue::Number(number) => number.to_css(dest),
+                SpecifiedValue::System(_) => Ok(()),
             }
         }
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
             match *self {
                 SpecifiedValue::None => computed_value::T::None,
                 SpecifiedValue::Number(ref n) => computed_value::T::Number(n.to_computed_value(context)),
+                SpecifiedValue::System(_) => {
+                    <%self:nongecko_unreachable>
+                        context.style.cached_system_font.as_ref().unwrap().font_size_adjust
+                    </%self:nongecko_unreachable>
+                }
             }
         }
 
         fn from_computed_value(computed: &computed_value::T) -> Self {
             match *computed {
                 computed_value::T::None => SpecifiedValue::None,
                 computed_value::T::Number(ref v) => SpecifiedValue::Number(specified::Number::from_computed_value(v)),
             }
         }
     }
 
+    impl SpecifiedValue {
+        pub fn system_font(f: SystemFont) -> Self {
+            SpecifiedValue::System(f)
+        }
+        pub fn get_system(&self) -> Option<SystemFont> {
+            if let SpecifiedValue::System(s) = *self {
+                Some(s)
+            } else {
+                None
+            }
+        }
+    }
+
     pub mod computed_value {
         use properties::animated_properties::{ComputeDistance, Interpolate};
         use std::fmt;
         use style_traits::ToCss;
         use values::CSSFloat;
 
         #[derive(Copy, Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -836,16 +1018,25 @@
             {
                 match *self {
                     T::None => dest.write_str("none"),
                     T::Number(number) => number.to_css(dest),
                 }
             }
         }
 
+        impl T {
+            pub fn from_gecko_adjust(gecko: f32) -> Self {
+                match gecko {
+                    -1.0 => T::None,
+                    _ => T::Number(gecko),
+                }
+            }
+        }
+
         impl Interpolate for T {
             fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
                 match (*self, *other) {
                     (T::Number(ref number), T::Number(ref other)) =>
                         Ok(T::Number(try!(number.interpolate(other, time)))),
                     _ => Err(()),
                 }
             }
@@ -944,61 +1135,78 @@
                 Ok(result)
             },
             _ => Err(())
         }
     }
 </%helpers:longhand>
 
 // FIXME: This prop should be animatable
-${helpers.single_keyword("font-stretch",
-                         "normal ultra-condensed extra-condensed condensed \
-                          semi-condensed semi-expanded expanded extra-expanded \
-                          ultra-expanded",
-                         gecko_ffi_name="mFont.stretch",
-                         gecko_constant_prefix="NS_FONT_STRETCH",
-                         cast_type='i16',
-                         spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
-                         animation_type="none")}
+${helpers.single_keyword_system("font-stretch",
+                                "normal ultra-condensed extra-condensed condensed \
+                                 semi-condensed semi-expanded expanded extra-expanded \
+                                 ultra-expanded",
+                                gecko_ffi_name="mFont.stretch",
+                                gecko_constant_prefix="NS_FONT_STRETCH",
+                                cast_type='i16',
+                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
+                                animation_type="none")}
 
-${helpers.single_keyword("font-kerning",
-                         "auto none normal",
-                         products="gecko",
-                         gecko_ffi_name="mFont.kerning",
-                         gecko_constant_prefix="NS_FONT_KERNING",
-                         spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
-                         animation_type="none")}
+${helpers.single_keyword_system("font-kerning",
+                                "auto none normal",
+                                products="gecko",
+                                gecko_ffi_name="mFont.kerning",
+                                gecko_constant_prefix="NS_FONT_KERNING",
+                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
+                                animation_type="none")}
 
 /// FIXME: Implement proper handling of each values.
 /// https://github.com/servo/servo/issues/15957
 <%helpers:longhand name="font-variant-alternates" products="gecko" animation_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
+    use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub flags SpecifiedValue: u8 {
+        pub flags VariantAlternates: u8 {
             const NORMAL = 0,
             const HISTORICAL_FORMS = 0x01,
             const STYLISTIC = 0x02,
             const STYLESET = 0x04,
             const CHARACTER_VARIANT = 0x08,
             const SWASH = 0x10,
             const ORNAMENTS = 0x20,
             const ANNOTATION = 0x40,
         }
     }
 
-    impl ToCss for SpecifiedValue {
+    #[derive(Debug, Clone, PartialEq)]
+    pub enum SpecifiedValue {
+        Value(VariantAlternates),
+        System(SystemFont)
+    }
+
+    <%self:simple_system_boilerplate name="font_variant_alternates"></%self:simple_system_boilerplate>
+
+    <% font_variant_alternates_map = { "HISTORICAL_FORMS": "HISTORICAL",
+                                       "STYLISTIC": "STYLISTIC",
+                                       "STYLESET": "STYLESET",
+                                       "CHARACTER_VARIANT": "CHARACTER_VARIANT",
+                                       "SWASH": "SWASH",
+                                       "ORNAMENTS": "ORNAMENTS",
+                                       "ANNOTATION": "ANNOTATION" } %>
+    ${helpers.gecko_bitflags_conversion(font_variant_alternates_map, 'NS_FONT_VARIANT_ALTERNATES_',
+                                        'VariantAlternates', kw_type='u16')}
+
+    impl ToCss for VariantAlternates {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.is_empty() {
                 return dest.write_str("normal")
             }
 
             let mut has_any = false;
 
             macro_rules! write_value {
@@ -1022,40 +1230,40 @@
             write_value!(ANNOTATION => "annotation");
 
             debug_assert!(has_any);
             Ok(())
         }
     }
 
     pub mod computed_value {
-        pub type T = super::SpecifiedValue;
+        pub type T = super::VariantAlternates;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::empty()
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::empty()
+        SpecifiedValue::Value(VariantAlternates::empty())
     }
 
     /// normal |
     ///  [ stylistic(<feature-value-name>)           ||
     ///    historical-forms                          ||
     ///    styleset(<feature-value-name> #)          ||
     ///    character-variant(<feature-value-name> #) ||
     ///    swash(<feature-value-name>)               ||
     ///    ornaments(<feature-value-name>)           ||
     ///    annotation(<feature-value-name>) ]
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut result = SpecifiedValue::empty();
+        let mut result = VariantAlternates::empty();
 
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
-            return Ok(result)
+            return Ok(SpecifiedValue::Value(result))
         }
 
         while let Ok(ident) = input.try(|input| input.expect_ident()) {
             let flag = match_ignore_ascii_case! { &ident,
                 "stylistic" => STYLISTIC,
                 "historical-forms" => HISTORICAL_FORMS,
                 "styleset" => STYLESET,
                 "character-variant" => CHARACTER_VARIANT,
@@ -1066,17 +1274,17 @@
             };
             if result.intersects(flag) {
                 return Err(())
             }
             result.insert(flag);
         }
 
         if !result.is_empty() {
-            Ok(result)
+            Ok(SpecifiedValue::Value(result))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
 macro_rules! exclusive_value {
     (($value:ident, $set:expr) => $ident:ident) => {
@@ -1085,41 +1293,64 @@ macro_rules! exclusive_value {
         } else {
             $ident
         }
     }
 }
 
 <%helpers:longhand name="font-variant-east-asian" products="gecko" animation_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian">
+    use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub flags SpecifiedValue: u16 {
+        pub flags VariantEastAsian: u16 {
             const NORMAL = 0,
             const JIS78 = 0x01,
             const JIS83 = 0x02,
             const JIS90 = 0x04,
             const JIS04 = 0x08,
             const SIMPLIFIED = 0x10,
             const TRADITIONAL = 0x20,
             const FULL_WIDTH = 0x40,
             const PROPORTIONAL_WIDTH = 0x80,
             const RUBY = 0x100,
         }
     }
 
-    impl ToCss for SpecifiedValue {
+
+    #[derive(Debug, Clone, PartialEq)]
+    pub enum SpecifiedValue {
+        Value(VariantEastAsian),
+        System(SystemFont)
+    }
+
+    <%self:simple_system_boilerplate name="font_variant_east_asian"></%self:simple_system_boilerplate>
+
+    //                                 servo_bit: gecko_bit
+    <% font_variant_east_asian_map = { "JIS78": "JIS78",
+                                       "JIS83": "JIS83",
+                                       "JIS90": "JIS90",
+                                       "JIS04": "JIS04",
+                                       "SIMPLIFIED": "SIMPLIFIED",
+                                       "TRADITIONAL": "TRADITIONAL",
+                                       "FULL_WIDTH": "FULL_WIDTH",
+                                       "PROPORTIONAL_WIDTH": "PROP_WIDTH",
+                                       "RUBY": "RUBY" } %>
+
+    ${helpers.gecko_bitflags_conversion(font_variant_east_asian_map, 'NS_FONT_VARIANT_EAST_ASIAN_',
+                                        'VariantEastAsian', kw_type='u16')}
+
+
+    impl ToCss for VariantEastAsian {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.is_empty() {
                 return dest.write_str("normal")
             }
 
             let mut has_any = false;
 
             macro_rules! write_value {
@@ -1145,37 +1376,37 @@ macro_rules! exclusive_value {
             write_value!(RUBY => "ruby");
 
             debug_assert!(has_any);
             Ok(())
         }
     }
 
     pub mod computed_value {
-        pub type T = super::SpecifiedValue;
+        pub type T = super::VariantEastAsian;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::empty()
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::empty()
+        SpecifiedValue::Value(VariantEastAsian::empty())
     }
 
     /// normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]
     /// <east-asian-variant-values> = [ jis78 | jis83 | jis90 | jis04 | simplified | traditional ]
     /// <east-asian-width-values>   = [ full-width | proportional-width ]
     <% east_asian_variant_values = "JIS78 | JIS83 | JIS90 | JIS04 | SIMPLIFIED | TRADITIONAL" %>
     <% east_asian_width_values = "FULL_WIDTH | PROPORTIONAL_WIDTH" %>
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut result = SpecifiedValue::empty();
+        let mut result = VariantEastAsian::empty();
 
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
-            return Ok(result)
+            return Ok(SpecifiedValue::Value(result))
         }
 
         while let Ok(ident) = input.try(|input| input.expect_ident()) {
             let flag = match_ignore_ascii_case! { &ident,
                 "jis78" =>
                     exclusive_value!((result, ${east_asian_variant_values}) => JIS78),
                 "jis83" =>
                     exclusive_value!((result, ${east_asian_variant_values}) => JIS83),
@@ -1194,50 +1425,72 @@ macro_rules! exclusive_value {
                 "ruby" =>
                     exclusive_value!((result, RUBY) => RUBY),
                 _ => return Err(()),
             };
             result.insert(flag);
         }
 
         if !result.is_empty() {
-            Ok(result)
+            Ok(SpecifiedValue::Value(result))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-ligatures" products="gecko" animation_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures">
+    use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub flags SpecifiedValue: u16 {
+        pub flags VariantLigatures: u16 {
             const NORMAL = 0,
             const NONE = 0x01,
             const COMMON_LIGATURES = 0x02,
             const NO_COMMON_LIGATURES = 0x04,
             const DISCRETIONARY_LIGATURES = 0x08,
             const NO_DISCRETIONARY_LIGATURES = 0x10,
             const HISTORICAL_LIGATURES = 0x20,
             const NO_HISTORICAL_LIGATURES = 0x40,
             const CONTEXTUAL = 0x80,
             const NO_CONTEXTUAL = 0x100,
         }
     }
 
-    impl ToCss for SpecifiedValue {
+
+    #[derive(Debug, Clone, PartialEq)]
+    pub enum SpecifiedValue {
+        Value(VariantLigatures),
+        System(SystemFont)
+    }
+
+    <%self:simple_system_boilerplate name="font_variant_ligatures"></%self:simple_system_boilerplate>
+
+    //                                 servo_bit: gecko_bit
+    <% font_variant_ligatures_map = { "NONE": "NONE",
+                                      "COMMON_LIGATURES": "COMMON",
+                                      "NO_COMMON_LIGATURES": "NO_COMMON",
+                                      "DISCRETIONARY_LIGATURES": "DISCRETIONARY",
+                                      "NO_DISCRETIONARY_LIGATURES": "NO_DISCRETIONARY",
+                                      "HISTORICAL_LIGATURES": "HISTORICAL",
+                                      "NO_HISTORICAL_LIGATURES": "NO_HISTORICAL",
+                                      "CONTEXTUAL": "CONTEXTUAL",
+                                      "NO_CONTEXTUAL": "NO_CONTEXTUAL" } %>
+
+    ${helpers.gecko_bitflags_conversion(font_variant_ligatures_map, 'NS_FONT_VARIANT_LIGATURES_',
+                                        'VariantLigatures', kw_type='u16')}
+
+    impl ToCss for VariantLigatures {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.is_empty() {
                 return dest.write_str("normal")
             }
             if self.contains(NONE) {
                 return dest.write_str("none")
             }
 
@@ -1265,48 +1518,48 @@ macro_rules! exclusive_value {
             write_value!(NO_CONTEXTUAL => "no-contextual");
 
             debug_assert!(has_any);
             Ok(())
         }
     }
 
     pub mod computed_value {
-        pub type T = super::SpecifiedValue;
+        pub type T = super::VariantLigatures;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::empty()
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::empty()
+        SpecifiedValue::Value(VariantLigatures::empty())
     }
 
     /// normal | none |
     /// [ <common-lig-values> ||
     ///   <discretionary-lig-values> ||
     ///   <historical-lig-values> ||
     ///   <contextual-alt-values> ]
     /// <common-lig-values>        = [ common-ligatures | no-common-ligatures ]
     /// <discretionary-lig-values> = [ discretionary-ligatures | no-discretionary-ligatures ]
     /// <historical-lig-values>    = [ historical-ligatures | no-historical-ligatures ]
     /// <contextual-alt-values>    = [ contextual | no-contextual ]
     <% common_lig_values = "COMMON_LIGATURES | NO_COMMON_LIGATURES" %>
     <% discretionary_lig_values = "DISCRETIONARY_LIGATURES | NO_DISCRETIONARY_LIGATURES" %>
     <% historical_lig_values = "HISTORICAL_LIGATURES | NO_HISTORICAL_LIGATURES" %>
     <% contextual_alt_values = "CONTEXTUAL | NO_CONTEXTUAL" %>
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut result = SpecifiedValue::empty();
+        let mut result = VariantLigatures::empty();
 
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
-            return Ok(result)
+            return Ok(SpecifiedValue::Value(result))
         }
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
-            return Ok(NONE)
+            return Ok(SpecifiedValue::Value(NONE))
         }
 
         while let Ok(ident) = input.try(|input| input.expect_ident()) {
             let flag = match_ignore_ascii_case! { &ident,
                 "common-ligatures" =>
                     exclusive_value!((result, ${common_lig_values}) => COMMON_LIGATURES),
                 "no-common-ligatures" =>
                     exclusive_value!((result, ${common_lig_values}) => NO_COMMON_LIGATURES),
@@ -1323,49 +1576,72 @@ macro_rules! exclusive_value {
                 "no-contextual" =>
                     exclusive_value!((result, ${contextual_alt_values}) => NO_CONTEXTUAL),
                 _ => return Err(()),
             };
             result.insert(flag);
         }
 
         if !result.is_empty() {
-            Ok(result)
+            Ok(SpecifiedValue::Value(result))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-numeric" products="gecko" animation_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric">
+    use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
-    use values::computed::ComputedValueAsSpecified;
 
-    impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub flags SpecifiedValue: u8 {
+        pub flags VariantNumeric: u8 {
             const NORMAL = 0,
             const LINING_NUMS = 0x01,
             const OLDSTYLE_NUMS = 0x02,
             const PROPORTIONAL_NUMS = 0x04,
             const TABULAR_NUMS = 0x08,
             const DIAGONAL_FRACTIONS = 0x10,
             const STACKED_FRACTIONS = 0x20,
             const SLASHED_ZERO = 0x40,
             const ORDINAL = 0x80,
         }
     }
 
-    impl ToCss for SpecifiedValue {
+
+
+    #[derive(Debug, Clone, PartialEq)]
+    pub enum SpecifiedValue {
+        Value(VariantNumeric),
+        System(SystemFont)
+    }
+
+    <%self:simple_system_boilerplate name="font_variant_numeric"></%self:simple_system_boilerplate>
+
+
+    //                              servo_bit: gecko_bit
+    <% font_variant_numeric_map = { "LINING_NUMS": "LINING",
+                                    "OLDSTYLE_NUMS": "OLDSTYLE",
+                                    "PROPORTIONAL_NUMS": "PROPORTIONAL",
+                                    "TABULAR_NUMS": "TABULAR",
+                                    "DIAGONAL_FRACTIONS": "DIAGONAL_FRACTIONS",
+                                    "STACKED_FRACTIONS": "STACKED_FRACTIONS",
+                                    "SLASHED_ZERO": "SLASHZERO",
+                                    "ORDINAL": "ORDINAL" } %>
+
+    ${helpers.gecko_bitflags_conversion(font_variant_numeric_map, 'NS_FONT_VARIANT_NUMERIC_',
+                                        'VariantNumeric')}
+
+    impl ToCss for VariantNumeric {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             if self.is_empty() {
                 return dest.write_str("normal")
             }
 
             let mut has_any = false;
 
             macro_rules! write_value {
@@ -1390,44 +1666,44 @@ macro_rules! exclusive_value {
             write_value!(ORDINAL => "ordinal");
 
             debug_assert!(has_any);
             Ok(())
         }
     }
 
     pub mod computed_value {
-        pub type T = super::SpecifiedValue;
+        pub type T = super::VariantNumeric;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::empty()
     }
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
-        SpecifiedValue::empty()
+        SpecifiedValue::Value(VariantNumeric::empty())
     }
 
     /// normal |
     ///  [ <numeric-figure-values>   ||
     ///    <numeric-spacing-values>  ||
     ///    <numeric-fraction-values> ||
     ///    ordinal                   ||
     ///    slashed-zero ]
     /// <numeric-figure-values>   = [ lining-nums | oldstyle-nums ]
     /// <numeric-spacing-values>  = [ proportional-nums | tabular-nums ]
     /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
     <% numeric_figure_values = "LINING_NUMS | OLDSTYLE_NUMS" %>
     <% numeric_spacing_values = "PROPORTIONAL_NUMS | TABULAR_NUMS" %>
     <% numeric_fraction_values = "DIAGONAL_FRACTIONS | STACKED_FRACTIONS" %>
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        let mut result = SpecifiedValue::empty();
+        let mut result = VariantNumeric::empty();
 
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
-            return Ok(result)
+            return Ok(SpecifiedValue::Value(result))
         }
 
         while let Ok(ident) = input.try(|input| input.expect_ident()) {
             let flag = match_ignore_ascii_case! { &ident,
                 "ordinal" =>
                     exclusive_value!((result, ORDINAL) => ORDINAL),
                 "slashed-zero" =>
                     exclusive_value!((result, SLASHED_ZERO) => SLASHED_ZERO),
@@ -1444,30 +1720,30 @@ macro_rules! exclusive_value {
                 "stacked-fractions" =>
                     exclusive_value!((result, ${numeric_fraction_values}) => STACKED_FRACTIONS ),
                 _ => return Err(()),
             };
             result.insert(flag);
         }
 
         if !result.is_empty() {
-            Ok(result)
+            Ok(SpecifiedValue::Value(result))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
-${helpers.single_keyword("font-variant-position",
-                         "normal sub super",
-                         products="gecko",
-                         gecko_ffi_name="mFont.variantPosition",
-                         gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
-                         spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
-                         animation_type="none")}
+${helpers.single_keyword_system("font-variant-position",
+                                "normal sub super",
+                                products="gecko",
+                                gecko_ffi_name="mFont.variantPosition",
+                                gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
+                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
+                                animation_type="none")}
 
 <%helpers:longhand name="font-feature-settings" products="none" animation_type="none" extra_prefixes="moz"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
     pub use self::computed_value::T as SpecifiedValue;
@@ -1569,38 +1845,55 @@ macro_rules! exclusive_value {
             Ok(computed_value::T::Normal)
         } else {
             input.parse_comma_separated(|i| computed_value::FeatureTagValue::parse(context, i))
                  .map(computed_value::T::Tag)
         }
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="font-language-override" products="gecko" animation_type="none" extra_prefixes="moz"
+<%helpers:longhand name="font-language-override" products="gecko" animation_type="none"
+                   extra_prefixes="moz" boxed="True"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
+    use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use byteorder::{BigEndian, ByteOrder};
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq, Eq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Normal,
         Override(String),
+        System(SystemFont)
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             use cssparser;
             match *self {
                 SpecifiedValue::Normal => dest.write_str("normal"),
                 SpecifiedValue::Override(ref lang) =>
                     cssparser::serialize_string(lang, dest),
+                SpecifiedValue::System(_) => Ok(())
+            }
+        }
+    }
+
+    impl SpecifiedValue {
+        pub fn system_font(f: SystemFont) -> Self {
+            SpecifiedValue::System(f)
+        }
+        pub fn get_system(&self) -> Option<SystemFont> {
+            if let SpecifiedValue::System(s) = *self {
+                Some(s)
+            } else {
+                None
             }
         }
     }
 
     pub mod computed_value {
         use std::{fmt, str};
         use style_traits::ToCss;
         use byteorder::{BigEndian, ByteOrder};
@@ -1641,31 +1934,36 @@ macro_rules! exclusive_value {
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::Normal
     }
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
-        fn to_computed_value(&self, _: &Context) -> computed_value::T {
+        fn to_computed_value(&self, _context: &Context) -> computed_value::T {
             use std::ascii::AsciiExt;
             match *self {
                 SpecifiedValue::Normal => computed_value::T(0),
                 SpecifiedValue::Override(ref lang) => {
                     if lang.is_empty() || lang.len() > 4 || !lang.is_ascii() {
                         return computed_value::T(0)
                     }
                     let mut computed_lang = lang.clone();
                     while computed_lang.len() < 4 {
                         computed_lang.push(' ');
                     }
                     let bytes = computed_lang.into_bytes();
                     computed_value::T(BigEndian::read_u32(&bytes))
                 }
+                SpecifiedValue::System(_) => {
+                    <%self:nongecko_unreachable>
+                        _context.style.cached_system_font.as_ref().unwrap().font_language_override
+                    </%self:nongecko_unreachable>
+                }
             }
         }
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             if computed.0 == 0 {
                 return SpecifiedValue::Normal
             }
             let mut buf = [0; 4];
@@ -1875,8 +2173,190 @@ macro_rules! exclusive_value {
     }
 
     pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<SpecifiedValue, ()> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
         Err(())
     }
 </%helpers:longhand>
 
+
+% if product == "gecko":
+    pub mod system_font {
+        //! We deal with system fonts here
+        //!
+        //! System fonts can only be set as a group via the font shorthand.
+        //! They resolve at compute time (not parse time -- this lets the
+        //! browser respond to changes to the OS font settings).
+        //!
+        //! While Gecko handles these as a separate property and keyword
+        //! values on each property indicating that the font should be picked
+        //! from the -x-system-font property, we avoid this. Instead,
+        //! each font longhand has a special SystemFont variant which contains
+        //! the specified system font. When the cascade function (in helpers)
+        //! detects that a value has a system font, it will resolve it, and
+        //! cache it on the ComputedValues. After this, it can be just fetched
+        //! whenever a font longhand on the same element needs the system font.
+
+        use app_units::Au;
+        use cssparser::Parser;
+        use properties::longhands;
+        use std::fmt;
+        use std::hash::{Hash, Hasher};
+        use style_traits::ToCss;
+        use values::computed::{ToComputedValue, Context};
+        <%
+            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_alternates
+                               font_variant_ligatures font_variant_east_asian
+                               font_variant_numeric""".split()
+            kw_cast = """font_style font_variant_caps font_stretch
+                         font_kerning font_variant_position""".split()
+        %>
+        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+        pub enum SystemFont {
+            % for font in system_fonts:
+                ${to_camel_case(font)},
+            % endfor
+        }
+
+        impl ToCss for SystemFont {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+                dest.write_str(match *self {
+                    % for font in system_fonts:
+                        SystemFont::${to_camel_case(font)} => "${font}",
+                    % endfor
+                })
+            }
+        }
+
+        // ComputedValues are compared at times
+        // so we need these impls. We don't want to
+        // add Eq to Number (which contains a float)
+        // so instead we have an eq impl which skips the
+        // cached values
+        impl PartialEq for ComputedSystemFont {
+            fn eq(&self, other: &Self) -> bool {
+                self.system_font == other.system_font
+            }
+        }
+        impl Eq for ComputedSystemFont {}
+
+        impl Hash for ComputedSystemFont {
+            fn hash<H: Hasher>(&self, hasher: &mut H) {
+                self.system_font.hash(hasher)
+            }
+        }
+
+        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;
+
+                let id = match *self {
+                    % for font in system_fonts:
+                        SystemFont::${to_camel_case(font)} => {
+                            LookAndFeel_FontID::eFont_${to_camel_case(font.replace("-moz-", ""))}
+                        }
+                    % endfor
+                };
+
+                let mut system: nsFont = unsafe { mem::uninitialized() };
+                unsafe {
+                    bindings::Gecko_nsFont_InitSystem(&mut system, id as i32,
+                                            cx.style.get_font().gecko(),
+                                            &*cx.device.pres_context)
+                }
+                let family = system.fontlist.mFontlist.iter().map(|font| {
+                    use properties::longhands::font_family::computed_value::*;
+                    FontFamily::FamilyName(FamilyName {
+                        name: (&*font.mName).into(),
+                        quoted: true
+                    })
+                }).collect::<Vec<_>>();
+                let weight = unsafe {
+                    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_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
+                            % endif
+                        ),
+                    % endfor
+                    font_language_override: longhands::font_language_override::computed_value
+                                                     ::T(system.languageOverride),
+                    system_font: *self,
+                };
+                unsafe { bindings::Gecko_nsFont_Destroy(&mut system); }
+                ret
+            }
+
+            fn from_computed_value(_: &ComputedSystemFont) -> Self {
+                unreachable!()
+            }
+        }
+
+        #[inline]
+        /// Compute and cache a system font
+        ///
+        /// Must be called before attempting to compute a system font
+        /// specified value
+        pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
+            if context.style.cached_system_font.is_none() {
+                let computed = system.to_computed_value(context);
+                context.style.cached_system_font = Some(computed);
+            }
+            debug_assert!(system == context.style.cached_system_font.as_ref().unwrap().system_font)
+        }
+
+        #[derive(Clone, Debug)]
+        pub struct ComputedSystemFont {
+            % for name in SYSTEM_FONT_LONGHANDS:
+                pub ${name}: longhands::${name}::computed_value::T,
+            % endfor
+            pub system_font: SystemFont,
+        }
+
+        impl SystemFont {
+            pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+                Ok(match_ignore_ascii_case! { &*input.expect_ident()?,
+                    % for font in system_fonts:
+                        "${font}" => SystemFont::${to_camel_case(font)},
+                    % endfor
+                    _ => return Err(())
+                })
+            }
+        }
+    }
+% else:
+    pub mod system_font {
+        use cssparser::Parser;
+
+        // We don't parse system fonts, but in the interest of not littering
+        // a lot of code with `if product == gecko` conditionals, we have a
+        // dummy system font module that does nothing
+
+        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        /// void enum for system font, can never exist
+        pub enum SystemFont {}
+        impl SystemFont {
+            pub fn parse(_: &mut Parser) -> Result<Self, ()> {
+                Err(())
+            }
+        }
+    }
+% endif
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -1,48 +1,62 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
+<% from data import SYSTEM_FONT_LONGHANDS %>
 
 <%helpers:shorthand name="font"
                     sub_properties="font-style font-variant-caps font-weight font-stretch
                                     font-size line-height font-family
                                     ${'font-size-adjust' if product == 'gecko' or data.testing else ''}
                                     ${'font-kerning' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-alternates' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-east-asian' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-ligatures' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-numeric' if product == 'gecko' or data.testing else ''}
                                     ${'font-variant-position' if product == 'gecko' or data.testing else ''}
                                     ${'font-language-override' if product == 'gecko' or data.testing else ''}"
                     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
-    use properties::longhands::{font_style, font_variant_caps, font_weight, font_stretch};
-    use properties::longhands::{font_size, line_height};
+    use properties::longhands::{font_family, font_style, font_weight, font_stretch};
+    use properties::longhands::{font_size, line_height, font_variant_caps};
+    #[cfg(feature = "gecko")]
+    use properties::longhands::system_font::SystemFont;
     <%
         gecko_sub_properties = "kerning language_override size_adjust \
                                 variant_alternates variant_east_asian \
                                 variant_ligatures variant_numeric \
                                 variant_position".split()
     %>
     % if product == "gecko" or data.testing:
         % for prop in gecko_sub_properties:
             use properties::longhands::font_${prop};
         % endfor
     % endif
-    use properties::longhands::font_family::SpecifiedValue as FontFamily;
+    use self::font_family::SpecifiedValue as FontFamily;
 
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
         let mut nb_normals = 0;
         let mut style = None;
         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(Longhands {
+                     % for name in SYSTEM_FONT_LONGHANDS:
+                         ${name}: ${name}::SpecifiedValue::system_font(sys),
+                     % endfor
+                     // line-height is just reset to initial
+                     line_height: line_height::get_initial_specified_value(),
+                 })
+            }
+        % endif
         loop {
             // Special-case 'normal' because it is valid in each of
             // font-style, font-weight, font-variant and font-stretch.
             // Leaves the values to None, 'normal' is the initial value for each of them.
             if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
                 nb_normals += 1;
                 continue;
             }
@@ -83,32 +97,74 @@
         }
         let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
             Some(try!(line_height::parse(context, input)))
         } else {
             None
         };
         let family = FontFamily::parse(input)?;
         Ok(Longhands {
-            % for name in "style variant_caps weight stretch size".split():
+            % for name in "style weight stretch size variant_caps".split():
                 font_${name}: unwrap_or_initial!(font_${name}, ${name}),
             % endfor
             line_height: unwrap_or_initial!(line_height),
             font_family: family,
             % if product == "gecko" or data.testing:
                 % for name in gecko_sub_properties:
                     font_${name}: font_${name}::get_initial_specified_value(),
                 % endfor
             % endif
         })
     }
 
+    % if product == "gecko":
+        enum CheckSystemResult {
+            AllSystem(SystemFont),
+            SomeSystem,
+            None
+        }
+
+        impl<'a> LonghandsToSerialize<'a> {
+            /// Check if some or all members are system fonts
+            fn check_system(&self) -> CheckSystemResult {
+                let mut sys = None;
+                let mut all = true;
+
+                % for prop in SYSTEM_FONT_LONGHANDS:
+                    if let Some(s) = self.${prop}.get_system() {
+                        debug_assert!(sys.is_none() || s == sys.unwrap());
+                        sys = Some(s);
+                    } else {
+                        all = false;
+                    }
+                % endfor
+                if self.line_height != &line_height::get_initial_specified_value() {
+                    all = false
+                }
+                if all {
+                    CheckSystemResult::AllSystem(sys.unwrap())
+                } else if sys.is_some() {
+                    CheckSystemResult::SomeSystem
+                } else {
+                    CheckSystemResult::None
+                }
+            }
+        }
+    % endif
+
     // This may be a bit off, unsure, possibly needs changes
     impl<'a> ToCss for LonghandsToSerialize<'a>  {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            % if product == "gecko":
+                match self.check_system() {
+                    CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
+                    CheckSystemResult::SomeSystem => return Ok(()),
+                    CheckSystemResult::None => ()
+                }
+            % endif
 
     % if product == "gecko" or data.testing:
         % for name in gecko_sub_properties:
             if self.font_${name} != &font_${name}::get_initial_specified_value() {
                 return Ok(());
             }
         % endfor
     % endif
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1388,17 +1388,17 @@ pub extern "C" fn Servo_DeclarationBlock
         VerticalAlign => longhands::vertical_align::SpecifiedValue::from_gecko_keyword(value),
         TextAlign => longhands::text_align::SpecifiedValue::from_gecko_keyword(value),
         TextEmphasisPosition => longhands::text_emphasis_position::SpecifiedValue::from_gecko_keyword(value),
         Clear => longhands::clear::SpecifiedValue::from_gecko_keyword(value),
         FontSize => {
             // We rely on Gecko passing in font-size values (0...7) here.
             longhands::font_size::SpecifiedValue::from_html_size(value as u8)
         },
-        FontStyle => longhands::font_style::SpecifiedValue::from_gecko_keyword(value),
+        FontStyle => longhands::font_style::computed_value::T::from_gecko_keyword(value).into(),
         FontWeight => longhands::font_weight::SpecifiedValue::from_gecko_keyword(value),
         ListStyleType => longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value),
         MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
         WhiteSpace => longhands::white_space::SpecifiedValue::from_gecko_keyword(value),
         CaptionSide => longhands::caption_side::SpecifiedValue::from_gecko_keyword(value),
         BorderTopStyle => BorderStyle::from_gecko_keyword(value),
         BorderRightStyle => BorderStyle::from_gecko_keyword(value),
         BorderBottomStyle => BorderStyle::from_gecko_keyword(value),
@@ -1626,17 +1626,17 @@ pub extern "C" fn Servo_DeclarationBlock
     use cssparser::Parser;
     use style::properties::PropertyDeclaration;
     use style::properties::longhands::font_family::SpecifiedValue as FontFamily;
 
     let string = unsafe { (*value).to_string() };
     let mut parser = Parser::new(&string);
     if let Ok(family) = FontFamily::parse(&mut parser) {
         if parser.is_exhausted() {
-            let decl = PropertyDeclaration::FontFamily(family);
+            let decl = PropertyDeclaration::FontFamily(Box::new(family));
             write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
                 decls.push(decl, Importance::Normal);
             })
         }
     }
 }
 
 #[no_mangle]
@@ -1923,17 +1923,17 @@ pub extern "C" fn Servo_GetComputedKeyfr
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
 
     let style = ComputedValues::as_arc(&style);
     let parent_style = parent_style.as_ref().map(|r| &**ComputedValues::as_arc(&r));
 
     let default_values = data.default_computed_values();
     let metrics = get_metrics_provider_for_product();
 
-    let context = Context {
+    let mut context = Context {
         is_root_element: false,
         device: &data.stylist.device,
         inherited_style: parent_style.unwrap_or(default_values),
         layout_parent_style: parent_style.unwrap_or(default_values),
         style: (**style).clone(),
         font_metrics_provider: &metrics,
         in_media_query: false,
     };
@@ -1951,17 +1951,18 @@ pub extern "C" fn Servo_GetComputedKeyfr
             let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
             let guard = declarations.read_with(&guard);
 
             let anim_iter = guard.declarations()
                             .iter()
                             .filter_map(|&(ref decl, imp)| {
                                 if imp == Importance::Normal {
                                     let property = TransitionProperty::from_declaration(decl);
-                                    let animation = AnimationValue::from_declaration(decl, &context, default_values);
+                                    let animation = AnimationValue::from_declaration(decl, &mut context,
+                                                                                     default_values);
                                     debug_assert!(property.is_none() == animation.is_none(),
                                                   "The failure condition of TransitionProperty::from_declaration \
                                                    and AnimationValue::from_declaration should be the same");
                                     // Skip the property if either ::from_declaration fails.
                                     if property.is_none() || animation.is_none() {
                                         None
                                     } else {
                                         Some((property.unwrap(), animation.unwrap()))