servo: Merge #15644 - stylo: Support all presentation attributes (from Manishearth:stylo-abstract); r=emilio,bz
authorNazım Can Altınova <canaltinova@gmail.com>
Sat, 18 Feb 2017 20:43:33 -0800
changeset 372744 fcccffa68c9df87a4c35cc8232cd01b2f2c2fb07
parent 372743 c73bba5d7bea1149ad0d6d797def73ebf37bbd43
child 372745 7943ca5e483ab2865df343e0529d5217c3aced1b
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio, bz
milestone54.0a1
servo: Merge #15644 - stylo: Support all presentation attributes (from Manishearth:stylo-abstract); r=emilio,bz From https://bugzilla.mozilla.org/show_bug.cgi?id=1338936 r=emilio,bz Source-Repo: https://github.com/servo/servo Source-Revision: 4d82d4e68e1f1954f4fbdd823a92c8b533a49022
servo/components/style/build_gecko.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/properties/data.py
servo/components/style/properties/declaration_block.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/components/style/properties/longhand/table.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
servo/components/style/values/specified/length.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -497,17 +497,17 @@ mod bindings {
             .disable_name_namespacing()
             .with_codegen_config(CodegenConfig {
                 functions: true,
                 ..CodegenConfig::nothing()
             })
             .header(add_include("mozilla/ServoBindings.h"))
             .hide_type("nsACString_internal")
             .hide_type("nsAString_internal")
-            .raw_line("pub use nsstring::{nsACString, nsAString};")
+            .raw_line("pub use nsstring::{nsACString, nsAString, nsString};")
             .raw_line("type nsACString_internal = nsACString;")
             .raw_line("type nsAString_internal = nsAString;")
             .whitelisted_function("Servo_.*")
             .whitelisted_function("Gecko_.*");
         let structs_types = [
             "mozilla::css::URLValue",
             "RawGeckoDocument",
             "RawGeckoElement",
@@ -579,16 +579,17 @@ mod bindings {
             "nsStyleUIReset",
             "nsStyleUnion",
             "nsStyleUnit",
             "nsStyleUserInterface",
             "nsStyleVariables",
             "nsStyleVisibility",
             "nsStyleXUL",
             "nsTimingFunction",
+            "nscolor",
             "nscoord",
             "nsresult",
             "Loader",
             "ServoStyleSheet",
             "EffectCompositor_CascadeLevel",
             "RawServoAnimationValueBorrowedListBorrowed",
         ];
         struct ArrayType {
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -1,11 +1,11 @@
 /* automatically generated by rust-bindgen */
 
-pub use nsstring::{nsACString, nsAString};
+pub use nsstring::{nsACString, nsAString, nsString};
 type nsACString_internal = nsACString;
 type nsAString_internal = nsAString;
 use gecko_bindings::structs::mozilla::css::URLValue;
 use gecko_bindings::structs::RawGeckoDocument;
 use gecko_bindings::structs::RawGeckoElement;
 use gecko_bindings::structs::RawGeckoKeyframeList;
 use gecko_bindings::structs::RawGeckoNode;
 use gecko_bindings::structs::RawGeckoAnimationValueList;
@@ -154,16 +154,17 @@ unsafe impl Send for nsStyleVariables {}
 unsafe impl Sync for nsStyleVariables {}
 use gecko_bindings::structs::nsStyleVisibility;
 unsafe impl Send for nsStyleVisibility {}
 unsafe impl Sync for nsStyleVisibility {}
 use gecko_bindings::structs::nsStyleXUL;
 unsafe impl Send for nsStyleXUL {}
 unsafe impl Sync for nsStyleXUL {}
 use gecko_bindings::structs::nsTimingFunction;
+use gecko_bindings::structs::nscolor;
 use gecko_bindings::structs::nscoord;
 use gecko_bindings::structs::nsresult;
 use gecko_bindings::structs::Loader;
 use gecko_bindings::structs::ServoStyleSheet;
 use gecko_bindings::structs::EffectCompositor_CascadeLevel;
 use gecko_bindings::structs::RawServoAnimationValueBorrowedListBorrowed;
 pub type nsTArrayBorrowed_uintptr_t<'a> = &'a mut ::gecko_bindings::structs::nsTArray<usize>;
 pub type ServoComputedValuesStrong = ::gecko_bindings::sugar::ownership::Strong<ServoComputedValues>;
@@ -849,16 +850,24 @@ extern "C" {
 extern "C" {
     pub fn Gecko_ReleaseCSSValueSharedListArbitraryThread(aPtr:
                                                               *mut nsCSSValueSharedList);
 }
 extern "C" {
     pub fn Gecko_PropertyId_IsPrefEnabled(id: nsCSSPropertyID) -> bool;
 }
 extern "C" {
+    pub fn Gecko_nsStyleFont_SetLang(font: *mut nsStyleFont,
+                                     atom: *mut nsIAtom);
+}
+extern "C" {
+    pub fn Gecko_nsStyleFont_CopyLangFrom(aFont: *mut nsStyleFont,
+                                          aSource: *const nsStyleFont);
+}
+extern "C" {
     pub fn Gecko_GetMediaFeatures() -> *const nsMediaFeature;
 }
 extern "C" {
     pub fn Gecko_Construct_Default_nsStyleFont(ptr: *mut nsStyleFont,
                                                pres_context:
                                                    RawGeckoPresContextBorrowed);
 }
 extern "C" {
@@ -1435,21 +1444,77 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_DeclarationBlock_RemovePropertyById(declarations:
                                                          RawServoDeclarationBlockBorrowed,
                                                      property:
                                                          nsCSSPropertyID);
 }
 extern "C" {
-    pub fn Servo_DeclarationBlock_AddPresValue(declarations:
+    pub fn Servo_DeclarationBlock_PropertyIsSet(declarations:
+                                                    RawServoDeclarationBlockBorrowed,
+                                                property: nsCSSPropertyID)
+     -> bool;
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetIdentStringValue(declarations:
+                                                          RawServoDeclarationBlockBorrowed,
+                                                      property:
+                                                          nsCSSPropertyID,
+                                                      value: *mut nsIAtom);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetKeywordValue(declarations:
+                                                      RawServoDeclarationBlockBorrowed,
+                                                  property: nsCSSPropertyID,
+                                                  value: i32);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetIntValue(declarations:
+                                                  RawServoDeclarationBlockBorrowed,
+                                              property: nsCSSPropertyID,
+                                              value: i32);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetPixelValue(declarations:
+                                                    RawServoDeclarationBlockBorrowed,
+                                                property: nsCSSPropertyID,
+                                                value: f32);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetPercentValue(declarations:
+                                                      RawServoDeclarationBlockBorrowed,
+                                                  property: nsCSSPropertyID,
+                                                  value: f32);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetAutoValue(declarations:
                                                    RawServoDeclarationBlockBorrowed,
-                                               property: nsCSSPropertyID,
-                                               css_value:
-                                                   nsCSSValueBorrowedMut);
+                                               property: nsCSSPropertyID);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetCurrentColor(declarations:
+                                                      RawServoDeclarationBlockBorrowed,
+                                                  property: nsCSSPropertyID);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetColorValue(declarations:
+                                                    RawServoDeclarationBlockBorrowed,
+                                                property: nsCSSPropertyID,
+                                                value: nscolor);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetFontFamily(declarations:
+                                                    RawServoDeclarationBlockBorrowed,
+                                                value:
+                                                    *const nsAString_internal);
+}
+extern "C" {
+    pub fn Servo_DeclarationBlock_SetTextDecorationColorOverride(declarations:
+                                                                     RawServoDeclarationBlockBorrowed);
 }
 extern "C" {
     pub fn Servo_CSSSupports2(name: *const nsACString_internal,
                               value: *const nsACString_internal) -> bool;
 }
 extern "C" {
     pub fn Servo_CSSSupports(cond: *const nsACString_internal) -> bool;
 }
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -62,17 +62,17 @@ class Keyword(object):
         if product == "gecko":
             return self.gecko_values()
         elif product == "servo":
             return self.servo_values()
         else:
             raise Exception("Bad product: " + product)
 
     def gecko_constant(self, value):
-        moz_stripped = value.replace("-moz-", '') if self.gecko_strip_moz_prefix else value
+        moz_stripped = value.replace("-moz-", '') if self.gecko_strip_moz_prefix else value.replace("-moz-", 'moz-')
         mapped = self.consts_map.get(value)
         if self.gecko_enum_prefix:
             parts = moz_stripped.split('-')
             parts = mapped if mapped else [p.title() for p in parts]
             return self.gecko_enum_prefix + "::" + "".join(parts)
         else:
             suffix = mapped if mapped else moz_stripped.replace("-", "_")
             return self.gecko_constant_prefix + "_" + suffix.upper()
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -35,16 +35,23 @@ impl Importance {
     pub fn important(self) -> bool {
         match self {
             Importance::Normal => false,
             Importance::Important => true,
         }
     }
 }
 
+impl Default for Importance {
+    #[inline]
+    fn default() -> Self {
+        Importance::Normal
+    }
+}
+
 /// Overridden declarations are skipped.
 #[derive(Debug, PartialEq, Clone)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct PropertyDeclarationBlock {
     /// The group of declarations, along with their importance.
     ///
     /// Only deduplicated declarations appear here.
     #[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -28,16 +28,18 @@ use gecko_bindings::bindings::Gecko_Copy
 use gecko_bindings::bindings::Gecko_CopyListStyleTypeFrom;
 use gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendGeneric;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed;
 use gecko_bindings::bindings::Gecko_FontFamilyList_Clear;
 use gecko_bindings::bindings::Gecko_SetCursorArrayLength;
 use gecko_bindings::bindings::Gecko_SetCursorImage;
 use gecko_bindings::bindings::Gecko_NewCSSShadowArray;
+use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
+use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
 use gecko_bindings::bindings::Gecko_SetListStyleImage;
 use gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use gecko_bindings::bindings::Gecko_SetListStyleType;
 use gecko_bindings::bindings::Gecko_SetNullImageValue;
 use gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
 use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use gecko_bindings::bindings::RawGeckoPresContextBorrowed;
 use gecko_bindings::structs;
@@ -48,17 +50,17 @@ use gecko::values::convert_nscolor_to_rg
 use gecko::values::convert_rgba_to_nscolor;
 use gecko::values::GeckoStyleCoordConvertible;
 use gecko::values::round_border_to_device_pixels;
 use logical_geometry::WritingMode;
 use properties::longhands;
 use properties::{DeclaredValue, Importance, LonghandId};
 use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
 use std::fmt::{self, Debug};
-use std::mem::{transmute, zeroed};
+use std::mem::{forget, transmute, zeroed};
 use std::ptr;
 use std::sync::Arc;
 use std::cmp;
 use values::computed::ToComputedValue;
 use values::{Either, Auto};
 use computed_values::border_style;
 
 pub mod style_structs {
@@ -228,18 +230,16 @@ pub struct ${style_struct.gecko_struct_n
 def get_gecko_property(ffi_name, self_param = "self"):
     if "mBorderColor" in ffi_name:
         return ffi_name.replace("mBorderColor",
                                 "unsafe { *%s.gecko.__bindgen_anon_1.mBorderColor.as_ref() }"
                                 % self_param)
     return "%s.gecko.%s" % (self_param, ffi_name)
 
 def set_gecko_property(ffi_name, expr):
-    if ffi_name == "__LIST_STYLE_TYPE__":
-        return "unsafe { Gecko_SetListStyleType(&mut self.gecko, %s as u32); }" % expr
     if "mBorderColor" in ffi_name:
         ffi_name = ffi_name.replace("mBorderColor",
                                     "*self.gecko.__bindgen_anon_1.mBorderColor.as_mut()")
         return "unsafe { %s = %s };" % (ffi_name, expr)
     return "self.gecko.%s = %s;" % (ffi_name, expr)
 %>
 
 <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')">
@@ -1116,17 +1116,17 @@ fn static_assert() {
     % endfor
 
     pub fn outline_has_nonzero_width(&self) -> bool {
         self.gecko.mActualOutlineWidth != 0
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Font"
-    skip_longhands="font-family font-size font-size-adjust font-weight font-synthesis"
+    skip_longhands="font-family font-size font-size-adjust font-weight font-synthesis -x-lang"
     skip_additionals="*">
 
     pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) {
         use properties::longhands::font_family::computed_value::FontFamily;
         use gecko_bindings::structs::FontFamilyType;
 
         let list = &mut self.gecko.mFont.fontlist;
         unsafe { Gecko_FontFamilyList_Clear(list); }
@@ -1225,16 +1225,31 @@ fn static_assert() {
         use values::specified::Number;
 
         match self.gecko.mFont.sizeAdjust {
             -1.0 => T::None,
             _ => T::Number(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);
+        }
+    }
+
+    #[allow(non_snake_case)]
+    pub fn copy__x_lang_from(&mut self, other: &Self) {
+        unsafe {
+            Gecko_nsStyleFont_CopyLangFrom(&mut self.gecko, &other.gecko);
+        }
+    }
 </%self:impl_trait>
 
 <%def name="impl_copy_animation_value(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn copy_animation_${ident}_from(&mut self, other: &Self) {
         unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) };
 
         let count = other.gecko.mAnimation${gecko_ffi_name}Count;
@@ -2149,18 +2164,42 @@ fn static_assert() {
             }
         }
     }
 
     pub fn copy_list_style_image_from(&mut self, other: &Self) {
         unsafe { Gecko_CopyListStyleImageFrom(&mut self.gecko, &other.gecko); }
     }
 
-    ${impl_keyword_setter("list_style_type", "__LIST_STYLE_TYPE__",
-                           data.longhands_by_name["list-style-type"].keyword)}
+    pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
+        use properties::longhands::list_style_type::computed_value::T as Keyword;
+        <%
+            keyword = data.longhands_by_name["list-style-type"].keyword
+            # The first four are @counter-styles
+            # The rest have special fallback behavior
+            special = """upper-roman lower-roman upper-alpha lower-alpha
+                         japanese-informal japanese-formal korean-hangul-formal korean-hanja-informal
+                         korean-hanja-formal simp-chinese-informal simp-chinese-formal
+                         trad-chinese-informal trad-chinese-formal""".split()
+        %>
+        let result = match v {
+            % for value in keyword.values_for('gecko'):
+                % if value in special:
+                    // Special keywords are implemented as @counter-styles
+                    // and need to be manually set as strings
+                    Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant("none")},
+                % else:
+                    Keyword::${to_rust_ident(value)} =>
+                        structs::${keyword.gecko_constant(value)},
+                % endif
+            % endfor
+        };
+        unsafe { Gecko_SetListStyleType(&mut self.gecko, result as u32); }
+    }
+
 
     pub fn copy_list_style_type_from(&mut self, other: &Self) {
         unsafe {
             Gecko_CopyListStyleTypeFrom(&mut self.gecko, &other.gecko);
         }
     }
 
     pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) {
@@ -2203,16 +2242,25 @@ fn static_assert() {
             }
         }
     }
 
     ${impl_simple_copy('_moz_image_region', 'mImageRegion')}
 
 </%self:impl_trait>
 
