Bug 1349417 - Part 7: stylo: System font support for bitflag properties and font-language-override; r?xidorn draft
authorManish Goregaokar <manishearth@gmail.com>
Tue, 21 Mar 2017 20:38:12 -0700
changeset 564174 b7412c05d863397487fb748cd2514658db925750
parent 564173 acd8ac848e495b529bcb9f7c8855f4c79bd7f339
child 564175 908b317f9207890fa5ff9ba11406913239cca43f
push id54548
push userbmo:manishearth@gmail.com
push dateTue, 18 Apr 2017 10:18:30 +0000
reviewersxidorn
bugs1349417
milestone55.0a1
Bug 1349417 - Part 7: stylo: System font support for bitflag properties and font-language-override; r?xidorn
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -10,17 +10,20 @@ 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""".split()
+                           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
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -292,37 +292,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)
         },
@@ -1364,17 +1343,20 @@ fn static_assert() {
     }
 
     pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) {
         self.gecko.mFont.weight = v as u16;
     }
     ${impl_simple_copy('font_weight', 'mFont.weight')}
 
     pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
-        unsafe { longhands::font_weight::computed_value::T::from_gecko_weight(self.gecko.mFont.weight) }
+        debug_assert!(self.gecko.mFont.weight >= 100);
+        debug_assert!(self.gecko.mFont.weight <= 900);
+        debug_assert!(self.gecko.mFont.weight % 10 == 0);
+        unsafe { transmute(self.gecko.mFont.weight) }
     }
 
     pub fn set_font_synthesis(&mut self, v: longhands::font_synthesis::computed_value::T) {
         use gecko_bindings::structs::{NS_FONT_SYNTHESIS_WEIGHT, NS_FONT_SYNTHESIS_STYLE};
 
         self.gecko.mFont.synthesis = 0;
         if v.weight {
             self.gecko.mFont.synthesis |= NS_FONT_SYNTHESIS_WEIGHT as u8;
@@ -1398,17 +1380,20 @@ fn static_assert() {
 
     pub fn copy_font_size_adjust_from(&mut self, other: &Self) {
         self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust;
     }
 
     pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T {
         use properties::longhands::font_size_adjust::computed_value::T;
 
-        T::from_gecko_adjust(self.gecko.mFont.sizeAdjust)
+        match self.gecko.mFont.sizeAdjust {
+            -1.0 => T::None,
+            _ => T::Number(self.gecko.mFont.sizeAdjust),
+        }
     }
 
     #[allow(non_snake_case)]
     pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
         let ptr = v.0.as_ptr();
         forget(v);
         unsafe {
             Gecko_nsStyleFont_SetLang(&mut self.gecko, ptr);
@@ -1422,84 +1407,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
@@ -537,16 +537,54 @@
                     ${to_rust_ident(value).upper()} => ${type}::${to_rust_ident(value)},
                 % endfor
                 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};
+                }
+            % 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};
+                }
+            % 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 [
             'gecko_constant_prefix', 'gecko_enum_prefix',
             'extra_gecko_values', 'extra_servo_values',
             'custom_consts', 'gecko_inexhaustive',
         ]}
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -11,16 +11,62 @@
 <%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"
                    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;
@@ -1031,39 +1077,56 @@
                                 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 {
@@ -1087,40 +1150,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,
@@ -1131,17 +1194,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) => {
@@ -1150,41 +1213,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 {
@@ -1210,37 +1296,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),
@@ -1259,50 +1345,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")
             }
 
@@ -1330,48 +1438,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),
@@ -1388,49 +1496,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 {
@@ -1455,44 +1586,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),
@@ -1509,17 +1640,17 @@ 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_system("font-variant-position",
                                 "normal sub super",
@@ -1636,36 +1767,52 @@ macro_rules! exclusive_value {
             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"
                    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};
@@ -1706,31 +1853,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];
@@ -1954,17 +2106,21 @@ macro_rules! exclusive_value {
         use std::hash::{Hash, Hasher};
         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""".split()
+                               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
         }
 
@@ -2020,19 +2176,23 @@ macro_rules! exclusive_value {
                 };
                 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_', ''))} as u32
+                            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!()
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -41,21 +41,17 @@
         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
-                     % for name in gecko_sub_properties + "variant_caps stretch".split():
-                        % if "font_" + name not in SYSTEM_FONT_LONGHANDS:
-                            font_${name}: font_${name}::get_initial_specified_value(),
-                        % endif
-                     % 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.