/* 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/. */

// `data` comes from components/style/properties.mako.rs; see build.rs for more details.

<%!
    from data import to_rust_ident
    from data import Keyword
%>

use app_units::Au;
use custom_properties::ComputedValuesMap;
% for style_struct in data.style_structs:
use gecko_bindings::structs::${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_Construct_${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
use gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endfor
use gecko_bindings::bindings::{Gecko_CopyMozBindingFrom, Gecko_CopyListStyleTypeFrom};
use gecko_bindings::bindings::{Gecko_SetMozBinding, Gecko_SetListStyleType};
use gecko_bindings::bindings::{Gecko_SetNullImageValue, Gecko_SetGradientImageValue};
use gecko_bindings::bindings::{Gecko_EnsureImageLayersLength, Gecko_CreateGradient};
use gecko_bindings::bindings::{Gecko_CopyImageValueFrom, Gecko_CopyFontFamilyFrom};
use gecko_bindings::bindings::{Gecko_FontFamilyList_AppendGeneric, Gecko_FontFamilyList_AppendNamed};
use gecko_bindings::bindings::{Gecko_FontFamilyList_Clear, Gecko_InitializeImageLayer};
use gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
use gecko_bindings::structs;
use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
use gecko_bindings::sugar::ownership::HasArcFFI;
use gecko::values::{StyleCoordHelpers, GeckoStyleCoordConvertible, convert_nscolor_to_rgba};
use gecko::values::convert_rgba_to_nscolor;
use gecko::values::round_border_to_device_pixels;
use logical_geometry::WritingMode;
use properties::CascadePropertyFn;
use properties::longhands;
use std::fmt::{self, Debug};
use std::mem::{transmute, zeroed};
use std::ptr;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::sync::Arc;
use std::cmp;

pub mod style_structs {
    % for style_struct in data.style_structs:
    pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
    % endfor
}

#[derive(Clone, Debug)]
pub struct ComputedValues {
    % for style_struct in data.style_structs:
    ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
    % endfor

    custom_properties: Option<Arc<ComputedValuesMap>>,
    shareable: bool,
    pub writing_mode: WritingMode,
    pub root_font_size: Au,
}

impl ComputedValues {
    pub fn inherit_from(parent: &Arc<Self>) -> Arc<Self> {
        Arc::new(ComputedValues {
            custom_properties: parent.custom_properties.clone(),
            shareable: parent.shareable,
            writing_mode: parent.writing_mode,
            root_font_size: parent.root_font_size,
            % for style_struct in data.style_structs:
            % if style_struct.inherited:
            ${style_struct.ident}: parent.${style_struct.ident}.clone(),
            % else:
            ${style_struct.ident}: Self::initial_values().${style_struct.ident}.clone(),
            % endif
            % endfor
        })
    }

    pub 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_structs::${style_struct.name}>,
            % endfor
    ) -> Self {
        ComputedValues {
            custom_properties: custom_properties,
            shareable: shareable,
            writing_mode: writing_mode,
            root_font_size: root_font_size,
            % for style_struct in data.style_structs:
            ${style_struct.ident}: ${style_struct.ident},
            % endfor
        }
    }

    pub fn style_for_child_text_node(parent: &Arc<Self>) -> Arc<Self> {
        // 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).
        ComputedValues::inherit_from(parent)
    }

    pub fn initial_values() -> &'static Self {
        unsafe {
            debug_assert!(!raw_initial_values().is_null());
            &*raw_initial_values()
        }
    }

    pub unsafe fn initialize() {
        debug_assert!(raw_initial_values().is_null());
        set_raw_initial_values(Box::into_raw(Box::new(ComputedValues {
            % for style_struct in data.style_structs:
               ${style_struct.ident}: style_structs::${style_struct.name}::initial(),
            % endfor
            custom_properties: None,
            shareable: true,
            writing_mode: WritingMode::empty(),
            root_font_size: longhands::font_size::get_initial_value(),
        })));
    }

    pub unsafe fn shutdown() {
        debug_assert!(!raw_initial_values().is_null());
        let _ = Box::from_raw(raw_initial_values());
        set_raw_initial_values(ptr::null_mut());
    }

    #[inline]
    pub fn do_cascade_property<F: FnOnce(&[CascadePropertyFn])>(f: F) {
        f(&CASCADE_PROPERTY)
    }

    % for style_struct in data.style_structs:
    #[inline]
    pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
        self.${style_struct.ident}.clone()
    }
    #[inline]
    pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
        &self.${style_struct.ident}
    }
    #[inline]
    pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
        Arc::make_mut(&mut self.${style_struct.ident})
    }
    % endfor

    pub fn custom_properties(&self) -> Option<Arc<ComputedValuesMap>> {
        self.custom_properties.as_ref().map(|x| x.clone())
    }

    pub fn root_font_size(&self) -> Au { self.root_font_size }
    pub fn set_root_font_size(&mut self, s: Au) { self.root_font_size = s; }
    pub fn set_writing_mode(&mut self, mode: WritingMode) { self.writing_mode = mode; }

    // FIXME(bholley): Implement this properly.
    #[inline]
    pub fn is_multicol(&self) -> bool { false }
}

<%def name="declare_style_struct(style_struct)">
pub struct ${style_struct.gecko_struct_name} {
    gecko: ${style_struct.gecko_ffi_name},
}
</%def>

<%def name="impl_simple_setter(ident, gecko_ffi_name)">
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        ${set_gecko_property(gecko_ffi_name, "v")}
    }
</%def>

<%def name="impl_simple_clone(ident, gecko_ffi_name)">
    #[allow(non_snake_case)]
    pub 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)">
    #[allow(non_snake_case)]
    pub 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)">
    #[allow(non_snake_case)]
    pub fn copy_${ident}_from(&mut self, other: &Self) {
        self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name});
    }
</%def>

<%!
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')">
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        use properties::longhands::${ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
        let result = match v {
            % for value in keyword.values_for('gecko'):
                Keyword::${to_rust_ident(value)} =>
                    structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
            % endfor
        };
        ${set_gecko_property(gecko_ffi_name, "result")}
    }
</%def>

<%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword)">
    #[allow(non_snake_case)]
    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        use properties::longhands::${ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
        match ${get_gecko_property(gecko_ffi_name)} ${keyword.maybe_cast("u32")} {
            % for value in keyword.values_for('gecko'):
            structs::${keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)},
            % endfor
            x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x),
        }
    }
</%def>

<%def name="impl_color_setter(ident, gecko_ffi_name, complex_color=True)">
    #[allow(unreachable_code)]
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        % if complex_color:
            let result = v.into();
        % else:
            use cssparser::Color;
            let result = match v {
                Color::RGBA(rgba) => convert_rgba_to_nscolor(&rgba),
                // FIXME #13547
                Color::CurrentColor => 0,
            };
        % endif
        ${set_gecko_property(gecko_ffi_name, "result")}
    }
</%def>

<%def name="impl_color_copy(ident, gecko_ffi_name, complex_color=True)">
    #[allow(non_snake_case)]
    pub fn copy_${ident}_from(&mut self, other: &Self) {
        let color = ${get_gecko_property(gecko_ffi_name, self_param = "other")};
        ${set_gecko_property(gecko_ffi_name, "color")};
    }
</%def>

<%def name="impl_color_clone(ident, gecko_ffi_name, complex_color=True)">
    #[allow(non_snake_case)]
    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        % if complex_color:
            ${get_gecko_property(gecko_ffi_name)}.into()
        % else:
            use cssparser::Color;
            Color::RGBA(convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)}))
        % endif
    }
</%def>

<%def name="impl_keyword(ident, gecko_ffi_name, keyword, need_clone, **kwargs)">
<%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, **kwargs)"></%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, 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, need_clone=False, complex_color=True)">
<%call expr="impl_color_setter(ident, gecko_ffi_name, complex_color)"></%call>
<%call expr="impl_color_copy(ident, gecko_ffi_name, complex_color)"></%call>
% if need_clone:
    <%call expr="impl_color_clone(ident, gecko_ffi_name, complex_color)"></%call>