+<%self:impl_trait style_struct_name="Table" skip_longhands="-x-span">
+    #[allow(non_snake_case)]
+    pub fn set__x_span(&mut self, v: longhands::_x_span::computed_value::T) {
+        self.gecko.mSpan = v.0
+    }
+
+    ${impl_simple_copy('_x_span', 'mSpan')}
+</%self:impl_trait>
+
 <%self:impl_trait style_struct_name="Effects"
                   skip_longhands="box-shadow filter">
     pub fn set_box_shadow(&mut self, v: longhands::box_shadow::computed_value::T) {
 
         self.gecko.mBoxShadow.replace_with_new(v.0.len() as u32);
 
         for (servo, gecko_shadow) in v.0.into_iter()
                                       .zip(self.gecko.mBoxShadow.iter_mut()) {
@@ -2374,17 +2422,17 @@ fn static_assert() {
 </%self:impl_trait>
 
 
 <%self:impl_trait style_struct_name="InheritedText"
                   skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing
                                   -webkit-text-stroke-width text-emphasis-position -moz-tab-size">
 
     <% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left " +
-                                                  "-moz-right match-parent") %>
+                                                  "-moz-right match-parent char") %>
     ${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)}
 
     pub fn set_text_shadow(&mut self, v: longhands::text_shadow::computed_value::T) {
         self.gecko.mTextShadow.replace_with_new(v.0.len() as u32);
 
         for (servo, gecko_shadow) in v.0.into_iter()
                                       .zip(self.gecko.mTextShadow.iter_mut()) {
 
@@ -2574,16 +2622,19 @@ fn static_assert() {
             bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8;
         }
         if v.contains(longhands::text_decoration_line::LINE_THROUGH) {
             bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8;
         }
         if v.contains(longhands::text_decoration_line::BLINK) {
             bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_BLINK as u8;
         }
+        if v.contains(longhands::text_decoration_line::COLOR_OVERRIDE) {
+            bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL as u8;
+        }
         self.gecko.mTextDecorationLine = bits;
     }
 
     ${impl_simple_copy('text_decoration_line', 'mTextDecorationLine')}
 
 
     fn clear_overflow_sides_if_string(&mut self) {
         use gecko_bindings::structs::nsStyleTextOverflowSide;
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -371,30 +371,67 @@
     <%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="single_keyword_computed(name, values, vector=False, extra_specified=None, **kwargs)">
+<%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue')">
+    <%
+        if not values:
+            values = keyword.values_for(product)
+    %>
+    #[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()}: u32
+                        = structs::${keyword.gecko_enum_prefix}::${to_camel_case(value)} as u32;
+                % 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
+        }
+    }
+</%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',
         ]}
     %>
 
