Bug 1349417 - Part 3: stylo: System font support for keyword font longhands; r?xidorn draft
authorManish Goregaokar <manishearth@gmail.com>
Tue, 21 Mar 2017 20:38:12 -0700
changeset 564171 960d40b060d8d8b00cb43f01a440123c4951289b
parent 562841 0e0148a96e7ed644039c7565b06ce0de1e7819ee
child 564172 feb1cd187b7d8af415057ed36ac3971178a4c4bd
push id54548
push userbmo:manishearth@gmail.com
push dateTue, 18 Apr 2017 10:18:30 +0000
reviewersxidorn
bugs1349417
milestone55.0a1
Bug 1349417 - Part 3: stylo: System font support for keyword font longhands; r?xidorn MozReview-Commit-ID: 7p3gtzw540J
servo/components/style/properties/data.py
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/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -8,16 +8,19 @@ 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""".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
 
@@ -27,16 +30,20 @@ def to_rust_ident(name):
     if name in ["static", "super", "box", "move"]:  # Rust keywords
         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:]
+
 
 class Keyword(object):
     def __init__(self, name, values, gecko_constant_prefix=None,
                  gecko_enum_prefix=None, custom_consts=None,
                  extra_gecko_values=None, extra_servo_values=None,
                  gecko_strip_moz_prefix=True,
                  gecko_inexhaustive=None):
         self.name = name
--- 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;
@@ -194,17 +197,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} {
@@ -260,17 +262,17 @@
                                                              &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 "font_size font_family".split() and product == "gecko":
+                                % 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":
                                     if let longhands::font_size::SpecifiedValue::Keyword(kw, fraction)
                                                         = **specified_value {
@@ -393,57 +395,154 @@
                         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)">
         use values::computed::ComputedValueAsSpecified;
         use values::HasViewportPercentage;
         impl ComputedValueAsSpecified for SpecifiedValue {}
         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),
-                }
-            % 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),
-                }
-            % endif
+                x => panic!("Found unexpected value in style struct for ${keyword.name} property: {:?}", x),
+            }
         }
     }
 </%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 BackgroundSize;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
@@ -349,21 +351,21 @@ impl AnimationValue {
         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 "font_size font_family".split() and product == "gecko":
-                    if let Some(sf) = val.get_system() {
-                        longhands::system_font::resolve_system_font(sf, context);
-                    }
-                % endif
+            % 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,14 +1,14 @@
 /* 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, to_camel_case, to_rust_ident %>
+<% from data import Method, to_camel_case, to_rust_ident, to_camel_case_lower, SYSTEM_FONT_LONGHANDS %>
 
 <% data.new_style_struct("Font",
                          inherited=True) %>
 
 <%def name="nongecko_unreachable()">
     %if product == "gecko":
         ${caller.body()}
     %else:
@@ -277,40 +277,38 @@
                 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;
 
     no_viewport_percentage!(SpecifiedValue);
@@ -949,33 +947,33 @@
                 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 std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
@@ -1456,23 +1454,23 @@ macro_rules! exclusive_value {
         if !result.is_empty() {
             Ok(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;
@@ -1892,16 +1890,18 @@ macro_rules! exclusive_value {
         use cssparser::Parser;
         use properties::longhands;
         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()
         %>
         #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
         pub enum SystemFont {
             % for font in system_fonts:
                 ${to_camel_case(font)},
             % endfor
         }
 
@@ -1932,16 +1932,21 @@ macro_rules! exclusive_value {
                     FontFamily::FamilyName(FamilyName {
                         name: (&*font.mName).into(),
                         quoted: true
                     })
                 }).collect::<Vec<_>>();
                 let ret = ComputedSystemFont {
                     font_family: longhands::font_family::computed_value::T(family),
                     font_size: Au(system.size),
+                    % 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
+                        ),
+                    % endfor
                     system_font: *self,
                 };
                 unsafe { bindings::Gecko_nsFont_Destroy(&mut system); }
                 ret
             }
 
             fn from_computed_value(_: &ComputedSystemFont) -> Self {
                 unreachable!()
@@ -1958,18 +1963,19 @@ macro_rules! exclusive_value {
                 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, PartialEq, Eq, Hash)]
         pub struct ComputedSystemFont {
-            pub font_family: longhands::font_family::computed_value::T,
-            pub font_size: longhands::font_size::computed_value::T,
+            % 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)},
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -1,13 +1,14 @@
 /* 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 ''}
@@ -37,21 +38,23 @@
         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 "family size".split():
-                         font_${name}: font_${name}::SpecifiedValue::system_font(sys),
+                     % for name in SYSTEM_FONT_LONGHANDS:
+                         ${name}: ${name}::SpecifiedValue::system_font(sys),
                      % endfor
-                     % for name in "style weight stretch variant_caps".split() + gecko_sub_properties:
-                        font_${name}: font_${name}::get_initial_specified_value(),
+                     % for name in gecko_sub_properties + "weight variant_caps stretch".split():
+                        % if "font_" + name not in SYSTEM_FONT_LONGHANDS:
+                            font_${name}: font_${name}::get_initial_specified_value(),
+                        % endif
                      % endfor
                      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.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1374,17 +1374,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),