% endif
</%def>

<%def name="impl_app_units(ident, gecko_ffi_name, need_clone, round_to_pixels=False)">
    #[allow(non_snake_case)]
    pub 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:
        self.gecko.${gecko_ffi_name} = v.0;
        % endif
    }
<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
%if need_clone:
    #[allow(non_snake_case)]
    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        Au(self.gecko.${gecko_ffi_name})
    }
% endif
</%def>

<%def name="impl_split_style_coord(ident, gecko_ffi_name, index, need_clone=False)">
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        v.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${index}));
    }
    #[allow(non_snake_case)]
    pub fn copy_${ident}_from(&mut self, other: &Self) {
        self.gecko.${gecko_ffi_name}.data_at_mut(${index}).copy_from(&other.gecko.${gecko_ffi_name}.data_at(${index}));
    }
    % if need_clone:
        #[allow(non_snake_case)]
        pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
            use properties::longhands::${ident}::computed_value::T;
            T::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}.data_at(${index}))
                .expect("clone for ${ident} failed")
        }
    % endif
</%def>

<%def name="impl_style_coord(ident, gecko_ffi_name, need_clone=False)">
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        v.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name});
    }
    #[allow(non_snake_case)]
    pub fn copy_${ident}_from(&mut self, other: &Self) {
        self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name});
    }
    % if need_clone:
        #[allow(non_snake_case)]
        pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
            use properties::longhands::${ident}::computed_value::T;
            T::from_gecko_style_coord(&self.gecko.${gecko_ffi_name})
                .expect("clone for ${ident} failed")
        }
    % endif
</%def>

<%def name="impl_corner_style_coord(ident, gecko_ffi_name, x_index, y_index, need_clone=False)">
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        v.0.width.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${x_index}));
        v.0.height.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${y_index}));
    }
    #[allow(non_snake_case)]
    pub fn copy_${ident}_from(&mut self, other: &Self) {
        self.gecko.${gecko_ffi_name}.data_at_mut(${x_index})
                  .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${x_index}));
        self.gecko.${gecko_ffi_name}.data_at_mut(${y_index})
                  .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${y_index}));
    }
    % if need_clone:
        #[allow(non_snake_case)]
        pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
            use properties::longhands::${ident}::computed_value::T;
            use euclid::Size2D;
            let width = GeckoStyleCoordConvertible::from_gecko_style_coord(
                            &self.gecko.${gecko_ffi_name}.data_at(${x_index}))
                            .expect("Failed to clone ${ident}");
            let height = GeckoStyleCoordConvertible::from_gecko_style_coord(
                            &self.gecko.${gecko_ffi_name}.data_at(${y_index}))
                            .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)]
    pub fn initial() -> Arc<Self> {
        let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: unsafe { zeroed() } });
        unsafe {
            Gecko_Construct_${style_struct.gecko_ffi_name}(&mut Arc::get_mut(&mut result).unwrap().gecko);
        }
        result
    }
    pub fn get_gecko(&self) -> &${style_struct.gecko_ffi_name} {
        &self.gecko
    }
}
impl Drop for ${style_struct.gecko_struct_name} {
    fn drop(&mut self) {
        unsafe {
            Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut self.gecko);
        }
    }
}
impl Clone for ${style_struct.gecko_struct_name} {
    fn clone(&self) -> Self {
        unsafe {
            let mut result = ${style_struct.gecko_struct_name} { gecko: zeroed() };
            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut result.gecko, &self.gecko);
            result
        }
    }
}

// FIXME(bholley): Make bindgen generate Debug for all types.
%if style_struct.gecko_ffi_name in ("nsStyle" + x for x in "Border Display List Background Font SVGReset".split()):
impl Debug for ${style_struct.gecko_struct_name} {
    // FIXME(bholley): Generate this.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Gecko style struct: ${style_struct.gecko_struct_name}")
    }
}
%else:
impl Debug for ${style_struct.gecko_struct_name} {
    // FIXME(bholley): Generate this.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.gecko.fmt(f) }
}
%endif
</%def>

<%def name="raw_impl_trait(style_struct, skip_longhands='', skip_additionals='')">
<%
    longhands = [x for x in style_struct.longhands
                if not (skip_longhands == "*" or x.name in skip_longhands.split())]

    #
    # Make a list of types we can't auto-generate.
    #
    force_stub = [];
    # These are currently being shuffled to a different style struct on the gecko side.
    force_stub += ["backface-visibility", "transform-box", "transform-style"]
    # These live in an nsFont member in Gecko. Should be straightforward to do manually.
    force_stub += ["font-kerning", "font-variant"]
    # These have unusual representations in gecko.
    force_stub += ["list-style-type", "text-overflow"]
    # In a nsTArray, have to be done manually, but probably not too much work
    # (the "filling them", not the "making them work")
    force_stub += ["animation-name", "animation-duration",
                  "animation-timing-function", "animation-iteration-count",
                  "animation-direction", "animation-play-state",
                  "animation-fill-mode", "animation-delay"]

    # These are part of shorthands so we must include them in stylo builds,
    # but we haven't implemented the stylo glue for the longhand
    # so we generate a stub
    force_stub += ["list-style-image", # box
                   "flex-basis", # position

                   # transition
                   "transition-duration", "transition-timing-function",
                   "transition-property", "transition-delay",

                   "column-count", # column
                   ]

    # Types used with predefined_type()-defined properties that we can auto-generate.
    predefined_types = {
       "LengthOrPercentage": impl_style_coord,
       "LengthOrPercentageOrAuto": impl_style_coord,
       "LengthOrPercentageOrNone": impl_style_coord,
       "Number": impl_simple,
       "Opacity": impl_simple,
    }

    keyword_longhands = [x for x in longhands if x.keyword and not x.name in force_stub]
    predefined_longhands = [x for x in longhands
                           if x.predefined_type in predefined_types and not x.name in force_stub]
    stub_longhands = [x for x in longhands if x not in keyword_longhands + predefined_longhands]

    # If one of the longhands is not handled
    # by either:
    # - being a keyword
    # - being a predefined longhand
    # - being a longhand with manual glue code (i.e. in skip_longhands)
    # - being generated as a stub
    #
    # then we raise an error here.
    #
    # If you hit this error, please add `product="servo"` to the longhand.
    # In case the longhand is used in a shorthand, add it to the force_stub
    # list above.
    for stub in stub_longhands:
       if stub.name not in force_stub:
           raise Exception("Don't know what to do with longhand %s in style struct %s"
                           % (stub.name,style_struct. gecko_struct_name))
%>
impl ${style_struct.gecko_struct_name} {
    /*
     * Manually-Implemented Methods.
     */
    ${caller.body().strip()}

    /*
     * 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, need_clone=longhand.need_clone)
    %>

    /*
     * Stubs.
     */
    % for longhand in stub_longhands:
    #[allow(non_snake_case)]
    pub fn set_${longhand.ident}(&mut self, _: longhands::${longhand.ident}::computed_value::T) {
        if cfg!(debug_assertions) {
            println!("stylo: Unimplemented property setter: ${longhand.name}");
        }
    }
    #[allow(non_snake_case)]
    pub fn copy_${longhand.ident}_from(&mut self, _: &Self) {
        if cfg!(debug_assertions) {
            println!("stylo: Unimplemented property setter: ${longhand.name}");
        }
    }
    % if longhand.need_clone:
    #[allow(non_snake_case)]
    pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
        unimplemented!()
    }
    % endif
    % if longhand.need_index:
    pub fn ${longhand.ident}_count(&self) -> usize { 0 }
    pub 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>

<% data.manual_style_structs = [] %>
<%def name="impl_trait(style_struct_name, skip_longhands='', skip_additionals='')">
<%self:raw_impl_trait style_struct="${next(x for x in data.style_structs if x.name == style_struct_name)}"
                      skip_longhands="${skip_longhands}" skip_additionals="${skip_additionals}">
