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 390359 fcccffa68c9df87a4c35cc8232cd01b2f2c2fb07
parent 390358 c73bba5d7bea1149ad0d6d797def73ebf37bbd43
child 390360 7943ca5e483ab2865df343e0529d5217c3aced1b
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio, bz
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #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 {