-    <%def name="inner_body()">
+    <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)">
         % if extra_specified:
             use style_traits::ToCss;
             define_css_keyword_enum! { SpecifiedValue:
-                % for value in data.longhands_by_name[name].keyword.values_for(product) + extra_specified.split():
+                % for value in keyword.values_for(product) + extra_specified.split():
                     "${value}" => ${to_rust_ident(value)},
                 % endfor
             }
         % else:
             pub use self::computed_value::T as SpecifiedValue;
         % endif
         pub mod computed_value {
             use style_traits::ToCss;
@@ -419,25 +456,35 @@
         }
         impl Parse for SpecifiedValue {
             #[inline]
             fn parse(_context: &ParserContext, input: &mut Parser)
                          -> Result<SpecifiedValue, ()> {
                 SpecifiedValue::parse(input)
             }
         }
+
+        % if needs_conversion:
+            <%
+                conversion_values = keyword.values_for(product)
+                if extra_specified:
+                    conversion_values += extra_specified.split()
+            %>
+            ${gecko_keyword_conversion(keyword, values=conversion_values)}
+        % endif
     </%def>
     % if vector:
         <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
-            ${inner_body()}
+            ${inner_body(Keyword(name, values, **keyword_kwargs))}
             ${caller.body()}
         </%call>
     % else:
         <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