${caller.body()}
</%self:raw_impl_trait>
<% data.manual_style_structs.append(style_struct_name) %>
</%def>

<%!
class Side(object):
    def __init__(self, name, index):
        self.name = name
        self.ident = name.lower()
        self.index = index

class Corner(object):
    def __init__(self, name, index):
        self.x_name = "NS_CORNER_" + name + "_X"
        self.y_name = "NS_CORNER_" + name + "_Y"
        self.ident = name.lower()
        self.x_index = 2 * index
        self.y_index = 2 * index + 1

SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
CORNERS = [Corner("TOP_LEFT", 0), Corner("TOP_RIGHT", 1), Corner("BOTTOM_RIGHT", 2), Corner("BOTTOM_LEFT", 3)]
%>

#[allow(dead_code)]
fn static_assert() {
    unsafe {
        % for corner in CORNERS:
        transmute::<_, [u32; ${corner.x_index}]>([1; structs::${corner.x_name} as usize]);
        transmute::<_, [u32; ${corner.y_index}]>([1; structs::${corner.y_name} as usize]);
        % endfor
    }
    // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
    % for side in SIDES:
    { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
    % endfor
}


<% border_style_keyword = Keyword("border-style",
                                  "none solid double dotted dashed hidden groove ridge inset outset") %>

<% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
                                     for x in SIDES
                                     for y in ["color", "style", "width"]] +
                                    ["border-{0}-radius".format(x.ident.replace("_", "-"))
                                     for x in CORNERS]) %>
<%self:impl_trait style_struct_name="Border"
                  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, need_clone=True) %>

    <% impl_app_units("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, need_clone=True,
                      round_to_pixels=True) %>

    pub 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",
                               corner.x_index,
                               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",
                              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",
                              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",
                              side.index,
                              need_clone=True) %>
    % endfor

    pub fn set_z_index(&mut self, v: longhands::z_index::computed_value::T) {
        use properties::longhands::z_index::computed_value::T;
        match v {
            T::Auto => self.gecko.mZIndex.set_value(CoordDataValue::Auto),
            T::Number(n) => self.gecko.mZIndex.set_value(CoordDataValue::Integer(n)),
        }
    }

    pub fn copy_z_index_from(&mut self, other: &Self) {
        use gecko_bindings::structs::nsStyleUnit;
        // z-index is never a calc(). If it were, we'd be leaking here, so
        // assert that it isn't.
        debug_assert!(self.gecko.mZIndex.unit() != nsStyleUnit::eStyleUnit_Calc);
        unsafe {
            self.gecko.mZIndex.copy_from_unchecked(&other.gecko.mZIndex);
        }
    }

    pub fn clone_z_index(&self) -> longhands::z_index::computed_value::T {
        use properties::longhands::z_index::computed_value::T;
        return match self.gecko.mZIndex.as_value() {
            CoordDataValue::Auto => T::Auto,
            CoordDataValue::Integer(n) => T::Number(n),
            _ => {
                debug_assert!(false);
                T::Number(0)
            }
        }
    }

    pub fn set_box_sizing(&mut self, v: longhands::box_sizing::computed_value::T) {
        use 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
        }
    }
    ${impl_simple_copy('box_sizing', 'mBoxSizing')}

</%self:impl_trait>

<% skip_outline_longhands = " ".join("outline-color outline-style outline-width".split() +
                                     ["-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", need_clone=True) %>

    <% 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",
                               corner.x_index,
                               corner.y_index) %>
    % 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-stretch font-style font-size font-weight"
    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); }

        for family in &v.0 {
            match *family {
                FontFamily::FamilyName(ref name) => {
                    unsafe { Gecko_FontFamilyList_AppendNamed(list, name.as_ptr()); }
                }
                FontFamily::Generic(ref name) => {
                    let family_type =
                        if name == &atom!("serif") { FontFamilyType::eFamily_serif }
                        else if name == &atom!("sans-serif") { FontFamilyType::eFamily_sans_serif }
                        else if name == &atom!("cursive") { FontFamilyType::eFamily_cursive }
                        else if name == &atom!("fantasy") { FontFamilyType::eFamily_fantasy }
                        else if name == &atom!("monospace") { FontFamilyType::eFamily_monospace }
                        else { panic!("Unknown generic font family") };
                    unsafe { Gecko_FontFamilyList_AppendGeneric(list, family_type); }
                }
            }
        }
    }

    pub fn copy_font_family_from(&mut self, other: &Self) {
        unsafe { Gecko_CopyFontFamilyFrom(&mut self.gecko.mFont, &other.gecko.mFont); }
    }

    <%call expr="impl_keyword('font_style', 'mFont.style',
        data.longhands_by_name['font-style'].keyword, need_clone=False)"></%call>

    // FIXME(bholley): Gecko has two different sizes, one of which (mSize) is the
    // actual computed size, and the other of which (mFont.size) is the 'display
    // size' which takes font zooming into account. We don't handle font zooming yet.
    pub fn set_font_size(&mut self, v: longhands::font_size::computed_value::T) {
        self.gecko.mFont.size = v.0;
        self.gecko.mSize = v.0;
    }
    pub fn copy_font_size_from(&mut self, other: &Self) {
        self.gecko.mFont.size = other.gecko.mFont.size;
        self.gecko.mSize = other.gecko.mSize;
    }
    pub fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
        Au(self.gecko.mSize)
    }

    <% stretch_keyword = Keyword("font-stretch",
                                 "normal ultra-condensed extra-condensed condensed " +
                                 "semi-condensed semi-expanded expanded " +
                                 "extra-expanded ultra-expanded",
                                 gecko_constant_prefix='NS_FONT_STRETCH') %>

    ${impl_keyword('font_stretch', 'mFont.stretch', stretch_keyword, need_clone=False, cast_type='i16')}

    pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) {
        self.gecko.mFont.weight = v as u16;
    }
    ${impl_simple_copy('font_weight', 'mFont.weight')}

    pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
        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) }
    }

    // This is used for PartialEq, which we don't implement for gecko style structs.
    pub fn compute_font_hash(&mut self) {}

</%self:impl_trait>

