servo: Merge #11972 - Staticize CASCADE_PROPERTIES, (temporarily) fix stylo path for animations, and introduce the long-term path to follow (from emilio:style-thingies); r=bholley
authorEmilio Cobos Álvarez <me@emiliocobos.me>
Sat, 02 Jul 2016 13:27:25 -0700
changeset 339186 06bc6a420b9e2265fcb5489827482da1b957301f
parent 339185 b47fe3a47091e3c33f57cb856d60e46cac24fbfd
child 339187 e58f9d08829d262b36dfba48e9edd6c0789b63d7
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
servo: Merge #11972 - Staticize CASCADE_PROPERTIES, (temporarily) fix stylo path for animations, and introduce the long-term path to follow (from emilio:style-thingies); r=bholley <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors <!-- Either: --> - [x] These changes do not require tests because refactoring + geckolib <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> @bholley: I was going to do this "the good way", but that involves quite a few properties, so I thought I'd rather unlock stylo before :) r? @bholley Source-Repo: https://github.com/servo/servo Source-Revision: a77cc9950fb13ccd674a10e46c2327bfa0735dab
servo/components/style/animation.rs
servo/components/style/properties/data.py
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/values.rs
servo/ports/geckolib/properties.mako.rs
servo/ports/geckolib/values.rs
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -248,20 +248,20 @@ impl PropertyAnimation {
     /// Creates a new property animation for the given transition index and old and new styles.
     /// Any number of animations may be returned, from zero (if the property did not animate) to
     /// one (for a single transition property) to arbitrarily many (for `all`).
     pub fn from_transition<C: ComputedValues>(transition_index: usize,
                                               old_style: &C,
                                               new_style: &mut C)
                                               -> Vec<PropertyAnimation> {
         let mut result = vec![];
-        let box_style = new_style.as_servo().get_box();
-        let transition_property = box_style.transition_property.0[transition_index];
-        let timing_function = *box_style.transition_timing_function.0.get_mod(transition_index);
-        let duration = *box_style.transition_duration.0.get_mod(transition_index);
+        let box_style = new_style.get_box();
+        let transition_property = box_style.transition_property_at(transition_index);
+        let timing_function = box_style.transition_timing_function_mod(transition_index);
+        let duration = box_style.transition_duration_mod(transition_index);
 
 
         if transition_property != TransitionProperty::All {
             if let Some(property_animation) =
                     PropertyAnimation::from_transition_property(transition_property,
                                                                 timing_function,
                                                                 duration,
                                                                 old_style,
@@ -328,62 +328,45 @@ impl PropertyAnimation {
     }
 
     #[inline]
     fn does_animate(&self) -> bool {
         self.property.does_animate() && self.duration != Time(0.0)
     }
 }
 
-/// Accesses an element of an array, "wrapping around" using modular arithmetic. This is needed
-/// to handle [repeatable lists][lists] of differing lengths.
-///
-/// [lists]: https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
-pub trait GetMod {
-    type Item;
-    fn get_mod(&self, i: usize) -> &Self::Item;
-}
-
-impl<T> GetMod for Vec<T> {
-    type Item = T;
-    #[inline]
-    fn get_mod(&self, i: usize) -> &T {
-        &(*self)[i % self.len()]
-    }
-}
-
 /// Inserts transitions into the queue of running animations as applicable for
 /// the given style difference. This is called from the layout worker threads.
 /// Returns true if any animations were kicked off and false otherwise.
 //
 // TODO(emilio): Take rid of this mutex splitting SharedLayoutContex into a
 // cloneable part and a non-cloneable part..
 pub fn start_transitions_if_applicable<Impl: SelectorImplExt>(new_animations_sender: &Sender<Animation<Impl>>,
                                                               node: OpaqueNode,
                                                               old_style: &Impl::ComputedValues,
                                                               new_style: &mut Arc<Impl::ComputedValues>)
                                                               -> bool {
     let mut had_animations = false;
-    for i in 0..new_style.get_box().transition_count() {
+    for i in 0..new_style.get_box().transition_property_count() {
         // Create any property animations, if applicable.
         let property_animations = PropertyAnimation::from_transition(i, old_style, Arc::make_mut(new_style));
         for property_animation in property_animations {
             // Set the property to the initial value.
             // NB: get_mut is guaranteed to succeed since we called make_mut()
             // above.
             property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
 
             // Kick off the animation.
+            let box_style = new_style.get_box();
             let now = time::precise_time_s();
-            let box_style = new_style.as_servo().get_box();
             let start_time =
-                now + (box_style.transition_delay.0.get_mod(i).seconds() as f64);
+                now + (box_style.transition_delay_mod(i).seconds() as f64);
             new_animations_sender
                 .send(Animation::Transition(node, start_time, AnimationFrame {
-                    duration: box_style.transition_duration.0.get_mod(i).seconds() as f64,
+                    duration: box_style.transition_duration_mod(i).seconds() as f64,
                     property_animation: property_animation,
                 }, /* is_expired = */ false)).unwrap();
 
             had_animations = true;
         }
     }
 
     had_animations
@@ -417,54 +400,54 @@ fn compute_style_for_animation_step<Impl
 
 pub fn maybe_start_animations<Impl: SelectorImplExt>(context: &SharedStyleContext<Impl>,
                                                      new_animations_sender: &Sender<Animation<Impl>>,
                                                      node: OpaqueNode,
                                                      new_style: &Arc<Impl::ComputedValues>) -> bool
 {
     let mut had_animations = false;
 
-    let box_style = new_style.as_servo().get_box();
-    for (i, name) in box_style.animation_name.0.iter().enumerate() {
+    let box_style = new_style.get_box();
+    for (i, name) in box_style.animation_name_iter().enumerate() {
         debug!("maybe_start_animations: name={}", name);
-        let total_duration = box_style.animation_duration.0.get_mod(i).seconds();
+        let total_duration = box_style.animation_duration_mod(i).seconds();
         if total_duration == 0. {
             continue
         }
 
         if let Some(ref anim) = context.stylist.animations().get(&name) {
             debug!("maybe_start_animations: animation {} found", name);
 
             // If this animation doesn't have any keyframe, we can just continue
             // without submitting it to the compositor, since both the first and
             // the second keyframes would be synthetised from the computed
             // values.
             if anim.steps.is_empty() {
                 continue;
             }
 
-            let delay = box_style.animation_delay.0.get_mod(i).seconds();
+            let delay = box_style.animation_delay_mod(i).seconds();
             let now = time::precise_time_s();
             let animation_start = now + delay as f64;
-            let duration = box_style.animation_duration.0.get_mod(i).seconds();
-            let iteration_state = match *box_style.animation_iteration_count.0.get_mod(i) {
+            let duration = box_style.animation_duration_mod(i).seconds();
+            let iteration_state = match box_style.animation_iteration_count_mod(i) {
                 AnimationIterationCount::Infinite => KeyframesIterationState::Infinite,
                 AnimationIterationCount::Number(n) => KeyframesIterationState::Finite(0, n),
             };
 
-            let animation_direction = *box_style.animation_direction.0.get_mod(i);
+            let animation_direction = box_style.animation_direction_mod(i);
 
             let initial_direction = match animation_direction {
                 AnimationDirection::normal |
                 AnimationDirection::alternate => AnimationDirection::normal,
                 AnimationDirection::reverse |
                 AnimationDirection::alternate_reverse => AnimationDirection::reverse,
             };
 
-            let running_state = match *box_style.animation_play_state.0.get_mod(i) {
+            let running_state = match box_style.animation_play_state_mod(i) {
                 AnimationPlayState::paused => KeyframesRunningState::Paused(0.),
                 AnimationPlayState::running => KeyframesRunningState::Running,
             };
 
 
             new_animations_sender
                 .send(Animation::Keyframes(node, name.clone(), KeyframesAnimationState {
                     started_at: animation_start,
@@ -545,29 +528,29 @@ where Impl: SelectorImplExt,
                     warn!("update_style_for_animation: Animation {:?} not found", name);
                     return;
                 }
                 Some(animation) => animation,
             };
 
             debug_assert!(!animation.steps.is_empty());
 
-            let maybe_index = style.as_servo()
-                                   .get_box().animation_name.0.iter()
-                                   .position(|animation_name| name == animation_name);
+            let maybe_index = style.get_box()
+                                   .animation_name_iter()
+                                   .position(|animation_name| *name == animation_name);
 
             let index = match maybe_index {
                 Some(index) => index,
                 None => {
                     warn!("update_style_for_animation: Animation {:?} not found in style", name);
                     return;
                 }
             };
 
-            let total_duration = style.as_servo().get_box().animation_duration.0.get_mod(index).seconds() as f64;
+            let total_duration = style.get_box().animation_duration_mod(index).seconds() as f64;
             if total_duration == 0. {
                 debug!("update_style_for_animation: zero duration for animation {:?}", name);
                 return;
             }
 
             let mut total_progress = (now - started_at) / total_duration;
             if total_progress < 0. {
                 warn!("Negative progress found for animation {:?}", name);
@@ -637,19 +620,19 @@ where Impl: SelectorImplExt,
             // TODO: How could we optimise it? Is it such a big deal?
             let from_style = compute_style_for_animation_step(context,
                                                               last_keyframe,
                                                               &**style,
                                                               &state.cascade_style);
 
             // NB: The spec says that the timing function can be overwritten
             // from the keyframe style.
-            let mut timing_function = *style.as_servo().get_box().animation_timing_function.0.get_mod(index);
-            if !from_style.as_servo().get_box().animation_timing_function.0.is_empty() {
-                timing_function = from_style.as_servo().get_box().animation_timing_function.0[0];
+            let mut timing_function = style.get_box().animation_timing_function_mod(index);
+            if from_style.get_box().animation_timing_function_count() != 0 {
+                timing_function = from_style.get_box().animation_timing_function_at(0);
             }
 
             let target_style = compute_style_for_animation_step(context,
                                                                 target_keyframe,
                                                                 &from_style,
                                                                 &state.cascade_style);
 
             let mut new_style = (*style).clone();
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -42,40 +42,46 @@ class Keyword(object):
 
     def gecko_constant(self, value):
         return self.gecko_constant_prefix + "_" + value.replace("-moz-", "").replace("-", "_").upper()
 
 
 class Longhand(object):
     def __init__(self, style_struct, name, animatable=None, derived_from=None, keyword=None,
                  predefined_type=None, custom_cascade=False, experimental=False, internal=False,
-                 need_clone=False, gecko_ffi_name=None):
+                 need_clone=False, need_index=False, gecko_ffi_name=None):
         self.name = name
         self.keyword = keyword
         self.predefined_type = predefined_type
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
         self.style_struct = style_struct
         self.experimental = ("layout.%s.enabled" % name) if experimental else None
         self.custom_cascade = custom_cascade
         self.internal = internal
-        self.need_clone = need_clone
+        self.need_index = need_index
         self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
         self.derived_from = (derived_from or "").split()
 
         # This is done like this since just a plain bool argument seemed like
         # really random.
         if animatable is None:
             raise TypeError("animatable should be specified for " + name + ")")
         if isinstance(animatable, bool):
             self.animatable = animatable
         else:
             assert animatable == "True" or animatable == "False"
             self.animatable = animatable == "True"
 
+        # NB: Animatable implies clone because a property animation requires a
+        # copy of the computed value.
+        #
+        # See components/style/helpers/animated_properties.mako.rs.
+        self.need_clone = need_clone or self.animatable
+
 
 class Shorthand(object):
     def __init__(self, name, sub_properties, experimental=False, internal=False):
         self.name = name
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
         self.derived_from = None
         self.experimental = ("layout.%s.enabled" % name) if experimental else None
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -220,16 +220,18 @@
 
                         try!(item.to_css(dest));
                     }
 
                     Ok(())
                 }
             }
 
+            pub use self::${to_camel_case(name)} as SingleComputedValue;
+
             define_css_keyword_enum! { ${to_camel_case(name)}:
                 % for value in data.longhands_by_name[name].keyword.values_for(product):
                     "${value}" => ${to_rust_ident(value)},
                 % endfor
             }
         }
 
         #[inline]
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -122,32 +122,27 @@ impl AnimatedProperty {
                             style.mutate_${prop.style_struct.ident.strip("_")}().set_${prop.ident}(value);
                         }
                     }
                 % endif
             % endfor
         }
     }
 
-    // NB: Transition properties need clone
     pub fn from_transition_property<C: ComputedValues>(transition_property: &TransitionProperty,
                                                        old_style: &C,
                                                        new_style: &C) -> AnimatedProperty {
-        // TODO: Generalise this for GeckoLib, adding clone_xxx to the
-        // appropiate longhands.
-        let old_style = old_style.as_servo();
-        let new_style = new_style.as_servo();
         match *transition_property {
             TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
             % for prop in data.longhands:
                 % if prop.animatable:
                     TransitionProperty::${prop.camel_case} => {
                         AnimatedProperty::${prop.camel_case}(
-                            old_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone(),
-                            new_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone())
+                            old_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}(),
+                            new_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
                     }
                 % endif
             % endfor
         }
     }
 }
 
 /// A trait used to implement [interpolation][interpolated-types].
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -2,18 +2,17 @@
  * 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 %>
 
 <% data.new_style_struct("Box",
                          inherited=False,
-                         gecko_name="Display",
-                         additional_methods=[Method("transition_count", "usize")]) %>
+                         gecko_name="Display") %>
 
 // TODO(SimonSapin): don't parse `inline-table`, since we don't support it
 <%helpers:longhand name="display"
                    need_clone="True"
                    animatable="False"
                    custom_cascade="${product == 'servo'}">
     <%
         values = """inline block inline-block
@@ -280,17 +279,19 @@
   }
 
   pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
       overflow_x::parse(context, input).map(SpecifiedValue)
   }
 </%helpers:longhand>
 
 // TODO(pcwalton): Multiple transitions.
-<%helpers:longhand name="transition-duration" animatable="False">
+<%helpers:longhand name="transition-duration"
+                   need_index="True"
+                   animatable="False">
     use values::computed::ComputedValueAsSpecified;
     use values::specified::Time;
 
     pub use self::computed_value::T as SpecifiedValue;
     pub use values::specified::Time as SingleSpecifiedValue;
 
     pub mod computed_value {
         use cssparser::ToCss;
@@ -338,17 +339,19 @@
 
     pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
     }
 </%helpers:longhand>
 
 // TODO(pcwalton): Lots more timing functions.
 // TODO(pcwalton): Multiple transitions.
-<%helpers:longhand name="transition-timing-function" animatable="False">
+<%helpers:longhand name="transition-timing-function"
+                   need_index="True"
+                   animatable="False">
     use self::computed_value::{StartEnd, TransitionTimingFunction};
 
     use euclid::point::Point2D;
 
     pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
     pub use self::computed_value::T as SpecifiedValue;
 
     static EASE: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
@@ -536,17 +539,19 @@
         }
     }
 
     pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="transition-property" animatable="False">
+<%helpers:longhand name="transition-property"
+                   need_index="True"
+                   animatable="False">
     pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
     pub use self::computed_value::T as SpecifiedValue;
 
     pub mod computed_value {
         use cssparser::ToCss;
         use std::fmt;
         // NB: Can't generate the type here because it needs all the longhands
         // generated beforehand.
@@ -587,31 +592,37 @@
 
         #[inline]
         fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> computed_value::T {
             (*self).clone()
         }
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="transition-delay" animatable="False">
+<%helpers:longhand name="transition-delay"
+                   need_index="True"
+                   animatable="False">
     pub use properties::longhands::transition_duration::{SingleSpecifiedValue, SpecifiedValue};
     pub use properties::longhands::transition_duration::{computed_value};
     pub use properties::longhands::transition_duration::{get_initial_single_value};
     pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
 </%helpers:longhand>
 
-<%helpers:longhand name="animation-name" animatable="False">
+<%helpers:longhand name="animation-name"
+                   need_index="True"
+                   animatable="False">
     use values::computed::ComputedValueAsSpecified;
 
     pub mod computed_value {
         use cssparser::ToCss;
         use std::fmt;
         use string_cache::Atom;
 
+        pub use string_cache::Atom as SingleComputedValue;
+
         #[derive(Debug, Clone, PartialEq, HeapSizeOf)]
         pub struct T(pub Vec<Atom>);
 
         impl ToCss for T {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 if self.0.is_empty() {
                     return dest.write_str("none")
                 }
@@ -640,35 +651,43 @@
         Ok(SpecifiedValue(try!(input.parse_comma_separated(|input| {
             input.expect_ident().map(Atom::from)
         }))))
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 </%helpers:longhand>
 
-<%helpers:longhand name="animation-duration" animatable="False">
+<%helpers:longhand name="animation-duration"
+                   need_index="True"
+                   animatable="False">
     pub use super::transition_duration::computed_value;
     pub use super::transition_duration::{parse, get_initial_value};
     pub use super::transition_duration::SpecifiedValue;
 </%helpers:longhand>
 
-<%helpers:longhand name="animation-timing-function" animatable="False">
+<%helpers:longhand name="animation-timing-function"
+                   need_index="True"
+                   animatable="False">
     pub use super::transition_timing_function::computed_value;
     pub use super::transition_timing_function::{parse, get_initial_value};
     pub use super::transition_timing_function::SpecifiedValue;
 </%helpers:longhand>
 
-<%helpers:longhand name="animation-iteration-count" animatable="False">
+<%helpers:longhand name="animation-iteration-count"
+                   need_index="True"
+                   animatable="False">
     use values::computed::ComputedValueAsSpecified;
 
     pub mod computed_value {
         use cssparser::ToCss;
         use std::fmt;
 
+        pub use self::AnimationIterationCount as SingleComputedValue;
+
         #[derive(Debug, Clone, PartialEq, HeapSizeOf)]
         pub enum AnimationIterationCount {
             Number(u32),
             Infinite,
         }
 
         impl ToCss for AnimationIterationCount {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -723,28 +742,33 @@
         computed_value::T(vec![AnimationIterationCount::Number(1)])
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 </%helpers:longhand>
 
 ${helpers.keyword_list("animation-direction",
                        "normal reverse alternate alternate-reverse",
+                       need_index=True,
                        animatable=False)}
 
 ${helpers.keyword_list("animation-play-state",
                        "running paused",
                        need_clone=True,
+                       need_index=True,
                        animatable=False)}
 
 ${helpers.keyword_list("animation-fill-mode",
                        "none forwards backwards both",
+                       need_index=True,
                        animatable=False)}
 
-<%helpers:longhand name="animation-delay" animatable="False">
+<%helpers:longhand name="animation-delay"
+                   need_index="True"
+                   animatable="False">
     pub use super::transition_duration::computed_value;
     pub use super::transition_duration::{parse, get_initial_value};
     pub use super::transition_duration::SpecifiedValue;
 </%helpers:longhand>
 
 // CSSOM View Module
 // https://www.w3.org/TR/cssom-view-1/
 ${helpers.single_keyword("scroll-behavior",
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1077,22 +1077,71 @@ pub mod style_struct_traits {
                 #[allow(non_snake_case)]
                 fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T);
                 #[allow(non_snake_case)]
                 fn copy_${longhand.ident}_from(&mut self, other: &Self);
                 % if longhand.need_clone:
                     #[allow(non_snake_case)]
                     fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T;
                 % endif
+                % if longhand.need_index:
+                    #[allow(non_snake_case)]
+                    fn ${longhand.ident}_count(&self) -> usize;
+
+                    #[allow(non_snake_case)]
+                    fn ${longhand.ident}_at(&self, index: usize)
+                        -> longhands::${longhand.ident}::computed_value::SingleComputedValue;
+
+                    #[allow(non_snake_case)]
+                    #[inline]
+                    fn ${longhand.ident}_iter<'a>(&'a self)
+                        -> ${longhand.camel_case}Iter<'a, Self> {
+                        ${longhand.camel_case}Iter {
+                            style_struct: self,
+                            current: 0,
+                            max: self.${longhand.ident}_count(),
+                        }
+                    }
+
+                    #[allow(non_snake_case)]
+                    #[inline]
+                    fn ${longhand.ident}_mod(&self, index: usize)
+                        -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
+                        self.${longhand.ident}_at(index % self.${longhand.ident}_count())
+                    }
+                % endif
             % endfor
             % for additional in style_struct.additional_methods:
                 #[allow(non_snake_case)]
                 ${additional.declare()}
             % endfor
         }
+
+        % for longhand in style_struct.longhands:
+            % if longhand.need_index:
+                pub struct ${longhand.camel_case}Iter<'a, S: ${style_struct.trait_name} + 'static> {
+                    style_struct: &'a S,
+                    current: usize,
+                    max: usize,
+                }
+
+                impl<'a, S: ${style_struct.trait_name} + 'static> Iterator for ${longhand.camel_case}Iter<'a, S> {
+                    type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;
+
+                    fn next(&mut self) -> Option<Self::Item> {
+                        self.current += 1;
+                        if self.current <= self.max {
+                            Some(self.style_struct.${longhand.ident}_at(self.current - 1))
+                        } else {
+                            None
+                        }
+                    }
+                }
+            % endif
+        % endfor
     % endfor
 }
 
 pub mod style_structs {
     use fnv::FnvHasher;
     use super::longhands;
     use std::hash::{Hash, Hasher};
 
@@ -1121,120 +1170,62 @@ pub mod style_structs {
                     && self.${longhand.ident} == other.${longhand.ident}
                 % endfor
             }
         }
         % endif
 
         impl super::style_struct_traits::${style_struct.trait_name} for ${style_struct.servo_struct_name} {
             % for longhand in style_struct.longhands:
+                #[inline]
                 fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
                     self.${longhand.ident} = v;
                 }
+                #[inline]
                 fn copy_${longhand.ident}_from(&mut self, other: &Self) {
                     self.${longhand.ident} = other.${longhand.ident}.clone();
                 }
+                % if longhand.need_clone:
+                    #[inline]
+                    fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
+                        self.${longhand.ident}.clone()
+                    }
+                % endif
+
+                % if longhand.need_index:
+                    fn ${longhand.ident}_count(&self) -> usize {
+                        self.${longhand.ident}.0.len()
+                    }
+
+                    fn ${longhand.ident}_at(&self, index: usize)
+                        -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
+                        self.${longhand.ident}.0[index].clone()
+                    }
+                % endif
             % endfor
             % if style_struct.trait_name == "Border":
                 % for side in ["top", "right", "bottom", "left"]:
-                fn clone_border_${side}_style(&self) -> longhands::border_${side}_style::computed_value::T {
-                    self.border_${side}_style.clone()
-                }
-                fn border_${side}_has_nonzero_width(&self) -> bool {
-                    self.border_${side}_width != ::app_units::Au(0)
-                }
+                    fn border_${side}_has_nonzero_width(&self) -> bool {
+                        self.border_${side}_width != ::app_units::Au(0)
+                    }
                 % endfor
-            % elif style_struct.trait_name == "Box":
-                #[inline]
-                fn clone_display(&self) -> longhands::display::computed_value::T {
-                    self.display.clone()
-                }
-                #[inline]
-                fn clone_position(&self) -> longhands::position::computed_value::T {
-                    self.position.clone()
-                }
-                #[inline]
-                fn clone_float(&self) -> longhands::float::computed_value::T {
-                    self.float.clone()
-                }
-                #[inline]
-                fn clone_overflow_x(&self) -> longhands::overflow_x::computed_value::T {
-                    self.overflow_x.clone()
-                }
-                #[inline]
-                fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T {
-                    self.overflow_y.clone()
-                }
-                #[inline]
-                fn clone_animation_play_state(&self) -> longhands::animation_play_state::computed_value::T {
-                    self.animation_play_state.clone()
-                }
-                #[inline]
-                fn transition_count(&self) -> usize {
-                    self.transition_property.0.len()
-                }
-            % elif style_struct.trait_name == "Color":
-                #[inline]
-                fn clone_color(&self) -> longhands::color::computed_value::T {
-                    self.color.clone()
-                }
             % elif style_struct.trait_name == "Font":
-                #[inline]
-                fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
-                    self.font_size.clone()
-                }
-                #[inline]
-                fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
-                    self.font_weight.clone()
-                }
                 fn compute_font_hash(&mut self) {
                     // Corresponds to the fields in `gfx::font_template::FontTemplateDescriptor`.
                     let mut hasher: FnvHasher = Default::default();
                     hasher.write_u16(self.font_weight as u16);
                     self.font_stretch.hash(&mut hasher);
                     self.font_family.hash(&mut hasher);
                     self.hash = hasher.finish()
                 }
-            % elif style_struct.trait_name == "InheritedBox":
-                #[inline]
-                fn clone_direction(&self) -> longhands::direction::computed_value::T {
-                    self.direction.clone()
-                }
-                #[inline]
-                fn clone_writing_mode(&self) -> longhands::writing_mode::computed_value::T {
-                    self.writing_mode.clone()
-                }
-                #[inline]
-                fn clone_text_orientation(&self) -> longhands::text_orientation::computed_value::T {
-                    self.text_orientation.clone()
-                }
-            % elif style_struct.trait_name == "InheritedText" and product == "servo":
-                #[inline]
-                fn clone__servo_text_decorations_in_effect(&self) ->
-                    longhands::_servo_text_decorations_in_effect::computed_value::T {
-                    self._servo_text_decorations_in_effect.clone()
-                }
             % elif style_struct.trait_name == "Outline":
                 #[inline]
-                fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
-                    self.outline_style.clone()
-                }
-                #[inline]
                 fn outline_has_nonzero_width(&self) -> bool {
                     self.outline_width != ::app_units::Au(0)
                 }
-            % elif style_struct.trait_name == "Position":
-                #[inline]
-                fn clone_align_items(&self) -> longhands::align_items::computed_value::T {
-                    self.align_items.clone()
-                }
-                #[inline]
-                fn clone_align_self(&self) -> longhands::align_self::computed_value::T {
-                    self.align_self.clone()
-                }
             % elif style_struct.trait_name == "Text":
                 <% text_decoration_field = 'text_decoration' if product == 'servo' else 'text_decoration_line' %>
                 #[inline]
                 fn has_underline(&self) -> bool {
                     self.${text_decoration_field}.underline
                 }
                 #[inline]
                 fn has_overline(&self) -> bool {
@@ -1250,37 +1241,30 @@ pub mod style_structs {
     % endfor
 }
 
 pub trait ComputedValues : Debug + Clone + Send + Sync + 'static {
     % for style_struct in data.active_style_structs():
         type Concrete${style_struct.trait_name}: style_struct_traits::${style_struct.trait_name};
     % endfor
 
-        // Temporary bailout case for stuff we haven't made work with the trait
-        // yet - panics for non-Servo implementations.
-        //
-        // Used only for animations. Don't use it in other places.
-        fn as_servo<'a>(&'a self) -> &'a ServoComputedValues;
-        fn as_servo_mut<'a>(&'a mut self) -> &'a mut ServoComputedValues;
-
         fn new(custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
                shareable: bool,
                writing_mode: WritingMode,
                root_font_size: Au,
         % for style_struct in data.active_style_structs():
                ${style_struct.ident}: Arc<Self::Concrete${style_struct.trait_name}>,
         % endfor
         ) -> Self;
 
         fn style_for_child_text_node(parent: &Arc<Self>) -> Arc<Self>;
 
         fn initial_values() -> &'static Self;
 
-        fn do_cascade_property<F: FnOnce(&Vec<CascadePropertyFn<Self>>)>(f: F);
+        fn do_cascade_property<F: FnOnce(&[CascadePropertyFn<Self>])>(f: F);
 
     % for style_struct in data.active_style_structs():
         fn clone_${style_struct.trait_name_lower}(&self) ->
             Arc<Self::Concrete${style_struct.trait_name}>;
         fn get_${style_struct.trait_name_lower}<'a>(&'a self) ->
             &'a Self::Concrete${style_struct.trait_name};
         fn mutate_${style_struct.trait_name_lower}<'a>(&'a mut self) ->
             &'a mut Self::Concrete${style_struct.trait_name};
@@ -1305,19 +1289,16 @@ pub struct ServoComputedValues {
     pub root_font_size: Au,
 }
 
 impl ComputedValues for ServoComputedValues {
     % for style_struct in data.active_style_structs():
         type Concrete${style_struct.trait_name} = style_structs::${style_struct.servo_struct_name};
     % endfor
 
-        fn as_servo<'a>(&'a self) -> &'a ServoComputedValues { self }
-        fn as_servo_mut<'a>(&'a mut self) -> &'a mut ServoComputedValues { self }
-
         fn new(custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
                shareable: bool,
                writing_mode: WritingMode,
                root_font_size: Au,
             % for style_struct in data.active_style_structs():
                ${style_struct.ident}: Arc<style_structs::${style_struct.servo_struct_name}>,
             % endfor
         ) -> Self {
@@ -1341,18 +1322,19 @@ impl ComputedValues for ServoComputedVal
             // Servo layout that is not central to how fragment construction
             // works, but would be difficult to change. (Text node style is
             // also not visible to script.)
             parent.clone()
         }
 
         fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
 
-        fn do_cascade_property<F: FnOnce(&Vec<CascadePropertyFn<Self>>)>(f: F) {
-            CASCADE_PROPERTY.with(|x| f(x));
+        #[inline]
+        fn do_cascade_property<F: FnOnce(&[CascadePropertyFn<Self>])>(f: F) {
+            f(&CASCADE_PROPERTY)
         }
 
     % for style_struct in data.active_style_structs():
         #[inline]
         fn clone_${style_struct.trait_name_lower}(&self) ->
             Arc<Self::Concrete${style_struct.trait_name}> {
                 self.${style_struct.ident}.clone()
             }
@@ -1742,29 +1724,21 @@ fn cascade_with_cached_declarations<C: C
 pub type CascadePropertyFn<C /*: ComputedValues */> =
     extern "Rust" fn(declaration: &PropertyDeclaration,
                      inherited_style: &C,
                      context: &mut computed::Context<C>,
                      seen: &mut PropertyBitField,
                      cacheable: &mut bool,
                      error_reporter: &mut StdBox<ParseErrorReporter + Send>);
 
-pub fn make_cascade_vec<C: ComputedValues>() -> Vec<CascadePropertyFn<C>> {
-    vec![
-        % for property in data.longhands:
-            longhands::${property.ident}::cascade_property,
-        % endfor
-    ]
-}
-
-// This is a thread-local rather than a lazy static to avoid atomic operations when cascading
-// properties.
-thread_local!(static CASCADE_PROPERTY: Vec<CascadePropertyFn<ServoComputedValues>> = {
-    make_cascade_vec::<ServoComputedValues>()
-});
+static CASCADE_PROPERTY: [CascadePropertyFn<ServoComputedValues>; ${len(data.longhands)}] = [
+    % for property in data.longhands:
+        longhands::${property.ident}::cascade_property,
+    % endfor
+];
 
 /// Performs the CSS cascade, computing new styles for an element from its parent style and
 /// optionally a cached related style. The arguments are:
 ///
 ///   * `viewport_size`: The size of the initial viewport.
 ///
 ///   * `applicable_declarations`: The list of CSS rules that matched.
 ///
--- a/servo/components/style/values.rs
+++ b/servo/components/style/values.rs
@@ -1189,16 +1189,21 @@ pub mod specified {
         }
     }
 
     impl Angle {
         #[inline]
         pub fn radians(self) -> f32 {
             self.0
         }
+
+        #[inline]
+        pub fn from_radians(r: f32) -> Self {
+            Angle(r)
+        }
     }
 
     const RAD_PER_DEG: CSSFloat = PI / 180.0;
     const RAD_PER_GRAD: CSSFloat = PI / 200.0;
     const RAD_PER_TURN: CSSFloat = PI * 2.0;
 
     impl Angle {
         /// Parses an angle according to CSS-VALUES ยง 6.1.
--- a/servo/ports/geckolib/properties.mako.rs
+++ b/servo/ports/geckolib/properties.mako.rs
@@ -28,19 +28,18 @@ use glue::ArcHelpers;
 use std::fmt::{self, Debug};
 use std::mem::{transmute, uninitialized, zeroed};
 use std::sync::Arc;
 use std::cmp;
 use style::custom_properties::ComputedValuesMap;
 use style::logical_geometry::WritingMode;
 use style::properties::{CascadePropertyFn, ServoComputedValues, ComputedValues};
 use style::properties::longhands;
-use style::properties::make_cascade_vec;
 use style::properties::style_struct_traits::*;
-use values::{StyleCoordHelpers, ToGeckoStyleCoord, convert_nscolor_to_rgba};
+use values::{StyleCoordHelpers, GeckoStyleCoordConvertible, convert_nscolor_to_rgba};
 use values::{convert_rgba_to_nscolor, debug_assert_unit_is_safe_to_copy};
 use values::round_border_to_device_pixels;
 
 #[derive(Clone, Debug)]
 pub struct GeckoComputedValues {
     % for style_struct in data.style_structs:
     ${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
     % endfor
@@ -69,20 +68,16 @@ impl GeckoComputedValues {
     }
 }
 
 impl ComputedValues for GeckoComputedValues {
 % for style_struct in data.style_structs:
     type Concrete${style_struct.trait_name} = ${style_struct.gecko_struct_name};
 % endfor
 
-    // These will go away, and we will never implement them.
-    fn as_servo<'a>(&'a self) -> &'a ServoComputedValues { unimplemented!() }
-    fn as_servo_mut<'a>(&'a mut self) -> &'a mut ServoComputedValues { unimplemented!() }
-
     fn new(custom_properties: Option<Arc<ComputedValuesMap>>,
            shareable: bool,
            writing_mode: WritingMode,
            root_font_size: Au,
             % for style_struct in data.style_structs:
            ${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
             % endfor
     ) -> Self {
@@ -101,18 +96,19 @@ impl ComputedValues for GeckoComputedVal
         // Gecko expects text nodes to be styled as if they were elements that
         // matched no rules (that is, inherited style structs are inherited and
         // non-inherited style structs are set to their initial values).
         GeckoComputedValues::inherit_from(parent)
     }
 
     fn initial_values() -> &'static Self { &*INITIAL_GECKO_VALUES }
 
-    fn do_cascade_property<F: FnOnce(&Vec<CascadePropertyFn<Self>>)>(f: F) {
-        CASCADE_PROPERTY.with(|x| f(x));
+    #[inline]
+    fn do_cascade_property<F: FnOnce(&[CascadePropertyFn<Self>])>(f: F) {
+        f(&CASCADE_PROPERTY)
     }
 
     % for style_struct in data.style_structs:
     #[inline]
     fn clone_${style_struct.trait_name_lower}(&self) -> Arc<Self::Concrete${style_struct.trait_name}> {
         self.${style_struct.ident}.clone()
     }
     #[inline]
@@ -142,17 +138,23 @@ pub struct ${style_struct.gecko_struct_n
 </%def>
 
 <%def name="impl_simple_setter(ident, gecko_ffi_name)">
     fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         ${set_gecko_property(gecko_ffi_name, "v")}
     }
 </%def>
 
-<%def name="impl_simple_copy(ident, gecko_ffi_name)">
+<%def name="impl_simple_clone(ident, gecko_ffi_name)">
+    fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+        self.gecko.${gecko_ffi_name}
+    }
+</%def>
+
+<%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
     fn copy_${ident}_from(&mut self, other: &Self) {
         self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
     }
 </%def>
 
 <%def name="impl_coord_copy(ident, gecko_ffi_name)">
     fn copy_${ident}_from(&mut self, other: &Self) {
         self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name});
@@ -242,41 +244,59 @@ def set_gecko_property(ffi_name, expr):
         };
         ${set_gecko_property(gecko_ffi_name, "result")}
     }
 </%def>
 
 <%def name="impl_color_copy(ident, gecko_ffi_name, color_flags_ffi_name=None)">
     fn copy_${ident}_from(&mut self, other: &Self) {
         % if color_flags_ffi_name:
-        ${clear_color_flags(color_flags_ffi_name)}
-        if ${get_current_color_flag_from("other.gecko." + color_flags_ffi_name)} {
-            ${set_current_color_flag(color_flags_ffi_name)}
-        }
+            ${clear_color_flags(color_flags_ffi_name)}
+            if ${get_current_color_flag_from("other.gecko." + color_flags_ffi_name)} {
+                ${set_current_color_flag(color_flags_ffi_name)}
+            }
         % endif
-        self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
+        self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}
+    }
+</%def>
+
+<%def name="impl_color_clone(ident, gecko_ffi_name, color_flags_ffi_name=None)">
+    fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+        use cssparser::Color;
+        % if color_flags_ffi_name:
+            if ${get_current_color_flag_from("self.gecko." + color_flags_ffi_name)} {
+                return Color::CurrentColor
+            }
+        % endif
+        Color::RGBA(convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)}))
     }
 </%def>
 
 <%def name="impl_keyword(ident, gecko_ffi_name, keyword, need_clone)">
 <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword)"></%call>
 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
 %if need_clone:
 <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword)"></%call>
 % endif
 </%def>
 
-<%def name="impl_simple(ident, gecko_ffi_name)">
+<%def name="impl_simple(ident, gecko_ffi_name, need_clone=False)">
 <%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
+% if need_clone:
+    <%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
+% endif
 </%def>
 
-<%def name="impl_color(ident, gecko_ffi_name, color_flags_ffi_name=None)">
+<%def name="impl_color(ident, gecko_ffi_name, color_flags_ffi_name=None, need_clone=False)">
 <%call expr="impl_color_setter(ident, gecko_ffi_name, color_flags_ffi_name)"></%call>
 <%call expr="impl_color_copy(ident, gecko_ffi_name, color_flags_ffi_name)"></%call>
+% if need_clone:
+    <%call expr="impl_color_clone(ident, gecko_ffi_name, color_flags_ffi_name)"></%call>
+% endif
 </%def>
 
 <%def name="impl_app_units(ident, gecko_ffi_name, need_clone, round_to_pixels=False)">
     fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         % if round_to_pixels:
         let au_per_device_px = Au(self.gecko.mTwipsPerPixel);
         self.gecko.${gecko_ffi_name} = round_border_to_device_pixels(v, au_per_device_px).0;
         % else:
@@ -286,47 +306,72 @@ def set_gecko_property(ffi_name, expr):
 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
 %if need_clone:
     fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         Au(self.gecko.${gecko_ffi_name})
     }
 % endif
 </%def>
 
-<%def name="impl_split_style_coord(ident, unit_ffi_name, union_ffi_name)">
+<%def name="impl_split_style_coord(ident, unit_ffi_name, union_ffi_name, need_clone=False)">
     fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         v.to_gecko_style_coord(&mut self.gecko.${unit_ffi_name},
                                &mut self.gecko.${union_ffi_name});
     }
     fn copy_${ident}_from(&mut self, other: &Self) {
         debug_assert_unit_is_safe_to_copy(self.gecko.${unit_ffi_name});
         self.gecko.${unit_ffi_name} =  other.gecko.${unit_ffi_name};
         self.gecko.${union_ffi_name} = other.gecko.${union_ffi_name};
     }
+    % if need_clone:
+        fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+            use style::properties::longhands::${ident}::computed_value::T;
+            T::from_gecko_style_coord(&self.gecko.${unit_ffi_name},
+                                      &self.gecko.${union_ffi_name})
+                .expect("clone for ${ident} failed")
+        }
+    % endif
 </%def>
 
-<%def name="impl_style_coord(ident, gecko_ffi_name)">
-<%call expr="impl_split_style_coord(ident, '%s.mUnit' % gecko_ffi_name, '%s.mValue' % gecko_ffi_name)"></%call>
+<%def name="impl_style_coord(ident, gecko_ffi_name, need_clone=False)">
+${impl_split_style_coord(ident,
+                         "%s.mUnit" % gecko_ffi_name,
+                         "%s.mValue" % gecko_ffi_name,
+                         need_clone=need_clone)}
 </%def>
 
-<%def name="impl_corner_style_coord(ident, x_unit_ffi_name, x_union_ffi_name, y_unit_ffi_name, y_union_ffi_name)">
+<%def name="impl_corner_style_coord(ident, x_unit_ffi_name, x_union_ffi_name, \
+                                    y_unit_ffi_name, y_union_ffi_name, need_clone=False)">
     fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         v.0.width.to_gecko_style_coord(&mut self.gecko.${x_unit_ffi_name},
                                        &mut self.gecko.${x_union_ffi_name});
         v.0.height.to_gecko_style_coord(&mut self.gecko.${y_unit_ffi_name},
                                         &mut self.gecko.${y_union_ffi_name});
     }
     fn copy_${ident}_from(&mut self, other: &Self) {
         debug_assert_unit_is_safe_to_copy(self.gecko.${x_unit_ffi_name});
         debug_assert_unit_is_safe_to_copy(self.gecko.${y_unit_ffi_name});
         self.gecko.${x_unit_ffi_name} = other.gecko.${x_unit_ffi_name};
         self.gecko.${x_union_ffi_name} = other.gecko.${x_union_ffi_name};
         self.gecko.${y_unit_ffi_name} = other.gecko.${y_unit_ffi_name};
         self.gecko.${y_union_ffi_name} = other.gecko.${y_union_ffi_name};
     }
+    % if need_clone:
+        fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+            use style::properties::longhands::${ident}::computed_value::T;
+            use euclid::Size2D;
+            let width = GeckoStyleCoordConvertible::from_gecko_style_coord(&self.gecko.${x_unit_ffi_name},
+                                                                           &self.gecko.${x_union_ffi_name})
+                            .expect("Failed to clone ${ident}");
+            let height = GeckoStyleCoordConvertible::from_gecko_style_coord(&self.gecko.${y_unit_ffi_name},
+                                                                            &self.gecko.${y_union_ffi_name})
+                            .expect("Failed to clone ${ident}");
+            T(Size2D::new(width, height))
+        }
+    % endif
 </%def>
 
 <%def name="impl_style_struct(style_struct)">
 impl ${style_struct.gecko_struct_name} {
     #[allow(dead_code, unused_variables)]
     fn initial() -> Arc<Self> {
         let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: unsafe { zeroed() } });
         unsafe {
@@ -419,17 +464,17 @@ impl ${style_struct.trait_name} for ${st
     /*
      * Auto-Generated Methods.
      */
     <%
     for longhand in keyword_longhands:
         impl_keyword(longhand.ident, longhand.gecko_ffi_name, longhand.keyword, longhand.need_clone)
     for longhand in predefined_longhands:
         impl_fn = predefined_types[longhand.predefined_type]
-        impl_fn(longhand.ident, longhand.gecko_ffi_name)
+        impl_fn(longhand.ident, longhand.gecko_ffi_name, need_clone=longhand.need_clone)
     %>
 
     /*
      * Stubs.
      */
     % for longhand in stub_longhands:
     fn set_${longhand.ident}(&mut self, _: longhands::${longhand.ident}::computed_value::T) {
         if cfg!(debug_assertions) {
@@ -441,16 +486,22 @@ impl ${style_struct.trait_name} for ${st
             println!("stylo: Unimplemented property setter: ${longhand.name}");
         }
     }
     % if longhand.need_clone:
     fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
         unimplemented!()
     }
     % endif
+    % if longhand.need_index:
+    fn ${longhand.ident}_count(&self) -> usize { 0 }
+    fn ${longhand.ident}_at(&self, _index: usize) -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
+        unimplemented!()
+    }
+    % endif
     % endfor
     <% additionals = [x for x in style_struct.additional_methods
                       if skip_additionals != "*" and not x.name in skip_additionals.split()] %>
     % for additional in additionals:
     ${additional.stub()}
     % endfor
 }
 </%def>
@@ -510,81 +561,96 @@ fn static_assert() {
                   skip_longhands="${skip_border_longhands}"
                   skip_additionals="*">
 
     % for side in SIDES:
     <% impl_keyword("border_%s_style" % side.ident, "mBorderStyle[%s]" % side.index, border_style_keyword,
                     need_clone=True) %>
 
     <% impl_color("border_%s_color" % side.ident, "mBorderColor[%s]" % side.index,
-                  color_flags_ffi_name="mBorderStyle[%s]" % side.index) %>
+                  color_flags_ffi_name="mBorderStyle[%s]" % side.index, need_clone=True) %>
 
-    <% impl_app_units("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, need_clone=False,
+    <% impl_app_units("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, need_clone=True,
                       round_to_pixels=True) %>
 
     fn border_${side.ident}_has_nonzero_width(&self) -> bool {
         self.gecko.mComputedBorder.${side.ident} != 0
     }
     % endfor
 
     % for corner in CORNERS:
     <% impl_corner_style_coord("border_%s_radius" % corner.ident,
                                "mBorderRadius.mUnits[%s]" % corner.x_index,
                                "mBorderRadius.mValues[%s]" % corner.x_index,
                                "mBorderRadius.mUnits[%s]" % corner.y_index,
-                               "mBorderRadius.mValues[%s]" % corner.y_index) %>
+                               "mBorderRadius.mValues[%s]" % corner.y_index,
+                               need_clone=True) %>
     % endfor
 </%self:impl_trait>
 
 <% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %>
 <%self:impl_trait style_struct_name="Margin"
                   skip_longhands="${skip_margin_longhands}">
 
     % for side in SIDES:
     <% impl_split_style_coord("margin_%s" % side.ident,
                               "mMargin.mUnits[%s]" % side.index,
-                              "mMargin.mValues[%s]" % side.index) %>
+                              "mMargin.mValues[%s]" % side.index,
+                              need_clone=True) %>
     % endfor
 </%self:impl_trait>
 
 <% skip_padding_longhands = " ".join(["padding-%s" % x.ident for x in SIDES]) %>
 <%self:impl_trait style_struct_name="Padding"
                   skip_longhands="${skip_padding_longhands}">
 
     % for side in SIDES:
     <% impl_split_style_coord("padding_%s" % side.ident,
                               "mPadding.mUnits[%s]" % side.index,
-                              "mPadding.mValues[%s]" % side.index) %>
+                              "mPadding.mValues[%s]" % side.index,
+                              need_clone=True) %>
     % endfor
 </%self:impl_trait>
 
 <% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
 <%self:impl_trait style_struct_name="Position"
                   skip_longhands="${skip_position_longhands} z-index box-sizing">
 
     % for side in SIDES:
     <% impl_split_style_coord("%s" % side.ident,
                               "mOffset.mUnits[%s]" % side.index,
-                              "mOffset.mValues[%s]" % side.index) %>
+                              "mOffset.mValues[%s]" % side.index,
+                              need_clone=True) %>
     % endfor
 
     fn set_z_index(&mut self, v: longhands::z_index::computed_value::T) {
         use style::properties::longhands::z_index::computed_value::T;
         match v {
             T::Auto => self.gecko.mZIndex.set_auto(),
             T::Number(n) => self.gecko.mZIndex.set_int(n),
         }
     }
 
     fn copy_z_index_from(&mut self, other: &Self) {
         debug_assert_unit_is_safe_to_copy(self.gecko.mZIndex.mUnit);
         self.gecko.mZIndex.mUnit = other.gecko.mZIndex.mUnit;
         self.gecko.mZIndex.mValue = other.gecko.mZIndex.mValue;
     }
 
+    fn clone_z_index(&self) -> longhands::z_index::computed_value::T {
+        use style::properties::longhands::z_index::computed_value::T;
+
+        if self.gecko.mZIndex.is_auto() {
+            return T::Auto;
+        }
+
+        debug_assert!(self.gecko.mZIndex.is_int());
+        T::Number(self.gecko.mZIndex.get_int())
+    }
+
     fn set_box_sizing(&mut self, v: longhands::box_sizing::computed_value::T) {
         use style::computed_values::box_sizing::T;
         use gecko_bindings::structs::StyleBoxSizing;
         // TODO: guess what to do with box-sizing: padding-box
         self.gecko.mBoxSizing = match v {
             T::content_box => StyleBoxSizing::Content,
             T::border_box => StyleBoxSizing::Border
         }
@@ -597,19 +663,19 @@ fn static_assert() {
                                      ["-moz-outline-radius-{0}".format(x.ident.replace("_", ""))
                                       for x in CORNERS]) %>
 <%self:impl_trait style_struct_name="Outline"
                   skip_longhands="${skip_outline_longhands}"
                   skip_additionals="*">
 
     <% impl_keyword("outline_style", "mOutlineStyle", border_style_keyword, need_clone=True) %>
 
-    <% impl_color("outline_color", "mOutlineColor", color_flags_ffi_name="mOutlineStyle") %>
+    <% impl_color("outline_color", "mOutlineColor", color_flags_ffi_name="mOutlineStyle", need_clone=True) %>
 
-    <% impl_app_units("outline_width", "mActualOutlineWidth", need_clone=False,
+    <% impl_app_units("outline_width", "mActualOutlineWidth", need_clone=True,
                       round_to_pixels=True) %>
 
     % for corner in CORNERS:
     <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.ident.replace("_", ""),
                                "mOutlineRadius.mUnits[%s]" % corner.x_index,
                                "mOutlineRadius.mValues[%s]" % corner.x_index,
                                "mOutlineRadius.mUnits[%s]" % corner.y_index,
                                "mOutlineRadius.mValues[%s]" % corner.y_index) %>
@@ -670,17 +736,17 @@ fn static_assert() {
     }
     fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
         Au(self.gecko.mSize)
     }
 
     fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) {
         self.gecko.mFont.weight = v as u16;
     }
-    <%call expr="impl_simple_copy('font_weight', 'mFont.weight')"></%call>
+    ${impl_simple_copy('font_weight', 'mFont.weight')}
 
     fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
         debug_assert!(self.gecko.mFont.weight >= 100);
         debug_assert!(self.gecko.mFont.weight <= 900);
         debug_assert!(self.gecko.mFont.weight % 10 == 0);
         unsafe { transmute(self.gecko.mFont.weight) }
     }
 
@@ -692,31 +758,31 @@ fn static_assert() {
 <%self:impl_trait style_struct_name="Box" skip_longhands="display overflow-y vertical-align -moz-binding">
 
     // We manually-implement the |display| property until we get general
     // infrastructure for preffing certain values.
     <% display_keyword = Keyword("display", "inline block inline-block table inline-table table-row-group " +
                                             "table-header-group table-footer-group table-row table-column-group " +
                                             "table-column table-cell table-caption list-item flex none " +
                                             "-moz-box -moz-inline-box") %>
-    <%call expr="impl_keyword('display', 'mDisplay', display_keyword, True)"></%call>
+    ${impl_keyword('display', 'mDisplay', display_keyword, True)}
 
     // overflow-y is implemented as a newtype of overflow-x, so we need special handling.
     // We could generalize this if we run into other newtype keywords.
     <% overflow_x = data.longhands_by_name["overflow-x"] %>
     fn set_overflow_y(&mut self, v: longhands::overflow_y::computed_value::T) {
         use style::properties::longhands::overflow_x::computed_value::T as BaseType;
         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
         self.gecko.mOverflowY = match v.0 {
             % for value in overflow_x.keyword.values_for('gecko'):
                 BaseType::${to_rust_ident(value)} => structs::${overflow_x.keyword.gecko_constant(value)} as u8,
             % endfor
         };
     }
-    <%call expr="impl_simple_copy('overflow_y', 'mOverflowY')"></%call>
+    ${impl_simple_copy('overflow_y', 'mOverflowY')}
     fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T {
         use style::properties::longhands::overflow_x::computed_value::T as BaseType;
         use style::properties::longhands::overflow_y::computed_value::T as NewType;
         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
         match self.gecko.mOverflowY as u32 {
             % for value in overflow_x.keyword.values_for('gecko'):
             structs::${overflow_x.keyword.gecko_constant(value)} => NewType(BaseType::${to_rust_ident(value)}),
             % endfor
@@ -732,16 +798,36 @@ fn static_assert() {
             % for value in keyword.values_for('gecko'):
                 T::${to_rust_ident(value)} =>
                     self.gecko.mVerticalAlign.set_enum(structs::${keyword.gecko_constant(value)} as i32),
             % endfor
             T::LengthOrPercentage(v) => self.gecko.mVerticalAlign.set(v),
         }
     }
 
+    fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T {
+        use style::properties::longhands::vertical_align::computed_value::T;
+        use style::values::computed::LengthOrPercentage;
+
+        if self.gecko.mVerticalAlign.is_enum() {
+            match self.gecko.mVerticalAlign.get_enum() as u32 {
+                % for value in keyword.values_for('gecko'):
+                    structs::${keyword.gecko_constant(value)}
+                        => T::${to_rust_ident(value)},
+                % endfor
+                _ => panic!("Unexpected enum variant for vertical-align"),
+            }
+        } else {
+            let v = LengthOrPercentage::from_gecko_style_coord(&self.gecko.mVerticalAlign.mUnit,
+                                                               &self.gecko.mVerticalAlign.mValue)
+                .expect("Expected length or percentage for vertical-align");
+            T::LengthOrPercentage(v)
+        }
+    }
+
     <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
 
     fn set__moz_binding(&mut self, v: longhands::_moz_binding::computed_value::T) {
         use style::properties::longhands::_moz_binding::SpecifiedValue as BindingValue;
         match v {
             BindingValue::None => debug_assert!(self.gecko.mBinding.mRawPtr.is_null()),
             BindingValue::Url(ref url, ref extra_data) => {
                 unsafe {
@@ -765,17 +851,17 @@ fn static_assert() {
 // add support for parsing these lists in servo and pushing to nsTArray's.
 <% skip_background_longhands = """background-color background-repeat
                                   background-image background-clip
                                   background-origin background-attachment""" %>
 <%self:impl_trait style_struct_name="Background"
                   skip_longhands="${skip_background_longhands}"
                   skip_additionals="*">
 
-    <% impl_color("background_color", "mBackgroundColor") %>
+    <% impl_color("background_color", "mBackgroundColor", need_clone=True) %>
 
     fn copy_background_repeat_from(&mut self, other: &Self) {
         self.gecko.mImage.mRepeatCount = cmp::min(1, other.gecko.mImage.mRepeatCount);
         self.gecko.mImage.mLayers.mFirstElement.mRepeat =
             other.gecko.mImage.mLayers.mFirstElement.mRepeat;
     }
 
     fn set_background_repeat(&mut self, v: longhands::background_repeat::computed_value::T) {
@@ -940,78 +1026,99 @@ fn static_assert() {
                 }
             }
         }
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="List" skip_longhands="list-style-type" skip_additionals="*">
 
-    <% impl_keyword_setter("list_style_type", "__LIST_STYLE_TYPE__",
-                           data.longhands_by_name["list-style-type"].keyword) %>
+    ${impl_keyword_setter("list_style_type", "__LIST_STYLE_TYPE__",
+                           data.longhands_by_name["list-style-type"].keyword)}
     fn copy_list_style_type_from(&mut self, other: &Self) {
         unsafe {
             Gecko_CopyListStyleTypeFrom(&mut self.gecko, &other.gecko);
         }
     }
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedText"
                   skip_longhands="text-align line-height">
 
     <% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left " +
                                                   "-moz-right match-parent") %>
-    <%call expr="impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)"></%call>
+    ${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)}
 
     fn set_line_height(&mut self, v: longhands::line_height::computed_value::T) {
         use style::properties::longhands::line_height::computed_value::T;
         // FIXME: Align binary representations and ditch |match| for cast + static_asserts
         match v {
             T::Normal => self.gecko.mLineHeight.set_normal(),
             T::Length(val) => self.gecko.mLineHeight.set_coord(val),
             T::Number(val) => self.gecko.mLineHeight.set_factor(val),
             T::MozBlockHeight =>
                 self.gecko.mLineHeight.set_enum(structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT as i32),
         }
     }
 
+    fn clone_line_height(&self) -> longhands::line_height::computed_value::T {
+        use style::properties::longhands::line_height::computed_value::T;
+        if self.gecko.mLineHeight.is_normal() {
+            return T::Normal;
+        }
+        if self.gecko.mLineHeight.is_coord() {
+            return T::Length(self.gecko.mLineHeight.get_coord());
+        }
+        if self.gecko.mLineHeight.is_factor() {
+            return T::Number(self.gecko.mLineHeight.get_factor());
+        }
+
+        debug_assert!(self.gecko.mLineHeight.get_enum() == structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT as i32);
+        T::MozBlockHeight
+    }
+
     <%call expr="impl_coord_copy('line_height', 'mLineHeight')"></%call>
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Text"
                   skip_longhands="text-decoration-color text-decoration-line"
                   skip_additionals="*">
 
-    <% impl_color("text_decoration_color", "mTextDecorationColor",
-                  color_flags_ffi_name="mTextDecorationStyle") %>
+    ${impl_color("text_decoration_color", "mTextDecorationColor",
+                  color_flags_ffi_name="mTextDecorationStyle", need_clone=True)}
 
     fn set_text_decoration_line(&mut self, v: longhands::text_decoration_line::computed_value::T) {
         let mut bits: u8 = 0;
         if v.underline {
             bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8;
         }
         if v.overline {
             bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8;
         }
         if v.line_through {
             bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8;
         }
         self.gecko.mTextDecorationLine = bits;
     }
 
-    <%call expr="impl_simple_copy('text_decoration_line', 'mTextDecorationLine')"></%call>
+    ${impl_simple_copy('text_decoration_line', 'mTextDecorationLine')}
 
+    #[inline]
     fn has_underline(&self) -> bool {
         (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0
     }
+
+    #[inline]
     fn has_overline(&self) -> bool {
         (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0
     }
+
+    #[inline]
     fn has_line_through(&self) -> bool {
         (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="SVG"
                   skip_longhands="flood-color lighting-color stop-color"
                   skip_additionals="*">
@@ -1021,33 +1128,31 @@ fn static_assert() {
     <% impl_color("lighting_color", "mLightingColor") %>
 
     <% impl_color("stop_color", "mStopColor") %>
 
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Color"
                   skip_longhands="*">
-
     fn set_color(&mut self, v: longhands::color::computed_value::T) {
         let result = convert_rgba_to_nscolor(&v);
         ${set_gecko_property("mColor", "result")}
     }
 
     <%call expr="impl_simple_copy('color', 'mColor')"></%call>
 
     fn clone_color(&self) -> longhands::color::computed_value::T {
         let color = ${get_gecko_property("mColor")} as u32;
         convert_nscolor_to_rgba(color)
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Pointing"
                   skip_longhands="cursor">
-
     fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
         use style::properties::longhands::cursor::computed_value::T;
         use style_traits::cursor::Cursor;
 
         self.gecko.mCursor = match v {
             T::AutoCursor => structs::NS_STYLE_CURSOR_AUTO,
             T::SpecifiedCursor(cursor) => match cursor {
                 Cursor::None => structs::NS_STYLE_CURSOR_NONE,
@@ -1085,31 +1190,29 @@ fn static_assert() {
                 Cursor::AllScroll => structs::NS_STYLE_CURSOR_ALL_SCROLL,
                 Cursor::ZoomIn => structs::NS_STYLE_CURSOR_ZOOM_IN,
                 Cursor::ZoomOut => structs::NS_STYLE_CURSOR_ZOOM_OUT,
             }
         } as u8;
     }
 
     ${impl_simple_copy('cursor', 'mCursor')}
-
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Column"
                   skip_longhands="column-width">
 
     fn set_column_width(&mut self, v: longhands::column_width::computed_value::T) {
         match v.0 {
             Some(au) => self.gecko.mColumnWidth.set_coord(au),
             None => self.gecko.mColumnWidth.set_auto(),
         }
     }
 
     ${impl_coord_copy('column_width', 'mColumnWidth')}
-
 </%self:impl_trait>
 
 <%def name="define_ffi_struct_accessor(style_struct)">
 #[no_mangle]
 #[allow(non_snake_case, unused_variables)]
 pub extern "C" fn Servo_GetStyle${style_struct.gecko_name}(computed_values: *mut ServoComputedValues)
   -> *const ${style_struct.gecko_ffi_name} {
     type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
@@ -1134,13 +1237,13 @@ lazy_static! {
         % endfor
         custom_properties: None,
         shareable: true,
         writing_mode: WritingMode::empty(),
         root_font_size: longhands::font_size::get_initial_value(),
     };
 }
 
-// This is a thread-local rather than a lazy static to avoid atomic operations when cascading
-// properties.
-thread_local!(static CASCADE_PROPERTY: Vec<CascadePropertyFn<GeckoComputedValues>> = {
-    make_cascade_vec::<GeckoComputedValues>()
-});
+static CASCADE_PROPERTY: [CascadePropertyFn<GeckoComputedValues>; ${len(data.longhands)}] = [
+    % for property in data.longhands:
+        longhands::${property.ident}::cascade_property,
+    % endfor
+];
--- a/servo/ports/geckolib/values.rs
+++ b/servo/ports/geckolib/values.rs
@@ -6,95 +6,189 @@ use app_units::Au;
 use cssparser::RGBA;
 use gecko_bindings::structs::{nsStyleCoord, nsStyleUnion, nsStyleUnit};
 use std::cmp::max;
 use style::values::computed::Angle;
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 
 pub trait StyleCoordHelpers {
     fn copy_from(&mut self, other: &Self);
-    fn set<T: ToGeckoStyleCoord>(&mut self, val: T);
+    fn set<T: GeckoStyleCoordConvertible>(&mut self, val: T);
+
     fn set_auto(&mut self);
+    fn is_auto(&self) -> bool;
+
     fn set_normal(&mut self);
+    fn is_normal(&self) -> bool;
+
     fn set_coord(&mut self, val: Au);
+    fn is_coord(&self) -> bool;
+    fn get_coord(&self) -> Au;
+
     fn set_int(&mut self, val: i32);
+    fn is_int(&self) -> bool;
+    fn get_int(&self) -> i32;
+
     fn set_enum(&mut self, val: i32);
+    fn is_enum(&self) -> bool;
+    fn get_enum(&self) -> i32;
+
     fn set_percent(&mut self, val: f32);
+    fn is_percent(&self) -> bool;
+    fn get_percent(&self) -> f32;
+
     fn set_factor(&mut self, val: f32);
+    fn is_factor(&self) -> bool;
+    fn get_factor(&self) -> f32;
 }
 
 impl StyleCoordHelpers for nsStyleCoord {
+    #[inline]
     fn copy_from(&mut self, other: &Self) {
         debug_assert_unit_is_safe_to_copy(self.mUnit);
         debug_assert_unit_is_safe_to_copy(other.mUnit);
         self.mUnit = other.mUnit;
         self.mValue = other.mValue;
     }
 
-    fn set<T: ToGeckoStyleCoord>(&mut self, val: T) {
+    #[inline]
+    fn set<T: GeckoStyleCoordConvertible>(&mut self, val: T) {
         val.to_gecko_style_coord(&mut self.mUnit, &mut self.mValue);
     }
 
+    #[inline]
     fn set_auto(&mut self) {
         self.mUnit = nsStyleUnit::eStyleUnit_Auto;
         unsafe { *self.mValue.mInt.as_mut() = 0; }
     }
+    #[inline]
+    fn is_auto(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Auto
+    }
 
+    #[inline]
     fn set_normal(&mut self) {
         self.mUnit = nsStyleUnit::eStyleUnit_Normal;
         unsafe { *self.mValue.mInt.as_mut() = 0; }
     }
+    #[inline]
+    fn is_normal(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Normal
+    }
 
+    #[inline]
     fn set_coord(&mut self, val: Au) {
         self.mUnit = nsStyleUnit::eStyleUnit_Coord;
         unsafe { *self.mValue.mInt.as_mut() = val.0; }
     }
+    #[inline]
+    fn is_coord(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Coord
+    }
+    #[inline]
+    fn get_coord(&self) -> Au {
+        debug_assert!(self.is_coord());
+        Au(unsafe { *self.mValue.mInt.as_ref() })
+    }
 
+    #[inline]
+    fn set_int(&mut self, val: i32) {
+        self.mUnit = nsStyleUnit::eStyleUnit_Integer;
+        unsafe { *self.mValue.mInt.as_mut() = val; }
+    }
+    #[inline]
+    fn is_int(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Integer
+    }
+    #[inline]
+    fn get_int(&self) -> i32 {
+        debug_assert!(self.is_int());
+        unsafe { *self.mValue.mInt.as_ref() }
+    }
+
+    #[inline]
+    fn set_enum(&mut self, val: i32) {
+        self.mUnit = nsStyleUnit::eStyleUnit_Enumerated;
+        unsafe { *self.mValue.mInt.as_mut() = val; }
+    }
+    #[inline]
+    fn is_enum(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Enumerated
+    }
+    #[inline]
+    fn get_enum(&self) -> i32 {
+        debug_assert!(self.is_enum());
+        unsafe { *self.mValue.mInt.as_ref() }
+    }
+
+    #[inline]
     fn set_percent(&mut self, val: f32) {
         self.mUnit = nsStyleUnit::eStyleUnit_Percent;
         unsafe { *self.mValue.mFloat.as_mut() = val; }
     }
-
-    fn set_int(&mut self, val: i32) {
-        self.mUnit = nsStyleUnit::eStyleUnit_Integer;
-        unsafe { *self.mValue.mInt.as_mut() = val; }
+    #[inline]
+    fn is_percent(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Percent
+    }
+    #[inline]
+    fn get_percent(&self) -> f32 {
+        debug_assert!(self.is_percent());
+        unsafe { *self.mValue.mFloat.as_ref() }
     }
 
-    fn set_enum(&mut self, val: i32) {
-        self.mUnit = nsStyleUnit::eStyleUnit_Enumerated;
-        unsafe { *self.mValue.mInt.as_mut() = val; }
-    }
-
+    #[inline]
     fn set_factor(&mut self, val: f32) {
         self.mUnit = nsStyleUnit::eStyleUnit_Factor;
         unsafe { *self.mValue.mFloat.as_mut() = val; }
     }
+    #[inline]
+    fn is_factor(&self) -> bool {
+        self.mUnit == nsStyleUnit::eStyleUnit_Factor
+    }
+    #[inline]
+    fn get_factor(&self) -> f32 {
+        debug_assert!(self.is_factor());
+        unsafe { *self.mValue.mFloat.as_ref() }
+    }
 }
 
-pub trait ToGeckoStyleCoord {
+pub trait GeckoStyleCoordConvertible : Sized {
     fn to_gecko_style_coord(&self, unit: &mut nsStyleUnit, union: &mut nsStyleUnion);
+    fn from_gecko_style_coord(unit: &nsStyleUnit, union: &nsStyleUnion) -> Option<Self>;
 }
 
-impl ToGeckoStyleCoord for LengthOrPercentage {
+impl GeckoStyleCoordConvertible for LengthOrPercentage {
     fn to_gecko_style_coord(&self, unit: &mut nsStyleUnit, union: &mut nsStyleUnion) {
         match *self {
             LengthOrPercentage::Length(au) => {
                 *unit = nsStyleUnit::eStyleUnit_Coord;
                 unsafe { *union.mInt.as_mut() = au.0; }
             },
             LengthOrPercentage::Percentage(p) => {
                 *unit = nsStyleUnit::eStyleUnit_Percent;
                 unsafe { *union.mFloat.as_mut() = p; }
             },
             LengthOrPercentage::Calc(_) => unimplemented!(),
         };
     }
+
+    fn from_gecko_style_coord(unit: &nsStyleUnit, union: &nsStyleUnion) -> Option<Self> {
+        match *unit {
+            nsStyleUnit::eStyleUnit_Coord
+                => Some(LengthOrPercentage::Length(Au(unsafe { *union.mInt.as_ref() }))),
+            nsStyleUnit::eStyleUnit_Percent
+                => Some(LengthOrPercentage::Percentage(unsafe { *union.mFloat.as_ref() })),
+            nsStyleUnit::eStyleUnit_Calc
+                => unimplemented!(),
+            _ => None,
+        }
+    }
 }
 
-impl ToGeckoStyleCoord for LengthOrPercentageOrAuto {
+impl GeckoStyleCoordConvertible for LengthOrPercentageOrAuto {
     fn to_gecko_style_coord(&self, unit: &mut nsStyleUnit, union: &mut nsStyleUnion) {
         match *self {
             LengthOrPercentageOrAuto::Length(au) => {
                 *unit = nsStyleUnit::eStyleUnit_Coord;
                 unsafe { *union.mInt.as_mut() = au.0; }
             },
             LengthOrPercentageOrAuto::Percentage(p) => {
                 *unit = nsStyleUnit::eStyleUnit_Percent;
@@ -102,19 +196,33 @@ impl ToGeckoStyleCoord for LengthOrPerce
             },
             LengthOrPercentageOrAuto::Auto => {
                 *unit = nsStyleUnit::eStyleUnit_Auto;
                 unsafe { *union.mInt.as_mut() = 0; }
             },
             LengthOrPercentageOrAuto::Calc(_) => unimplemented!(),
         };
     }
+
+    fn from_gecko_style_coord(unit: &nsStyleUnit, union: &nsStyleUnion) -> Option<Self> {
+        match *unit {
+            nsStyleUnit::eStyleUnit_Auto
+                => Some(LengthOrPercentageOrAuto::Auto),
+            nsStyleUnit::eStyleUnit_Coord
+                => Some(LengthOrPercentageOrAuto::Length(Au(unsafe { *union.mInt.as_ref() }))),
+            nsStyleUnit::eStyleUnit_Percent
+                => Some(LengthOrPercentageOrAuto::Percentage(unsafe { *union.mFloat.as_ref() })),
+            nsStyleUnit::eStyleUnit_Calc
+                => unimplemented!(),
+            _ => None,
+        }
+    }
 }
 
-impl ToGeckoStyleCoord for LengthOrPercentageOrNone {
+impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone {
     fn to_gecko_style_coord(&self, unit: &mut nsStyleUnit, union: &mut nsStyleUnion) {
         match *self {
             LengthOrPercentageOrNone::Length(au) => {
                 *unit = nsStyleUnit::eStyleUnit_Coord;
                 unsafe { *union.mInt.as_mut() = au.0; }
             },
             LengthOrPercentageOrNone::Percentage(p) => {
                 *unit = nsStyleUnit::eStyleUnit_Percent;
@@ -122,36 +230,62 @@ impl ToGeckoStyleCoord for LengthOrPerce
             },
             LengthOrPercentageOrNone::None => {
                 *unit = nsStyleUnit::eStyleUnit_None;
                 unsafe { *union.mInt.as_mut() = 0; }
             },
             LengthOrPercentageOrNone::Calc(_) => unimplemented!(),
         };
     }
+
+    fn from_gecko_style_coord(unit: &nsStyleUnit, union: &nsStyleUnion) -> Option<Self> {
+        match *unit {
+            nsStyleUnit::eStyleUnit_None
+                => Some(LengthOrPercentageOrNone::None),
+            nsStyleUnit::eStyleUnit_Coord
+                => Some(LengthOrPercentageOrNone::Length(Au(unsafe { *union.mInt.as_ref() }))),
+            nsStyleUnit::eStyleUnit_Percent
+                => Some(LengthOrPercentageOrNone::Percentage(unsafe { *union.mFloat.as_ref() })),
+            nsStyleUnit::eStyleUnit_Calc
+                => unimplemented!(),
+            _ => None,
+        }
+    }
 }
 
-impl<T: ToGeckoStyleCoord> ToGeckoStyleCoord for Option<T> {
+impl<T: GeckoStyleCoordConvertible> GeckoStyleCoordConvertible for Option<T> {
     fn to_gecko_style_coord(&self, unit: &mut nsStyleUnit, union: &mut nsStyleUnion) {
         if let Some(ref me) = *self {
             me.to_gecko_style_coord(unit, union);
         } else {
             *unit = nsStyleUnit::eStyleUnit_None;
             unsafe { *union.mInt.as_mut() = 0; }
         }
     }
+
+    fn from_gecko_style_coord(unit: &nsStyleUnit, union: &nsStyleUnion) -> Option<Self> {
+        Some(T::from_gecko_style_coord(unit, union))
+    }
 }
 
-impl ToGeckoStyleCoord for Angle {
+impl GeckoStyleCoordConvertible for Angle {
     fn to_gecko_style_coord(&self,
                             unit: &mut nsStyleUnit,
                             union: &mut nsStyleUnion) {
         *unit = nsStyleUnit::eStyleUnit_Radian;
         unsafe { *union.mFloat.as_mut() = self.radians() };
     }
+
+    fn from_gecko_style_coord(unit: &nsStyleUnit, union: &nsStyleUnion) -> Option<Self> {
+        if *unit == nsStyleUnit::eStyleUnit_Radian {
+            Some(Angle::from_radians(unsafe { *union.mFloat.as_ref() }))
+        } else {
+            None
+        }
+    }
 }
 
 pub fn convert_rgba_to_nscolor(rgba: &RGBA) -> u32 {
     (((rgba.alpha * 255.0).round() as u32) << 24) |
     (((rgba.blue  * 255.0).round() as u32) << 16) |
     (((rgba.green * 255.0).round() as u32) << 8) |
      ((rgba.red   * 255.0).round() as u32)
 }