-            ${inner_body()}
+            ${inner_body(Keyword(name, values, **keyword_kwargs),
+                         extra_specified=extra_specified, needs_conversion=needs_conversion)}
             ${caller.body()}
         </%call>
     % endif
 </%def>
 
 <%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
 <%
     shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.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, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
+<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
 
 <% data.new_style_struct("Border", inherited=False,
                    additional_methods=[Method("border_" + side + "_has_nonzero_width",
                                               "bool") for side in ["top", "right", "bottom", "left"]]) %>
 <%
     def maybe_logical_spec(side, kind):
         if side[1]: # if it is logical
             return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
@@ -27,16 +27,19 @@
     ${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle",
                               "specified::BorderStyle::none",
                               need_clone=True,
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
                               spec=maybe_logical_spec(side, "style"),
                               animatable=False, logical = side[1])}
 % endfor
 
+${helpers.gecko_keyword_conversion(Keyword('border-style',
+                                   "none solid double dotted dashed hidden groove ridge inset outset"),
+                                   type="::values::specified::BorderStyle")}
 % for side in ALL_SIDES:
     <%helpers:longhand name="border-${side[0]}-width" animatable="True" logical="${side[1]}"
                        alias="${maybe_moz_logical_alias(product, side, '-moz-border-%s-width')}"
                        spec="${maybe_logical_spec(side, 'width')}">
         use app_units::Au;
         use std::fmt;
         use style_traits::ToCss;
         use values::HasViewportPercentage;
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.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 Keyword, Method, to_rust_ident %>
+<% from data import Keyword, Method, to_rust_ident, to_camel_case%>
 
 <% data.new_style_struct("Box",
                          inherited=False,
                          gecko_name="Display") %>
 
 // TODO(SimonSapin): don't parse `inline-table`, since we don't support it
 <%helpers:longhand name="display"
                    need_clone="True"
@@ -88,16 +88,19 @@
                                    _cacheable: &mut bool,
                                    _error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
             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'))}
+
 </%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", animatable=False, internal=True,
                          spec="Internal (not web-exposed)")}
 
@@ -141,16 +144,17 @@
         }
     }
 </%helpers:single_keyword_computed>
 
 <%helpers:single_keyword_computed name="float"
                                   values="none left right"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
+                                  needs_conversion="True"
                                   animatable="False"
                                   need_clone="True"
                                   gecko_enum_prefix="StyleFloat"
                                   gecko_inexhaustive="True"
                                   gecko_ffi_name="mFloat"
                                   spec="https://drafts.csswg.org/css-box/#propdef-float">
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
@@ -185,16 +189,17 @@
         }
     }
 </%helpers:single_keyword_computed>
 
 <%helpers:single_keyword_computed name="clear"
                                   values="none left right both"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
+                                  needs_conversion="True"
                                   animatable="False"
                                   gecko_enum_prefix="StyleClear"
                                   gecko_ffi_name="mBreakType"
                                   spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control">
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
@@ -251,16 +256,18 @@
     use values::HasViewportPercentage;
 
     <% vertical_align = data.longhands_by_name["vertical-align"] %>
     <% vertical_align.keyword = Keyword("vertical-align",
                                         "baseline sub super top text-top middle bottom text-bottom",
                                         extra_gecko_values="middle-with-baseline") %>
     <% vertical_align_keywords = vertical_align.keyword.values_for(product) %>
 
+    ${helpers.gecko_keyword_conversion(vertical_align.keyword)}
+
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             match *self {
                 SpecifiedValue::LengthOrPercentage(ref length) => length.has_viewport_percentage(),
                 _ => false
             }
         }
     }
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -14,17 +14,17 @@
     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;
+        use cssparser::{CssStringWriter, Parser};
         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 {
@@ -68,16 +68,63 @@
                     "sans-serif" => return FontFamily::Generic(atom!("sans-serif")),
                     "cursive" => return FontFamily::Generic(atom!("cursive")),
                     "fantasy" => return FontFamily::Generic(atom!("fantasy")),
                     "monospace" => return FontFamily::Generic(atom!("monospace")),
                     _ => {}
                 }
                 FontFamily::FamilyName(FamilyName(input))
             }
+
+            /// Parse a font-family value
+            pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+                if let Ok(value) = input.try(|input| input.expect_string()) {
+                    return Ok(FontFamily::FamilyName(FamilyName(Atom::from(&*value))))
+                }
+                let first_ident = try!(input.expect_ident());
+
+                // FIXME(bholley): The fast thing to do here would be to look up the
+                // string (as lowercase) in the static atoms table. We don't have an
+                // API to do that yet though, so we do the simple thing for now.
+                let mut css_wide_keyword = false;
+                match_ignore_ascii_case! { first_ident,
+                    "serif" => return Ok(FontFamily::Generic(atom!("serif"))),
+                    "sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))),
+                    "cursive" => return Ok(FontFamily::Generic(atom!("cursive"))),
+                    "fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))),
+                    "monospace" => return Ok(FontFamily::Generic(atom!("monospace"))),
+
+                    // https://drafts.csswg.org/css-fonts/#propdef-font-family
+                    // "Font family names that happen to be the same as a keyword value
+                    //  (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
+                    //  must be quoted to prevent confusion with the keywords with the same names.
+                    //  The keywords ‘initial’ and ‘default’ are reserved for future use
+                    //  and must also be quoted when used as font names.
+                    //  UAs must not consider these keywords as matching the <family-name> type."
+                    "inherit" => css_wide_keyword = true,
+                    "initial" => css_wide_keyword = true,
+                    "unset" => css_wide_keyword = true,
+                    "default" => css_wide_keyword = true,
+                    _ => {}
+                }
+
+                let mut value = first_ident.into_owned();
+                // These keywords are not allowed by themselves.
+                // The only way this value can be valid with with another keyword.
+                if css_wide_keyword {
+                    let ident = input.expect_ident()?;
+                    value.push_str(" ");
+                    value.push_str(&ident);
+                }
+                while let Ok(ident) = input.try(|input| input.expect_ident()) {
+                    value.push_str(" ");
+                    value.push_str(&ident);
+                }
+                Ok(FontFamily::FamilyName(FamilyName(Atom::from(value))))
+            }
         }
 
         impl ToCss for FamilyName {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 dest.write_char('"')?;
                 write!(CssStringWriter::new(dest), "{}", self.0)?;
                 dest.write_char('"')
             }