<% skip_box_longhands= """display overflow-y vertical-align
                          -moz-binding page-break-before page-break-after""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">

    // 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",
                                            gecko_enum_prefix="StyleDisplay") %>
    ${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"] %>
    pub fn set_overflow_y(&mut self, v: longhands::overflow_y::computed_value::T) {
        use 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
        };
    }
    ${impl_simple_copy('overflow_y', 'mOverflowY')}
    pub fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T {
        use properties::longhands::overflow_x::computed_value::T as BaseType;
        use 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
            x => panic!("Found unexpected value in style struct for overflow_y property: {}", x),
        }
    }

    pub fn set_vertical_align(&mut self, v: longhands::vertical_align::computed_value::T) {
        <% keyword = data.longhands_by_name["vertical-align"].keyword %>
        use properties::longhands::vertical_align::computed_value::T;
        // FIXME: Align binary representations and ditch |match| for cast + static_asserts
        match v {
            % for value in keyword.values_for('gecko'):
                T::${to_rust_ident(value)} =>
                    self.gecko.mVerticalAlign.set_value(
                            CoordDataValue::Enumerated(structs::${keyword.gecko_constant(value)})),
            % endfor
            T::LengthOrPercentage(v) => self.gecko.mVerticalAlign.set(v),
        }
    }

    pub fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T {
        use properties::longhands::vertical_align::computed_value::T;
        use values::computed::LengthOrPercentage;

        match self.gecko.mVerticalAlign.as_value() {
            % for value in keyword.values_for('gecko'):
                CoordDataValue::Enumerated(structs::${keyword.gecko_constant(value)}) => T::${to_rust_ident(value)},
            % endfor
                CoordDataValue::Enumerated(_) => panic!("Unexpected enum variant for vertical-align"),
                _ => {
                    let v = LengthOrPercentage::from_gecko_style_coord(&self.gecko.mVerticalAlign)
                        .expect("Expected length or percentage for vertical-align");
                    T::LengthOrPercentage(v)
                }
        }
    }

    <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>

    #[allow(non_snake_case)]
    pub fn set__moz_binding(&mut self, v: longhands::_moz_binding::computed_value::T) {
        use 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 {
                    Gecko_SetMozBinding(&mut self.gecko,
                                        url.as_str().as_ptr(),
                                        url.as_str().len() as u32,
                                        extra_data.base.as_raw(),
                                        extra_data.referrer.as_raw(),
                                        extra_data.principal.as_raw());
                }
            }
        }
    }
    #[allow(non_snake_case)]
    pub fn copy__moz_binding_from(&mut self, other: &Self) {
        unsafe { Gecko_CopyMozBindingFrom(&mut self.gecko, &other.gecko); }
    }

    // Temp fix for Bugzilla bug 24000.
    // Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true.
    // "A conforming user agent may interpret the values 'left' and 'right'
    // as 'always'." - CSS2.1, section 13.3.1
    pub fn set_page_break_before(&mut self, v: longhands::page_break_before::computed_value::T) {
        use computed_values::page_break_before::T;
        let result = match v {
            T::auto   => false,
            T::always => true,
            T::avoid  => false,
            T::left   => true,
            T::right  => true
        };
        self.gecko.mBreakBefore = result;
    }

    ${impl_simple_copy('page_break_before', 'mBreakBefore')}

    // Temp fix for Bugzilla bug 24000.
    // See set_page_break_before for detail.
    pub fn set_page_break_after(&mut self, v: longhands::page_break_after::computed_value::T) {
        use computed_values::page_break_after::T;
        let result = match v {
            T::auto   => false,
            T::always => true,
            T::avoid  => false,
            T::left   => true,
            T::right  => true
        };
        self.gecko.mBreakBefore = result;
    }

    ${impl_simple_copy('page_break_after', 'mBreakAfter')}

</%self:impl_trait>

<%def name="simple_image_array_property(name, shorthand, field_name)">
    <%
        image_layers_field = "mImage" if shorthand == "background" else "mMask"
    %>
    pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
        unsafe {
            Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field},
                                          other.gecko.${image_layers_field}.mLayers.len());
        }
        for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
                                  .zip(other.gecko.${image_layers_field}.mLayers.iter())
                                  .take(other.gecko.${image_layers_field}
                                                   .${field_name}Count as usize) {
            layer.${field_name} = other.${field_name};
        }
        self.gecko.${image_layers_field}.${field_name}Count =
            other.gecko.${image_layers_field}.${field_name}Count;
    }

    pub fn set_${shorthand}_${name}(&mut self,
                                    v: longhands::${shorthand}_${name}::computed_value::T) {
        unsafe {
          Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.0.len());
        }

        self.gecko.${image_layers_field}.${field_name}Count = v.0.len() as u32;
        for (servo, geckolayer) in v.0.into_iter()
                                    .zip(self.gecko.${image_layers_field}.mLayers.iter_mut()) {
            geckolayer.${field_name} = {
                ${caller.body()}
            };
        }
    }
</%def>
<%def name="impl_common_image_layer_properties(shorthand)">
    <%
        image_layers_field = "mImage" if shorthand == "background" else "mMask"
    %>

    <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
        use properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
        use gecko_bindings::structs::nsStyleImageLayers_Repeat;
        use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_REPEAT;
        use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT;
        use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_SPACE;
        use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_ROUND;

        let (repeat_x, repeat_y) = match servo {
          T::repeat_x => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT,
                          NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT),
          T::repeat_y => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT,
                          NS_STYLE_IMAGELAYER_REPEAT_REPEAT),
          T::repeat  => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT,
                         NS_STYLE_IMAGELAYER_REPEAT_REPEAT),
          T::space => (NS_STYLE_IMAGELAYER_REPEAT_SPACE,
                       NS_STYLE_IMAGELAYER_REPEAT_SPACE),
          T::round => (NS_STYLE_IMAGELAYER_REPEAT_ROUND,
                       NS_STYLE_IMAGELAYER_REPEAT_ROUND),
          T::no_repeat => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT,
                           NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT),
        };
        nsStyleImageLayers_Repeat {
              mXRepeat: repeat_x as u8,
              mYRepeat: repeat_y as u8,
        }
    </%self:simple_image_array_property>

    <%self:simple_image_array_property name="clip" shorthand="${shorthand}" field_name="mClip">
        use properties::longhands::${shorthand}_clip::single_value::computed_value::T;

        match servo {
            T::border_box => structs::NS_STYLE_IMAGELAYER_CLIP_BORDER as u8,
            T::padding_box => structs::NS_STYLE_IMAGELAYER_CLIP_PADDING as u8,
            T::content_box => structs::NS_STYLE_IMAGELAYER_CLIP_CONTENT as u8,
        }
    </%self:simple_image_array_property>

    <%self:simple_image_array_property name="origin" shorthand="${shorthand}" field_name="mOrigin">
        use properties::longhands::${shorthand}_origin::single_value::computed_value::T;

        match servo {
            T::border_box => structs::NS_STYLE_IMAGELAYER_ORIGIN_BORDER as u8,
            T::padding_box => structs::NS_STYLE_IMAGELAYER_ORIGIN_PADDING as u8,
            T::content_box => structs::NS_STYLE_IMAGELAYER_ORIGIN_CONTENT as u8,
        }
    </%self:simple_image_array_property>

    pub fn copy_${shorthand}_position_from(&mut self, other: &Self) {
        self.gecko.${image_layers_field}.mPositionXCount
                = cmp::min(1, other.gecko.${image_layers_field}.mPositionXCount);
        self.gecko.${image_layers_field}.mPositionYCount
                = cmp::min(1, other.gecko.${image_layers_field}.mPositionYCount);
        self.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition =
            other.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition;
        unsafe {
            Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field},
                                          other.gecko.${image_layers_field}.mLayers.len());
        }
        for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
                                  .zip(other.gecko.${image_layers_field}.mLayers.iter())
                                  .take(other.gecko.${image_layers_field}.mPositionXCount as usize) {
            layer.mPosition.mXPosition
                = other.mPosition.mXPosition;
        }
        for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
                                  .zip(other.gecko.${image_layers_field}.mLayers.iter())
                                  .take(other.gecko.${image_layers_field}.mPositionYCount as usize) {
            layer.mPosition.mYPosition
                = other.mPosition.mYPosition;
        }
        self.gecko.${image_layers_field}.mPositionXCount
                = other.gecko.${image_layers_field}.mPositionXCount;
        self.gecko.${image_layers_field}.mPositionYCount
                = other.gecko.${image_layers_field}.mPositionYCount;
    }

    pub fn clone_${shorthand}_position(&self)
        -> longhands::${shorthand}_position::computed_value::T {
        use values::computed::position::Position;
        longhands::background_position::computed_value::T(
            self.gecko.${image_layers_field}.mLayers.iter()
                .take(self.gecko.${image_layers_field}.mPositionXCount as usize)
                .take(self.gecko.${image_layers_field}.mPositionYCount as usize)
                .map(|position| Position {
                    horizontal: position.mPosition.mXPosition.into(),
                    vertical: position.mPosition.mYPosition.into(),
                })
                .collect()
        )
    }

    pub fn set_${shorthand}_position(&mut self,
                                     v: longhands::${shorthand}_position::computed_value::T) {
        unsafe {
          Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.0.len());
        }

        self.gecko.${image_layers_field}.mPositionXCount = v.0.len() as u32;
        self.gecko.${image_layers_field}.mPositionYCount = v.0.len() as u32;
        for (servo, geckolayer) in v.0.into_iter().zip(self.gecko.${image_layers_field}
                                                           .mLayers.iter_mut()) {
            geckolayer.mPosition.mXPosition = servo.horizontal.into();
            geckolayer.mPosition.mYPosition = servo.vertical.into();
        }
    }

    <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
        use gecko_bindings::structs::nsStyleImageLayers_Size_Dimension;
        use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType;
        use gecko_bindings::structs::{nsStyleCoord_CalcValue, nsStyleImageLayers_Size};
        use properties::longhands::background_size::single_value::computed_value::T;

        let mut width = nsStyleCoord_CalcValue::new();
        let mut height = nsStyleCoord_CalcValue::new();

        let (w_type, h_type) = match servo {
            T::Explicit(size) => {
                let mut w_type = nsStyleImageLayers_Size_DimensionType::eAuto;
                let mut h_type = nsStyleImageLayers_Size_DimensionType::eAuto;
                if let Some(w) = size.width.to_calc_value() {
                    width = w;
                    w_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage;
                }
                if let Some(h) = size.height.to_calc_value() {
                    height = h;
                    h_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage;
                }
                (w_type, h_type)
            }
            T::Cover => (nsStyleImageLayers_Size_DimensionType::eCover,
                         nsStyleImageLayers_Size_DimensionType::eCover),
            T::Contain => (nsStyleImageLayers_Size_DimensionType::eContain,
                         nsStyleImageLayers_Size_DimensionType::eContain),
        };

        nsStyleImageLayers_Size {
            mWidth: nsStyleImageLayers_Size_Dimension { _base: width },
            mHeight: nsStyleImageLayers_Size_Dimension { _base: height },
            mWidthType: w_type as u8,
            mHeightType: h_type as u8,
        }
    </%self:simple_image_array_property>

    pub fn clone_${shorthand}_size(&self) -> longhands::background_size::computed_value::T {
        use gecko_bindings::structs::nsStyleCoord_CalcValue as CalcValue;
        use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType as DimensionType;
        use properties::longhands::background_size::single_value::computed_value::{ExplicitSize, T};
        use values::computed::LengthOrPercentageOrAuto;

        fn to_servo(value: CalcValue, ty: u8) -> LengthOrPercentageOrAuto {
            if ty == DimensionType::eAuto as u8 {
                LengthOrPercentageOrAuto::Auto
            } else {
                debug_assert!(ty == DimensionType::eLengthPercentage as u8);
                LengthOrPercentageOrAuto::Calc(value.into())
            }
        }

        longhands::background_size::computed_value::T(
            self.gecko.${image_layers_field}.mLayers.iter().map(|ref layer| {
                if DimensionType::eCover as u8 == layer.mSize.mWidthType {
                    debug_assert!(layer.mSize.mHeightType == DimensionType::eCover as u8);
                    return T::Cover
                }
                if DimensionType::eContain as u8 == layer.mSize.mWidthType {
                    debug_assert!(layer.mSize.mHeightType == DimensionType::eContain as u8);
                    return T::Contain
                }

                T::Explicit(ExplicitSize {
                    width: to_servo(layer.mSize.mWidth._base, layer.mSize.mWidthType),
                    height: to_servo(layer.mSize.mHeight._base, layer.mSize.mHeightType),
                })
            }).collect()
        )
    }


    pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
        unsafe {
            Gecko_CopyImageValueFrom(&mut self.gecko.${image_layers_field}.mLayers.mFirstElement.mImage,
                                     &other.gecko.${image_layers_field}.mLayers.mFirstElement.mImage);
        }
    }

    pub fn set_${shorthand}_image(&mut self,
                                  images: longhands::${shorthand}_image::computed_value::T) {
        use gecko_bindings::structs::nsStyleImage;
        use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
        use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_LINEAR, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER};
        use gecko_bindings::structs::nsStyleCoord;
        use values::computed::{Image, LinearGradient};
        use values::specified::AngleOrCorner;
        use values::specified::{HorizontalDirection, VerticalDirection};
        use cssparser::Color as CSSColor;

        fn set_linear_gradient(gradient: LinearGradient, geckoimage: &mut nsStyleImage) {
            let stop_count = gradient.stops.len();
            if stop_count >= ::std::u32::MAX as usize {
                warn!("stylo: Prevented overflow due to too many gradient stops");
                return;
            }

            let gecko_gradient = unsafe {
                Gecko_CreateGradient(NS_STYLE_GRADIENT_SHAPE_LINEAR as u8,
                                     NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8,
                                     /* repeating = */ false,
                                     /* legacy_syntax = */ false,
                                     stop_count as u32)
            };

            match gradient.angle_or_corner {
                AngleOrCorner::Angle(angle) => {
                    unsafe {
                        (*gecko_gradient).mAngle.set(angle);
                        (*gecko_gradient).mBgPosX.set_value(CoordDataValue::None);
                        (*gecko_gradient).mBgPosY.set_value(CoordDataValue::None);
                    }
                }
                AngleOrCorner::Corner(horiz, vert) => {
                    let percent_x = match horiz {
                        HorizontalDirection::Left => 0.0,
                        HorizontalDirection::Right => 1.0,
                    };
                    let percent_y = match vert {
                        VerticalDirection::Top => 0.0,
                        VerticalDirection::Bottom => 1.0,
                    };

                    unsafe {
                        (*gecko_gradient).mAngle.set_value(CoordDataValue::None);
                        (*gecko_gradient).mBgPosX
                                         .set_value(CoordDataValue::Percent(percent_x));
                        (*gecko_gradient).mBgPosY
                                         .set_value(CoordDataValue::Percent(percent_y));
                    }
                }
            }

            let mut coord: nsStyleCoord = nsStyleCoord::null();
            for (index, stop) in gradient.stops.iter().enumerate() {
                // NB: stops are guaranteed to be none in the gecko side by
                // default.
                coord.set(stop.position);
                let color = match stop.color {
                    CSSColor::CurrentColor => {
                        // TODO(emilio): gecko just stores an nscolor,
                        // and it doesn't seem to support currentColor
                        // as value in a gradient.
                        //
                        // Double-check it and either remove
                        // currentColor for servo or see how gecko
                        // handles this.
                        0
                    },
                    CSSColor::RGBA(ref rgba) => convert_rgba_to_nscolor(rgba),
                };

                let mut stop = unsafe {
                    &mut (*gecko_gradient).mStops[index]
                };

                stop.mColor = color;
                stop.mIsInterpolationHint = false;
                stop.mLocation.copy_from(&coord);
            }

            unsafe {
                Gecko_SetGradientImageValue(geckoimage, gecko_gradient);
            }
        }

        unsafe {
            // Prevent leaking of the last elements we did set
            for image in &mut self.gecko.${image_layers_field}.mLayers {
                Gecko_SetNullImageValue(&mut image.mImage)
            }
            // XXXManishearth clear mSourceURI for masks
            Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, images.0.len());
            for image in &mut self.gecko.${image_layers_field}.mLayers {
                Gecko_InitializeImageLayer(image, LayerType::${shorthand.title()});
            }
        }

        self.gecko.${image_layers_field}.mImageCount = images.0.len() as u32;

        for (image, geckoimage) in images.0.into_iter().zip(self.gecko.${image_layers_field}
                                                                .mLayers.iter_mut()) {
            % if shorthand == "background":
                if let Some(image) = image.0 {
                    match image {
                        Image::LinearGradient(gradient) => {
                            set_linear_gradient(gradient, &mut geckoimage.mImage)
                        },
                        Image::Url(..) => {
                            // let utf8_bytes = url.as_bytes();
                            // Gecko_SetUrlImageValue(&mut self.gecko.mImage.mLayers.mFirstElement,
                            //                        utf8_bytes.as_ptr() as *const _,
                            //                        utf8_bytes.len());
                            warn!("stylo: imgRequestProxies are not threadsafe in gecko, \
                                   background-image: url() not yet implemented");
                        }
                    }
                }
            % else:
                use properties::longhands::mask_image::single_value::computed_value::T;
                match image {
                    T::Image(image) => match image {
                        Image::LinearGradient(gradient) => {
                            set_linear_gradient(gradient, &mut geckoimage.mImage)
                        }
                        _ => () // we need to support image values
                    },
                    _ => () // we need to support url valeus
                }
            % endif

        }
    }

    <%
        fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage"
        if shorthand == "background":
            fill_fields += " mAttachment"
        else:
            # mSourceURI uses mImageCount
            fill_fields += " mMaskMode mComposite"
    %>
    pub fn fill_arrays(&mut self) {
        use gecko_bindings::bindings::Gecko_FillAll${shorthand.title()}Lists;
        use std::cmp;
        let mut max_len = 1;
        % for member in fill_fields.split():
            max_len = cmp::max(max_len, self.gecko.${image_layers_field}.${member}Count);
        % endfor

        // XXXManishearth Gecko does an optimization here where it only
        // fills things in if any of the properties have been set

        unsafe {
            // While we could do this manually, we'd need to also manually
            // run all the copy constructors, so we just delegate to gecko
            Gecko_FillAll${shorthand.title()}Lists(&mut self.gecko.${image_layers_field}, max_len);
        }
    }
</%def>

// TODO: Gecko accepts lists in most background-related properties. We just use
// the first element (which is the common case), but at some point we want to
// 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
                                  background-size background-position""" %>
<%self:impl_trait style_struct_name="Background"
                  skip_longhands="${skip_background_longhands}"
                  skip_additionals="*">

    <% impl_color("background_color", "mBackgroundColor", need_clone=True, complex_color=False) %>

    <% impl_common_image_layer_properties("background") %>

    <%self:simple_image_array_property name="attachment" shorthand="background" field_name="mAttachment">
        use properties::longhands::background_attachment::single_value::computed_value::T;
        match servo {
            T::scroll => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL as u8,
            T::fixed => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED as u8,
            T::local => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL as u8,
        }
    </%self:simple_image_array_property>
</%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)}
    pub 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="Effects"
                  skip_longhands="box-shadow">
    pub fn set_box_shadow(&mut self, v: longhands::box_shadow::computed_value::T) {
        use cssparser::Color;

        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()) {

            gecko_shadow.mXOffset = servo.offset_x.0;
            gecko_shadow.mYOffset = servo.offset_y.0;
            gecko_shadow.mRadius = servo.blur_radius.0;
            gecko_shadow.mSpread = servo.spread_radius.0;
            gecko_shadow.mSpread = servo.spread_radius.0;
            gecko_shadow.mInset = servo.inset;
            gecko_shadow.mColor = match servo.color {
                Color::RGBA(rgba) => {
                    gecko_shadow.mHasColor = true;
                    convert_rgba_to_nscolor(&rgba)
                },
                // TODO handle currentColor
                // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
                Color::CurrentColor => 0,
            }

        }
    }

    pub fn copy_box_shadow_from(&mut self, other: &Self) {
        self.gecko.mBoxShadow.copy_from(&other.gecko.mBoxShadow);
    }

    pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T {
        use cssparser::Color;

        let buf = self.gecko.mBoxShadow.iter().map(|shadow| {
            longhands::box_shadow::single_value::computed_value::T {
                offset_x: Au(shadow.mXOffset),
                offset_y: Au(shadow.mYOffset),
                blur_radius: Au(shadow.mRadius),
                spread_radius: Au(shadow.mSpread),
                inset: shadow.mInset,
                color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
            }
        }).collect();
        longhands::box_shadow::computed_value::T(buf)
    }