@@ -114,85 +161,37 @@
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(vec![FontFamily::Generic(atom!("serif"))])
     }
 
     /// <family-name>#
     /// <family-name> = <string> | [ <ident>+ ]
     /// TODO: <generic-family>
-    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        Vec::<FontFamily>::parse(context, input).map(SpecifiedValue)
+    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        SpecifiedValue::parse(input)
     }
 
-    impl Parse for Vec<FontFamily> {
-        fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-            input.parse_comma_separated(|input| FontFamily::parse(context, input))
+    impl SpecifiedValue {
+        pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+            input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue)
         }
     }
 
     /// `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(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-            match FontFamily::parse(context, input) {
+        fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+            match FontFamily::parse(input) {
                 Ok(FontFamily::FamilyName(name)) => Ok(name),
                 Ok(FontFamily::Generic(_)) |
                 Err(()) => Err(())
             }
         }
     }
-
-    impl Parse for FontFamily {
-        fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-            if let Ok(value) = input.try(|input| input.expect_string()) {
-                return Ok(FontFamily::FamilyName(FamilyName(Atom::from(&*value))))
-            }
-            let first_ident = try!(input.expect_ident());
-
-            // FIXME(bholley): The fast thing to do here would be to look up the
-            // string (as lowercase) in the static atoms table. We don't have an
-            // API to do that yet though, so we do the simple thing for now.
-            let mut css_wide_keyword = false;
-            match_ignore_ascii_case! { first_ident,
-                "serif" => return Ok(FontFamily::Generic(atom!("serif"))),
-                "sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))),
-                "cursive" => return Ok(FontFamily::Generic(atom!("cursive"))),
-                "fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))),
-                "monospace" => return Ok(FontFamily::Generic(atom!("monospace"))),
-
-                // https://drafts.csswg.org/css-fonts/#propdef-font-family
-                // "Font family names that happen to be the same as a keyword value
-                //  (‘inherit’, ‘serif’, ‘sans-serif’, ‘monospace’, ‘fantasy’, and ‘cursive’)
-                //  must be quoted to prevent confusion with the keywords with the same names.
-                //  The keywords ‘initial’ and ‘default’ are reserved for future use
-                //  and must also be quoted when used as font names.
-                //  UAs must not consider these keywords as matching the <family-name> type."
-                "inherit" => css_wide_keyword = true,
-                "initial" => css_wide_keyword = true,
-                "unset" => css_wide_keyword = true,
-                "default" => css_wide_keyword = true,
-                _ => {}
-            }
-
-            let mut value = first_ident.into_owned();
-            // These keywords are not allowed by themselves.
-            // The only way this value can be valid with with another keyword.
-            if css_wide_keyword {
-                let ident = input.expect_ident()?;
-                value.push_str(" ");
-                value.push_str(&ident);
-            }
-            while let Ok(ident) = input.try(|input| input.expect_ident()) {
-                value.push_str(" ");
-                value.push_str(&ident);
-            }
-            Ok(FontFamily::FamilyName(FamilyName(Atom::from(value))))
-        }
-    }
 </%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",
@@ -757,8 +756,45 @@
             Ok(SpecifiedValue::Normal)
         } else {
             input.expect_string().map(|cow| {
                 SpecifiedValue::Override(cow.into_owned())
             })
         }
     }
 </%helpers:longhand>
+
+<%helpers:longhand name="-x-lang" products="gecko" animatable="False" internal="True"
+                   spec="Internal (not web-exposed)"
+                   internal="True">
+    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 Atom;
+        use std::fmt;
+        use style_traits::ToCss;
+
+        impl ToCss for T {
+            fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
+                Ok(())
+            }
+        }
+
+        #[derive(Clone, Debug, PartialEq)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        pub struct T(pub Atom);
+    }
+
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        computed_value::T(atom!(""))
+    }
+
+    pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        debug_assert!(false, "Should be set directly by presentation attributes only.");
+        Err(())
+    }
+</%helpers:longhand>
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -11,16 +11,17 @@
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse")}
 ${helpers.single_keyword("empty-cells", "show hide",
                          gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells")}
 ${helpers.single_keyword("caption-side", "top bottom",
                          extra_gecko_values="right left top-outside bottom-outside",
+                         needs_conversion="True",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
 
 <%helpers:longhand name="border-spacing" animatable="False" boxed="True"
                    spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.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 Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
 <%helpers:longhand name="line-height" animatable="True"
                    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height">
     use std::fmt;
     use style_traits::ToCss;
     use values::{CSSFloat, HasViewportPercentage};
 
@@ -246,25 +246,30 @@
             servo_center("-servo-center") => 6,
             servo_left("-servo-left") => 7,
             servo_right("-servo-right") => 8,
             % else:
             _moz_center("-moz-center") => 6,
             _moz_left("-moz-left") => 7,
             _moz_right("-moz-right") => 8,
             match_parent("match-parent") => 9,
+            char("char") => 10,
             % endif
         }
     }
     #[inline] pub fn get_initial_value() -> computed_value::T {
         computed_value::T::start
     }
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         computed_value::T::parse(input)
     }
+    ${helpers.gecko_keyword_conversion(Keyword('text-align',
+                                               """left right center justify -moz-left -moz-right
+                                                -moz-center char end match-parent""",
+                                                gecko_strip_moz_prefix=False))}
 </%helpers:longhand>
 
 // FIXME: This prop should be animatable.
 <%helpers:longhand name="letter-spacing" animatable="False"
                    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
@@ -510,16 +515,17 @@
         let derived = derive(context);
         context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived);
     }
 </%helpers:longhand>
 
 <%helpers:single_keyword_computed name="white-space"
                                   values="normal pre nowrap pre-wrap pre-line"
                                   gecko_constant_prefix="NS_STYLE_WHITESPACE"
+                                  needs_conversion="True"
                                   animatable="False"
                                   spec="https://drafts.csswg.org/css-text/#propdef-white-space">
     use values::computed::ComputedValueAsSpecified;
     use values::HasViewportPercentage;
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     impl SpecifiedValue {
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -11,25 +11,34 @@
 
 // TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
 //
 //     decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
 //     upper-roman
 //
 // TODO(bholley): Missing quite a few gecko properties here as well.
 //
+// In gecko, {upper,lower}-{roman,alpha} are implemented as @counter-styles in the
+// UA, however they can also be set from pres attrs. When @counter-style is supported
+// we may need to look into this and handle these differently.
+//
 // [1]: http://dev.w3.org/csswg/css-counter-styles/
 ${helpers.single_keyword("list-style-type", """
-    disc none circle square decimal disclosure-open disclosure-closed
+    disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
 """, extra_servo_values="""arabic-indic bengali cambodian cjk-decimal devanagari
                            gujarati gurmukhi kannada khmer lao malayalam mongolian
                            myanmar oriya persian telugu thai tibetan cjk-earthly-branch
                            cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
-                           katakana-iroha lower-alpha upper-alpha""",
+                           katakana-iroha""",
+    extra_gecko_values="""japanese-informal japanese-formal korean-hangul-formal
+    korean-hanja-formal korean-hanja-informal simp-chinese-informal simp-chinese-formal
+    trad-chinese-informal trad-chinese-formal ethiopic-numeric upper-roman lower-roman
+    """,
     gecko_constant_prefix="NS_STYLE_LIST_STYLE",
+    needs_conversion="True",
     animatable=False,
     spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
 
 ${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_)",
                           animatable=False,
                           spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image")}
 
 <%helpers:longhand name="quotes" animatable="False"
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -154,17 +154,17 @@
                          gecko_enum_prefix="StyleUserInput",
                          gecko_inexhaustive=True,
                          animatable=False,
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)")}
 
 ${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only",
                          products="gecko", gecko_ffi_name="mUserModify",
                          gecko_enum_prefix="StyleUserModify",
-                         gecko_inexhaustive=True,
+                         needs_conversion=True,
                          animatable=False,
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)")}
 
 ${helpers.single_keyword("-moz-user-focus",
                          "none ignore normal select-after select-before select-menu select-same select-all",
                          products="gecko", gecko_ffi_name="mUserFocus",
                          gecko_enum_prefix="StyleUserFocus",
                          gecko_inexhaustive=True,
--- a/servo/components/style/properties/longhand/table.mako.rs
+++ b/servo/components/style/properties/longhand/table.mako.rs
@@ -4,8 +4,44 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Table", inherited=False) %>
 
 ${helpers.single_keyword("table-layout", "auto fixed",
                          gecko_ffi_name="mLayoutStrategy", animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-table-layout")}
+
+<%helpers:longhand name="-x-span" products="gecko"
+                   spec="Internal-only (for `<col span>` pres attr)"
+                   animatable="False"
+                   internal="True">
+    use values::HasViewportPercentage;
+    use values::computed::ComputedValueAsSpecified;
+
+    impl ComputedValueAsSpecified for SpecifiedValue {}
+    no_viewport_percentage!(SpecifiedValue);
+    pub type SpecifiedValue = computed_value::T;
+    pub mod computed_value {
+        use std::fmt;
+        use style_traits::ToCss;
+
+        #[derive(PartialEq, Clone, Copy, Debug)]
+        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+        pub struct T(pub i32);
+
+        impl ToCss for T {
+            fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
+                Ok(())
+            }
+        }
+    }
+
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        computed_value::T(1)
+    }
+
+    // never parse it, only set via presentation attribute
+    fn parse(_: &ParserContext, _: &mut Parser) -> Result<SpecifiedValue, ()> {
+        Err(())
+    }
+</%helpers:longhand>
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -117,16 +117,26 @@
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags SpecifiedValue: u8 {
             const NONE = 0,
             const OVERLINE = 0x01,
             const UNDERLINE = 0x02,
             const LINE_THROUGH = 0x04,
             const BLINK = 0x08,
+        % if product == "gecko":
+            /// Only set by presentation attributes
+            ///
+            /// Setting this will mean that text-decorations use the color
+            /// specified by `color` in quirks mode.
+            ///
+            /// For example, this gives <a href=foo><font color="red">text</font></a>
+            /// a red text decoration
+            const COLOR_OVERRIDE = 0x10,
+        % endif
         }
     }
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             let mut has_any = false;
             macro_rules! write_value {
                 ($line:ident => $css:expr) => {
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -7,20 +7,19 @@
 <%helpers:shorthand name="font" sub_properties="font-style font-variant font-weight font-stretch
                                                 font-size line-height font-family
                                                 ${'font-size-adjust' if product == 'gecko' else ''}
                                                 ${'font-kerning' if product == 'gecko' else ''}
                                                 ${'font-variant-caps' if product == 'gecko' else ''}
                                                 ${'font-variant-position' if product == 'gecko' else ''}
                                                 ${'font-language-override' if product == 'none' else ''}"
                     spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
-    use parser::Parse;
     use properties::longhands::{font_style, font_variant, font_weight, font_stretch};
-    use properties::longhands::{font_size, line_height, font_family};
-    use properties::longhands::font_family::computed_value::FontFamily;
+    use properties::longhands::{font_size, line_height};
+    use properties::longhands::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 = None;
         let mut weight = None;
         let mut stretch = None;
         let size;
@@ -66,25 +65,25 @@
         if size.is_none() || (count(&style) + count(&weight) + count(&variant) + count(&stretch) + nb_normals) > 4 {
             return Err(())
         }
         let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
             Some(try!(line_height::parse(context, input)))
         } else {
             None
         };
-        let family = Vec::<FontFamily>::parse(context, input)?;
+        let family = FontFamily::parse(input)?;
         Ok(Longhands {
             font_style: style,
             font_variant: variant,
             font_weight: weight,
             font_stretch: stretch,
             font_size: size,
             line_height: line_height,
-            font_family: Some(font_family::SpecifiedValue(family)),
+            font_family: Some(family),
     % if product == "gecko":
             font_size_adjust: None,
             font_kerning: None,
             font_variant_caps: None,
             font_variant_position: None,
     % endif
     % if product == "none":
             font_language_override: None,
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -363,16 +363,23 @@ pub enum Length {
     ///
     /// https://drafts.csswg.org/css-values/#calc-notation
     ///
     /// TODO(emilio): We have more `Calc` variants around, we should only use
     /// one.
     Calc(Box<CalcLengthOrPercentage>, AllowedNumericType),
 }
 
+impl From<NoCalcLength> for Length {
+    #[inline]
+    fn from(len: NoCalcLength) -> Self {
+        Length::NoCalc(len)
+    }
+}
+
 impl HasViewportPercentage for Length {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             Length::NoCalc(ref inner) => inner.has_viewport_percentage(),
             Length::Calc(ref calc, _) => calc.has_viewport_percentage(),
         }
     }
 }
@@ -933,16 +940,30 @@ impl From<Length> for LengthOrPercentage
     fn from(len: Length) -> LengthOrPercentage {
         match len {
             Length::NoCalc(l) => LengthOrPercentage::Length(l),
             Length::Calc(l, _) => LengthOrPercentage::Calc(l),
         }
     }
 }
 
+impl From<NoCalcLength> for LengthOrPercentage {
+    #[inline]
+    fn from(len: NoCalcLength) -> Self {
+        LengthOrPercentage::Length(len)
+    }
+}
+
+impl From<Percentage> for LengthOrPercentage {
+    #[inline]
+    fn from(pc: Percentage) -> Self {
+        LengthOrPercentage::Percentage(pc)
+    }
+}
+
 impl HasViewportPercentage for LengthOrPercentage {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             LengthOrPercentage::Length(ref length) => length.has_viewport_percentage(),
             LengthOrPercentage::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
@@ -1038,16 +1059,31 @@ impl Parse for LengthOrPercentage {
 #[allow(missing_docs)]
 pub enum LengthOrPercentageOrAuto {
     Length(NoCalcLength),
     Percentage(Percentage),
     Auto,
     Calc(Box<CalcLengthOrPercentage>),
 }
 
+
+impl From<NoCalcLength> for LengthOrPercentageOrAuto {
+    #[inline]
+    fn from(len: NoCalcLength) -> Self {
+        LengthOrPercentageOrAuto::Length(len)
+    }
+}
+
+impl From<Percentage> for LengthOrPercentageOrAuto {
+    #[inline]
+    fn from(pc: Percentage) -> Self {
+        LengthOrPercentageOrAuto::Percentage(pc)
+    }
+}
+
 impl HasViewportPercentage for LengthOrPercentageOrAuto {
     fn has_viewport_percentage(&self) -> bool {
         match *self {
             LengthOrPercentageOrAuto::Length(ref length) => length.has_viewport_percentage(),
             LengthOrPercentageOrAuto::Calc(ref calc) => calc.has_viewport_percentage(),
             _ => false
         }
     }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -31,17 +31,17 @@ use style::gecko::wrapper::DUMMY_BASE_UR
 use style::gecko::wrapper::GeckoElement;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoStyleRuleBorrowed, RawServoStyleRuleStrong};
 use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
 use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
 use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
 use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong};
-use style::gecko_bindings::bindings::{nsACString, nsCSSValueBorrowedMut, nsAString};
+use style::gecko_bindings::bindings::{nsACString, nsAString};
 use style::gecko_bindings::bindings::Gecko_AnimationAppendKeyframe;
 use style::gecko_bindings::bindings::RawGeckoAnimationValueListBorrowedMut;
 use style::gecko_bindings::bindings::RawGeckoElementBorrowed;
 use style::gecko_bindings::bindings::RawGeckoKeyframeListBorrowedMut;
 use style::gecko_bindings::bindings::RawGeckoPresContextBorrowed;
 use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
 use style::gecko_bindings::bindings::RawServoAnimationValueStrong;
 use style::gecko_bindings::bindings::RawServoImportRuleBorrowed;
@@ -947,69 +947,287 @@ pub extern "C" fn Servo_DeclarationBlock
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(declarations: RawServoDeclarationBlockBorrowed,
                                                             property: nsCSSPropertyID) {
     remove_property(declarations, get_property_id_from_nscsspropertyid!(property, ()))
 }
 
-#[no_mangle]
-pub extern "C" fn Servo_DeclarationBlock_AddPresValue(declarations: RawServoDeclarationBlockBorrowed,
-                                                      property: nsCSSPropertyID,
-                                                      css_value: nsCSSValueBorrowedMut) {
-    use style::gecko::values::convert_nscolor_to_rgba;
-    use style::properties::{DeclaredValue, LonghandId, PropertyDeclaration, PropertyId, longhands};
-    use style::values::specified;
-
-    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
-    let prop = PropertyId::from_nscsspropertyid(property);
-
-    let long = match prop {
-        Ok(PropertyId::Longhand(long)) => long,
-        _ => {
-            warn!("stylo: unknown presentation property with id {:?}", property);
-            return
+macro_rules! get_longhand_from_id {
+    ($id:expr, $retval:expr) => {
+        match PropertyId::from_nscsspropertyid($id) {
+            Ok(PropertyId::Longhand(long)) => long,
+            _ => {
+                error!("stylo: unknown presentation property with id {:?}", $id);
+                return $retval
+            }
         }
     };
-    let decl = match long {
-        LonghandId::FontSize => {
-            if let Some(int) = css_value.integer() {
-                PropertyDeclaration::FontSize(DeclaredValue::Value(
-                    longhands::font_size::SpecifiedValue(
-                        specified::LengthOrPercentage::Length(
-                            specified::NoCalcLength::from_font_size_int(int as u8)
-                        )
-                    )
-                ))
-            } else {
-                warn!("stylo: got unexpected non-integer value for font-size presentation attribute");
+    ($id:expr) => {
+        get_longhand_from_id!($id, ())
+    }
+}
+
+macro_rules! match_wrap_declared {
+    ($longhand:ident, $($property:ident => $inner:expr,)*) => (
+        match $longhand {
+            $(
+                LonghandId::$property => PropertyDeclaration::$property(DeclaredValue::Value($inner)),
+            )*
+            _ => {
+                error!("stylo: Don't know how to handle presentation property {:?}", $longhand);
                 return
             }
         }
-        LonghandId::Color => {
-            if let Some(color) = css_value.color_value() {
-                PropertyDeclaration::Color(DeclaredValue::Value(
-                    specified::CSSRGBA {
-                        parsed: convert_nscolor_to_rgba(color),
-                        authored: None
-                    }
-                ))
-            } else {
-                warn!("stylo: got unexpected non-integer value for color presentation attribute");
-                return
+    )
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_PropertyIsSet(declarations:
+                                                       RawServoDeclarationBlockBorrowed,
+                                                       property: nsCSSPropertyID)
+        -> bool {
+    use style::properties::PropertyDeclarationId;
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property, false);
+    declarations.read().get(PropertyDeclarationId::Longhand(long)).is_some()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetIdentStringValue(declarations:
+                                                             RawServoDeclarationBlockBorrowed,
+                                                             property:
+                                                             nsCSSPropertyID,
+                                                             value:
+                                                             *mut nsIAtom) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::properties::longhands::_x_lang::computed_value::T as Lang;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let prop = match_wrap_declared! { long,
+        XLang => Lang(Atom::from(value)),
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+#[allow(unreachable_code)]
+pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(declarations:
+                                                         RawServoDeclarationBlockBorrowed,
+                                                         property: nsCSSPropertyID,
+                                                         value: i32) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::properties::longhands;
+    use style::values::specified::{BorderStyle, NoCalcLength};
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let value = value as u32;
+
+    let prop = match_wrap_declared! { long,
+        MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value),
+        // TextEmphasisPosition => FIXME implement text-emphasis-position
+        Display => longhands::display::SpecifiedValue::from_gecko_keyword(value),
+        Float => longhands::float::SpecifiedValue::from_gecko_keyword(value),
+        VerticalAlign => longhands::vertical_align::SpecifiedValue::from_gecko_keyword(value),
+        TextAlign => longhands::text_align::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(NoCalcLength::from_font_size_int(value as u8).into())
+        },
+        ListStyleType => longhands::list_style_type::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),
+        BorderLeftStyle => BorderStyle::from_gecko_keyword(value),
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetIntValue(declarations: RawServoDeclarationBlockBorrowed,
+                                                     property: nsCSSPropertyID,
+                                                     value: i32) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::properties::longhands::_x_span::computed_value::T as Span;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let prop = match_wrap_declared! { long,
+        XSpan => Span(value),
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(declarations:
+                                                       RawServoDeclarationBlockBorrowed,
+                                                       property: nsCSSPropertyID,
+                                                       value: f32) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::properties::longhands::border_spacing::SpecifiedValue as BorderSpacing;
+    use style::values::specified::BorderWidth;
+    use style::values::specified::length::NoCalcLength;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let nocalc = NoCalcLength::from_px(value);
+
+    let prop = match_wrap_declared! { long,
+        Height => nocalc.into(),
+        Width => nocalc.into(),
+        BorderTopWidth => BorderWidth::Width(nocalc.into()),
+        BorderRightWidth => BorderWidth::Width(nocalc.into()),
+        BorderBottomWidth => BorderWidth::Width(nocalc.into()),
+        BorderLeftWidth => BorderWidth::Width(nocalc.into()),
+        MarginTop => nocalc.into(),
+        MarginRight => nocalc.into(),
+        MarginBottom => nocalc.into(),
+        MarginLeft => nocalc.into(),
+        PaddingTop => nocalc.into(),
+        PaddingRight => nocalc.into(),
+        PaddingBottom => nocalc.into(),
+        PaddingLeft => nocalc.into(),
+        BorderSpacing => Box::new(
+            BorderSpacing {
+                horizontal: nocalc.into(),
+                vertical: nocalc.into(),
             }
+        ),
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(declarations:
+                                                         RawServoDeclarationBlockBorrowed,
+                                                         property: nsCSSPropertyID,
+                                                         value: f32) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::values::specified::length::Percentage;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let pc = Percentage(value);
+
+    let prop = match_wrap_declared! { long,
+        Height => pc.into(),
+        Width => pc.into(),
+        MarginTop => pc.into(),
+        MarginRight => pc.into(),
+        MarginBottom => pc.into(),
+        MarginLeft => pc.into(),
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetAutoValue(declarations:
+                                                      RawServoDeclarationBlockBorrowed,
+                                                      property: nsCSSPropertyID) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::values::specified::LengthOrPercentageOrAuto;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let auto = LengthOrPercentageOrAuto::Auto;
+
+    let prop = match_wrap_declared! { long,
+        Height => auto,
+        Width => auto,
+        MarginTop => auto,
+        MarginRight => auto,
+        MarginBottom => auto,
+        MarginLeft => auto,
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetCurrentColor(declarations:
+                                                         RawServoDeclarationBlockBorrowed,
+                                                         property: nsCSSPropertyID) {
+    use cssparser::Color;
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::values::specified::CSSColor;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let cc = CSSColor { parsed: Color::CurrentColor, authored: None };
+
+    let prop = match_wrap_declared! { long,
+        BorderTopColor => cc,
+        BorderRightColor => cc,
+        BorderBottomColor => cc,
+        BorderLeftColor => cc,
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetColorValue(declarations:
+                                                       RawServoDeclarationBlockBorrowed,
+                                                       property: nsCSSPropertyID,
+                                                       value: structs::nscolor) {
+    use cssparser::Color;
+    use style::gecko::values::convert_nscolor_to_rgba;
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::values::specified::{CSSColor, CSSRGBA};
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let rgba = convert_nscolor_to_rgba(value);
+    let color = CSSColor { parsed: Color::RGBA(rgba), authored: None };
+
+    let prop = match_wrap_declared! { long,
+        BorderTopColor => color,
+        BorderRightColor => color,
+        BorderBottomColor => color,
+        BorderLeftColor => color,
+        Color => CSSRGBA { parsed: rgba, authored: None },
+        BackgroundColor => color,
+    };
+    declarations.write().declarations.push((prop, Default::default()));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetFontFamily(declarations:
+                                                       RawServoDeclarationBlockBorrowed,
+                                                       value: *const nsAString) {
+    use cssparser::Parser;
+    use style::properties::{DeclaredValue, PropertyDeclaration};
+    use style::properties::longhands::font_family::SpecifiedValue as FontFamily;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    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(DeclaredValue::Value(family));
+            declarations.write().declarations.push((decl, Default::default()));
         }
-        _ => {
-            warn!("stylo: cannot handle longhand {:?} from presentation attribute", long);
-            return
-        }
-    };
-    declarations.write().declarations.push((decl, Importance::Normal));
+    }
+}
 
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(declarations:
+                                                                RawServoDeclarationBlockBorrowed) {
+    use style::properties::{DeclaredValue, PropertyDeclaration};
+    use style::properties::longhands::text_decoration_line;
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let mut decoration = text_decoration_line::computed_value::none;
+    decoration |= text_decoration_line::COLOR_OVERRIDE;
+    let decl = PropertyDeclaration::TextDecorationLine(DeclaredValue::Value(decoration));
+    declarations.write().declarations.push((decl, Default::default()));
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_CSSSupports2(property: *const nsACString, value: *const nsACString) -> bool {
     let property = unsafe { property.as_ref().unwrap().as_str_unchecked() };
     let id =  if let Ok(id) = PropertyId::parse(property.into()) {
         id
     } else {