</%self:impl_trait>


<%self:impl_trait style_struct_name="InheritedTable"
                  skip_longhands="border-spacing">

    pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) {
        self.gecko.mBorderSpacingCol = v.horizontal.0;
        self.gecko.mBorderSpacingRow = v.vertical.0;
    }

    pub fn copy_border_spacing_from(&mut self, other: &Self) {
        self.gecko.mBorderSpacingCol = other.gecko.mBorderSpacingCol;
        self.gecko.mBorderSpacingRow = other.gecko.mBorderSpacingRow;
    }

</%self:impl_trait>


<%self:impl_trait style_struct_name="InheritedBox"
                  skip_longhands="image-rendering">

    <% render_keyword = Keyword("image-rendering",
                                "auto optimizequality optimizespeed crispedges") %>

    ${impl_keyword('image_rendering', 'mImageRendering', render_keyword, need_clone=False)}

</%self:impl_trait>


<%self:impl_trait style_struct_name="InheritedText"
                  skip_longhands="text-align text-shadow line-height letter-spacing word-spacing">

    <% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left " +
                                                  "-moz-right match-parent") %>
    ${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) {
        use cssparser::Color;
        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()) {

            gecko_shadow.mXOffset = servo.offset_x.0;
            gecko_shadow.mYOffset = servo.offset_y.0;
            gecko_shadow.mRadius = servo.blur_radius.0;
            gecko_shadow.mHasColor = false;
            gecko_shadow.mColor = match servo.color {
                Color::RGBA(rgba) => {
                    gecko_shadow.mHasColor = true;
                    convert_rgba_to_nscolor(&rgba)
                },
                // TODO handle currentColor
                // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
                Color::CurrentColor => 0,
            }

        }
    }

    pub fn copy_text_shadow_from(&mut self, other: &Self) {
        self.gecko.mTextShadow.copy_from(&other.gecko.mTextShadow);
    }

    pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T {
        use cssparser::Color;

        let buf = self.gecko.mTextShadow.iter().map(|shadow| {
            longhands::text_shadow::computed_value::TextShadow {
                offset_x: Au(shadow.mXOffset),
                offset_y: Au(shadow.mYOffset),
                blur_radius: Au(shadow.mRadius),
                color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
            }

        }).collect();
        longhands::text_shadow::computed_value::T(buf)
    }

    pub fn set_line_height(&mut self, v: longhands::line_height::computed_value::T) {
        use properties::longhands::line_height::computed_value::T;
        // FIXME: Align binary representations and ditch |match| for cast + static_asserts
        let en = match v {
            T::Normal => CoordDataValue::Normal,
            T::Length(val) => CoordDataValue::Coord(val.0),
            T::Number(val) => CoordDataValue::Factor(val),
            T::MozBlockHeight =>
                    CoordDataValue::Enumerated(structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT),
        };
        self.gecko.mLineHeight.set_value(en);
    }

    pub fn clone_line_height(&self) -> longhands::line_height::computed_value::T {
        use properties::longhands::line_height::computed_value::T;
        return match self.gecko.mLineHeight.as_value() {
            CoordDataValue::Normal => T::Normal,
            CoordDataValue::Coord(coord) => T::Length(Au(coord)),
            CoordDataValue::Factor(n) => T::Number(n),
            CoordDataValue::Enumerated(val) if val == structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT =>
                T::MozBlockHeight,
            _ => {
                debug_assert!(false);
                T::MozBlockHeight
            }
        }
    }

    <%call expr="impl_coord_copy('line_height', 'mLineHeight')"></%call>

    pub fn set_letter_spacing(&mut self, v: longhands::letter_spacing::computed_value::T) {
        match v.0 {
            Some(au) => self.gecko.mLetterSpacing.set_value(CoordDataValue::Coord(au.0)),
            None => self.gecko.mLetterSpacing.set_value(CoordDataValue::Normal)
        }
    }

    <%call expr="impl_coord_copy('letter_spacing', 'mLetterSpacing')"></%call>

    pub fn set_word_spacing(&mut self, v: longhands::word_spacing::computed_value::T) {
        use values::computed::LengthOrPercentage::*;

        match v.0 {
            Some(lop) => match lop {
                Length(au) => self.gecko.mWordSpacing.set_value(CoordDataValue::Coord(au.0)),
                Percentage(f) => self.gecko.mWordSpacing.set_value(CoordDataValue::Percent(f)),
                Calc(l_p) => self.gecko.mWordSpacing.set_value(CoordDataValue::Calc(l_p.into())),
            },
            // https://drafts.csswg.org/css-text-3/#valdef-word-spacing-normal
            None => self.gecko.mWordSpacing.set_value(CoordDataValue::Coord(0)),
        }
    }

    <%call expr="impl_coord_copy('word_spacing', 'mWordSpacing')"></%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", need_clone=True)}

    pub 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;
    }

    ${impl_simple_copy('text_decoration_line', 'mTextDecorationLine')}

    #[inline]
    pub fn has_underline(&self) -> bool {
        (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0
    }

    #[inline]
    pub fn has_overline(&self) -> bool {
        (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0
    }

    #[inline]
    pub fn has_line_through(&self) -> bool {
        (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0
    }
</%self:impl_trait>

<% skip_svg_longhands = """
flood-color lighting-color stop-color
mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position mask-size mask-image
clip-path
"""
%>
<%self:impl_trait style_struct_name="SVG"
                  skip_longhands="${skip_svg_longhands}"
                  skip_additionals="*">

    <% impl_color("flood_color", "mFloodColor", complex_color=False) %>

    <% impl_color("lighting_color", "mLightingColor", complex_color=False) %>

    <% impl_color("stop_color", "mStopColor", complex_color=False) %>

    <% impl_common_image_layer_properties("mask") %>

    <%self:simple_image_array_property name="mode" shorthand="mask" field_name="mMaskMode">
        use properties::longhands::mask_mode::single_value::computed_value::T;

        match servo {
          T::alpha => structs::NS_STYLE_MASK_MODE_ALPHA as u8,
          T::luminance => structs::NS_STYLE_MASK_MODE_LUMINANCE as u8,
          T::match_source => structs::NS_STYLE_MASK_MODE_MATCH_SOURCE as u8,
        }
    </%self:simple_image_array_property>
    <%self:simple_image_array_property name="composite" shorthand="mask" field_name="mComposite">
        use properties::longhands::mask_composite::single_value::computed_value::T;

        match servo {
            T::add => structs::NS_STYLE_MASK_COMPOSITE_ADD as u8,
            T::subtract => structs::NS_STYLE_MASK_COMPOSITE_SUBTRACT as u8,
            T::intersect => structs::NS_STYLE_MASK_COMPOSITE_INTERSECT as u8,
            T::exclude => structs::NS_STYLE_MASK_COMPOSITE_EXCLUDE as u8,
        }
    </%self:simple_image_array_property>
    pub fn set_clip_path(&mut self, v: longhands::clip_path::computed_value::T) {
        use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyClipPath};
        use gecko_bindings::structs::StyleClipPathGeometryBox;
        use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType};
        use gecko_bindings::structs::{StyleClipPath, StyleFillRule};
        use gecko::conversions::basic_shape::set_corners_from_radius;
        use gecko::values::GeckoStyleCoordConvertible;
        use values::computed::basic_shape::*;
        let ref mut clip_path = self.gecko.mClipPath;
        // clean up existing struct
        unsafe { Gecko_DestroyClipPath(clip_path) };

        clip_path.mType = StyleShapeSourceType::None;

        match v {
            ShapeSource::Url(..) => println!("stylo: clip-path: url() not yet implemented"),
            ShapeSource::None => {} // don't change the type
            ShapeSource::Box(reference) => {
                clip_path.mReferenceBox = reference.into();
                clip_path.mType = StyleShapeSourceType::Box;
            }
            ShapeSource::Shape(servo_shape, maybe_box) => {
                clip_path.mReferenceBox = maybe_box.map(Into::into)
                                                   .unwrap_or(StyleClipPathGeometryBox::NoBox);
                clip_path.mType = StyleShapeSourceType::Shape;

                fn init_shape(clip_path: &mut StyleClipPath, ty: StyleBasicShapeType) -> &mut StyleBasicShape {
                    unsafe {
                        // We have to be very careful to avoid a copy here!
                        let ref mut union = clip_path.__bindgen_anon_1;
                        let mut shape: &mut *mut StyleBasicShape = union.mBasicShape.as_mut();
                        *shape = Gecko_NewBasicShape(ty);
                        &mut **shape
                    }
                }
                match servo_shape {
                    BasicShape::Inset(rect) => {
                        let mut shape = init_shape(clip_path, StyleBasicShapeType::Inset);
                        unsafe { shape.mCoordinates.set_len(4) };

                        // set_len() can't call constructors, so the coordinates
                        // can contain any value. set_value() attempts to free
                        // allocated coordinates, so we don't want to feed it
                        // garbage values which it may misinterpret.
                        // Instead, we use leaky_set_value to blindly overwrite
                        // the garbage data without
                        // attempting to clean up.
                        shape.mCoordinates[0].leaky_set_null();
                        rect.top.to_gecko_style_coord(&mut shape.mCoordinates[0]);
                        shape.mCoordinates[1].leaky_set_null();
                        rect.right.to_gecko_style_coord(&mut shape.mCoordinates[1]);
                        shape.mCoordinates[2].leaky_set_null();
                        rect.bottom.to_gecko_style_coord(&mut shape.mCoordinates[2]);
                        shape.mCoordinates[3].leaky_set_null();
                        rect.left.to_gecko_style_coord(&mut shape.mCoordinates[3]);

                        set_corners_from_radius(rect.round, &mut shape.mRadius);
                    }
                    BasicShape::Circle(circ) => {
                        let mut shape = init_shape(clip_path, StyleBasicShapeType::Circle);
                        unsafe { shape.mCoordinates.set_len(1) };
                        shape.mCoordinates[0].leaky_set_null();
                        circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]);

                        shape.mPosition = circ.position.into();
                    }
                    BasicShape::Ellipse(el) => {
                        let mut shape = init_shape(clip_path, StyleBasicShapeType::Ellipse);
                        unsafe { shape.mCoordinates.set_len(2) };
                        shape.mCoordinates[0].leaky_set_null();
                        el.semiaxis_x.to_gecko_style_coord(&mut shape.mCoordinates[0]);
                        shape.mCoordinates[1].leaky_set_null();
                        el.semiaxis_y.to_gecko_style_coord(&mut shape.mCoordinates[1]);

                        shape.mPosition = el.position.into();
                    }
                    BasicShape::Polygon(poly) => {
                        let mut shape = init_shape(clip_path, StyleBasicShapeType::Polygon);
                        unsafe {
                            shape.mCoordinates.set_len(poly.coordinates.len() as u32 * 2);
                        }
                        for (i, coord) in poly.coordinates.iter().enumerate() {
                            shape.mCoordinates[2 * i].leaky_set_null();
                            shape.mCoordinates[2 * i + 1].leaky_set_null();
                            coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]);
                            coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]);
                        }
                        shape.mFillRule = if poly.fill == FillRule::EvenOdd {
                            StyleFillRule::Evenodd
                        } else {
                            StyleFillRule::Nonzero
                        };
                    }
                }
            }
        }

    }

    pub fn copy_clip_path_from(&mut self, other: &Self) {
        use gecko_bindings::bindings::Gecko_CopyClipPathValueFrom;
        unsafe {
            Gecko_CopyClipPathValueFrom(&mut self.gecko.mClipPath, &other.gecko.mClipPath);
        }
    }

    pub fn clone_clip_path(&self) -> longhands::clip_path::computed_value::T {
        use gecko_bindings::structs::StyleShapeSourceType;
        use gecko_bindings::structs::StyleClipPathGeometryBox;
        use values::computed::basic_shape::*;
        let ref clip_path = self.gecko.mClipPath;

        match clip_path.mType {
            StyleShapeSourceType::None => ShapeSource::None,
            StyleShapeSourceType::Box => {
                ShapeSource::Box(clip_path.mReferenceBox.into())
            }
            StyleShapeSourceType::URL => {
                warn!("stylo: clip-path: url() not implemented yet");
                Default::default()
            }
            StyleShapeSourceType::Shape => {
                let reference = if let StyleClipPathGeometryBox::NoBox = clip_path.mReferenceBox {
                    None
                } else {
                    Some(clip_path.mReferenceBox.into())
                };
                let union = clip_path.__bindgen_anon_1;
                let shape = unsafe { &**union.mBasicShape.as_ref() };
                ShapeSource::Shape(shape.into(), reference)
            }
        }
    }

</%self:impl_trait>

<%self:impl_trait style_struct_name="Color"
                  skip_longhands="*">
    pub 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>

    pub 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">
    pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
        use 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,
                Cursor::Default => structs::NS_STYLE_CURSOR_DEFAULT,
                Cursor::Pointer => structs::NS_STYLE_CURSOR_POINTER,
                Cursor::ContextMenu => structs::NS_STYLE_CURSOR_CONTEXT_MENU,
                Cursor::Help => structs::NS_STYLE_CURSOR_HELP,
                Cursor::Progress => structs::NS_STYLE_CURSOR_DEFAULT, // Gecko doesn't support "progress" yet
                Cursor::Wait => structs::NS_STYLE_CURSOR_WAIT,
                Cursor::Cell => structs::NS_STYLE_CURSOR_CELL,
                Cursor::Crosshair => structs::NS_STYLE_CURSOR_CROSSHAIR,
                Cursor::Text => structs::NS_STYLE_CURSOR_TEXT,
                Cursor::VerticalText => structs::NS_STYLE_CURSOR_VERTICAL_TEXT,
                Cursor::Alias => structs::NS_STYLE_CURSOR_ALIAS,
                Cursor::Copy => structs::NS_STYLE_CURSOR_COPY,
                Cursor::Move => structs::NS_STYLE_CURSOR_MOVE,
                Cursor::NoDrop => structs::NS_STYLE_CURSOR_NO_DROP,
                Cursor::NotAllowed => structs::NS_STYLE_CURSOR_NOT_ALLOWED,
                Cursor::Grab => structs::NS_STYLE_CURSOR_GRAB,
                Cursor::Grabbing => structs::NS_STYLE_CURSOR_GRABBING,
                Cursor::EResize => structs::NS_STYLE_CURSOR_E_RESIZE,
                Cursor::NResize => structs::NS_STYLE_CURSOR_N_RESIZE,
                Cursor::NeResize => structs::NS_STYLE_CURSOR_NE_RESIZE,
                Cursor::NwResize => structs::NS_STYLE_CURSOR_NW_RESIZE,
                Cursor::SResize => structs::NS_STYLE_CURSOR_S_RESIZE,
                Cursor::SeResize => structs::NS_STYLE_CURSOR_SE_RESIZE,
                Cursor::SwResize => structs::NS_STYLE_CURSOR_SW_RESIZE,
                Cursor::WResize => structs::NS_STYLE_CURSOR_W_RESIZE,
                Cursor::EwResize => structs::NS_STYLE_CURSOR_EW_RESIZE,
                Cursor::NsResize => structs::NS_STYLE_CURSOR_NS_RESIZE,
                Cursor::NeswResize => structs::NS_STYLE_CURSOR_NESW_RESIZE,
                Cursor::NwseResize => structs::NS_STYLE_CURSOR_NWSE_RESIZE,
                Cursor::ColResize => structs::NS_STYLE_CURSOR_COL_RESIZE,
                Cursor::RowResize => structs::NS_STYLE_CURSOR_ROW_RESIZE,
                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">

    pub fn set_column_width(&mut self, v: longhands::column_width::computed_value::T) {
        match v.0 {
            Some(au) => self.gecko.mColumnWidth.set_value(CoordDataValue::Coord(au.0)),
            None => self.gecko.mColumnWidth.set_value(CoordDataValue::Auto),
        }
    }

    ${impl_coord_copy('column_width', 'mColumnWidth')}
</%self:impl_trait>

<%self:impl_trait style_struct_name="Counters"
                  skip_longhands="content">
    pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
        use properties::longhands::content::computed_value::T;
        use properties::longhands::content::computed_value::ContentItem;
        use gecko_bindings::structs::nsStyleContentData;
        use gecko_bindings::structs::nsStyleContentType::*;
        use gecko_bindings::bindings::Gecko_ClearStyleContents;

        // Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
        fn as_utf16_and_forget(s: &str) -> *mut u16 {
            use std::mem;
            let mut vec = s.encode_utf16().collect::<Vec<_>>();
            vec.push(0u16);
            let ptr = vec.as_mut_ptr();
            mem::forget(vec);
            ptr
        }

        #[inline(always)]
        #[cfg(debug_assertions)]
        fn set_image_tracked(contents: &mut nsStyleContentData, val: bool) {
            contents.mImageTracked = val;
        }

        #[inline(always)]
        #[cfg(not(debug_assertions))]
        fn set_image_tracked(_contents: &mut nsStyleContentData, _val: bool) {}

        // Ensure destructors run, otherwise we could leak.
        if !self.gecko.mContents.is_empty() {
            unsafe {
                Gecko_ClearStyleContents(&mut self.gecko);
            }
        }

        match v {
            T::none |
            T::normal => {}, // Do nothing, already cleared.
            T::Content(items) => {
                // NB: set_len also reserves the appropriate space.
                unsafe { self.gecko.mContents.set_len(items.len() as u32) }
                for (i, item) in items.into_iter().enumerate() {
                    // TODO: Servo lacks support for attr(), and URIs,
                    // We don't support images, but need to remember to
                    // explicitly initialize mImageTracked in debug builds.
                    set_image_tracked(&mut self.gecko.mContents[i], false);
                    // NB: Gecko compares the mString value if type is not image
                    // or URI independently of whatever gets there. In the quote
                    // cases, they set it to null, so do the same here.
                    unsafe {
                        *self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut();
                    }
                    match item {
                        ContentItem::String(value) => {
                            self.gecko.mContents[i].mType = eStyleContentType_String;
                            unsafe {
                                // NB: we share allocators, so doing this is fine.
                                *self.gecko.mContents[i].mContent.mString.as_mut() =
                                    as_utf16_and_forget(&value);
                            }
                        }
                        ContentItem::OpenQuote
                            => self.gecko.mContents[i].mType = eStyleContentType_OpenQuote,
                        ContentItem::CloseQuote
                            => self.gecko.mContents[i].mType = eStyleContentType_CloseQuote,
                        ContentItem::NoOpenQuote
                            => self.gecko.mContents[i].mType = eStyleContentType_NoOpenQuote,
                        ContentItem::NoCloseQuote
                            => self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote,
                        ContentItem::Counter(..) |
                        ContentItem::Counters(..)
                            => self.gecko.mContents[i].mType = eStyleContentType_Uninitialized,
                    }
                }
            }
        }
    }

    pub fn copy_content_from(&mut self, other: &Self) {
        use gecko_bindings::bindings::Gecko_CopyStyleContentsFrom;
        unsafe {
            Gecko_CopyStyleContentsFrom(&mut self.gecko, &other.gecko)
        }
    }
</%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:
        ServoComputedValuesBorrowedOrNull) -> *const ${style_struct.gecko_ffi_name} {
    ComputedValues::arc_from_borrowed(&computed_values).unwrap().get_${style_struct.name_lower}().get_gecko()
        as *const ${style_struct.gecko_ffi_name}
}
</%def>

% for style_struct in data.style_structs:
${declare_style_struct(style_struct)}
${impl_style_struct(style_struct)}
% if not style_struct.name in data.manual_style_structs:
<%self:raw_impl_trait style_struct="${style_struct}"></%self:raw_impl_trait>
% endif
${define_ffi_struct_accessor(style_struct)}
% endfor

// To avoid UB, we store the initial values as a atomic. It would be nice to
// store them as AtomicPtr, but we can't have static AtomicPtr without const
// fns, which aren't in stable Rust.
static INITIAL_VALUES_STORAGE: AtomicUsize = ATOMIC_USIZE_INIT;
unsafe fn raw_initial_values() -> *mut ComputedValues {
    INITIAL_VALUES_STORAGE.load(Ordering::Relaxed) as *mut ComputedValues
}
unsafe fn set_raw_initial_values(v: *mut ComputedValues) {
    INITIAL_VALUES_STORAGE.store(v as usize, Ordering::Relaxed);
}

static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
    % for property in data.longhands:
        longhands::${property.ident}::cascade_property,
    % endfor
];
