Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  gecko.mako.rs   Sprache: unbekannt

 
/* 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 https://mozilla.org/MPL/2.0/. */

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

<%!
    from data import to_camel_case, to_camel_case_lower
    from data import Keyword
%>
<%namespace name="helpers" file="/helpers.mako.rs" />

use crate::Atom;
use app_units::Au;
use crate::computed_value_flags::*;
use crate::custom_properties::ComputedCustomProperties;
use crate::gecko_bindings::bindings;
% for style_struct in data.style_structs:
use crate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};
use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
% endfor
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
use crate::gecko::data::PerDocumentStyleData;
use crate::logical_geometry::WritingMode;
use crate::media_queries::Device;
use crate::properties::longhands;
use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement;
use servo_arc::{Arc, UniqueArc};
use std::mem::{forget, MaybeUninit, ManuallyDrop};
use std::{ops, ptr};
use crate::values;
use crate::values::computed::{BorderStyle, Time, Zoom};
use crate::values::computed::font::FontSize;


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

    unsafe impl Send for ${style_struct.name} {}
    unsafe impl Sync for ${style_struct.name} {}
    % endfor
}

/// FIXME(emilio): This is completely duplicated with the other properties code.
pub type ComputedValuesInner = structs::ServoComputedData;

#[repr(C)]
pub struct ComputedValues(structs::mozilla::ComputedStyle);

impl ComputedValues {
    #[inline]
    pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle {
        &self.0
    }

    pub fn new(
        pseudo: Option<<&PseudoElement>,
        custom_properties: ComputedCustomProperties,
        writing_mode: WritingMode,
        effective_zoom: Zoom,
        flags: ComputedValueFlags,
        rules: Option<StrongRuleNode>,
        visited_style: Option<Arc<ComputedValues>>,
        % for style_struct in data.style_structs:
        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
        % endfor
    ) -> Arc<Self> {
        ComputedValuesInner::new(
            custom_properties,
            writing_mode,
            effective_zoom,
            flags,
            rules,
            visited_style,
            % for style_struct in data.style_structs:
            ${style_struct.ident},
            % endfor
        ).to_outer(pseudo)
    }

    pub fn default_values(doc: &structs::Document) -> Arc<Self> {
        ComputedValuesInner::new(
            ComputedCustomProperties::default(),
            WritingMode::empty(), // FIXME(bz): This seems dubious
            Zoom::ONE,
            ComputedValueFlags::empty(),
            /* rules = */ None,
            /* visited_style = */ None,
            % for style_struct in data.style_structs:
            style_structs::${style_struct.name}::default(doc),
            % endfor
        ).to_outer(None)
    }

    /// Converts the computed values to an Arc<> from a reference.
    pub fn to_arc(&self) -> Arc<Self> {
        // SAFETY: We're guaranteed to be allocated as an Arc<> since the
        // functions above are the only ones that create ComputedValues
        // instances in Gecko (and that must be the case since ComputedValues'
        // member is private).
        unsafe { Arc::from_raw_addrefed(self) }
    }

    #[inline]
    pub fn is_pseudo_style(&self) -> bool {
        self.0.mPseudoType != PseudoStyleType::NotPseudo
    }

    #[inline]
    pub fn pseudo(&self) -> Option<PseudoElement> {
        if !self.is_pseudo_style() {
            return None;
        }
        PseudoElement::from_pseudo_type(self.0.mPseudoType, None)
    }

    #[inline]
    pub fn is_first_line_style(&self) -> bool {
        self.pseudo() == Some(PseudoElement::FirstLine)
    }

    /// Returns true if the display property is changed from 'none' to others.
    pub fn is_display_property_changed_from_none(
        &self,
        old_values: Option<<&ComputedValues>
    ) -> bool {
        use crate::properties::longhands::display::computed_value::T as Display;

        old_values.map_or(false, |old| {
            let old_display_style = old.get_box().clone_display();
            let new_display_style = self.get_box().clone_display();
            old_display_style == Display::None &&
            new_display_style != Display::None
        })
    }

}

impl Drop for ComputedValues {
    fn drop(&mut self) {
        // XXX this still relies on the destructor of ComputedValuesInner to run on the rust side,
        // that's pretty wild.
        unsafe {
            bindings::Gecko_ComputedStyle_Destroy(&mut self.0);
        }
    }
}

unsafe impl Sync for ComputedValues {}
unsafe impl Send for ComputedValues {}

impl Clone for ComputedValues {
    fn clone(&self) -> Self {
        unreachable!()
    }
}

impl Clone for ComputedValuesInner {
    fn clone(&self) -> Self {
        ComputedValuesInner {
            % for style_struct in data.style_structs:
            ${style_struct.gecko_name}: Arc::into_raw(unsafe { Arc::from_raw_addrefed(self.${style_struct.name_lower}_ptr()) }) as *const _,
            % endfor
            custom_properties: self.custom_properties.clone(),
            writing_mode: self.writing_mode.clone(),
            flags: self.flags.clone(),
            effective_zoom: self.effective_zoom,
            rules: self.rules.clone(),
            visited_style: if self.visited_style.is_null() {
                ptr::null()
            } else {
                Arc::into_raw(unsafe { Arc::from_raw_addrefed(self.visited_style_ptr()) }) as *const _
            },
        }
    }
}


impl Drop for ComputedValuesInner {
    fn drop(&mut self) {
        % for style_struct in data.style_structs:
        let _ = unsafe { Arc::from_raw(self.${style_struct.name_lower}_ptr()) };
        % endfor
        if !self.visited_style.is_null() {
            let _ = unsafe { Arc::from_raw(self.visited_style_ptr()) };
        }
    }
}

impl ComputedValuesInner {
    pub fn new(
        custom_properties: ComputedCustomProperties,
        writing_mode: WritingMode,
        effective_zoom: Zoom,
        flags: ComputedValueFlags,
        rules: Option<StrongRuleNode>,
        visited_style: Option<Arc<ComputedValues>>,
        % for style_struct in data.style_structs:
        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
        % endfor
    ) -> Self {
        Self {
            custom_properties,
            writing_mode,
            rules,
            visited_style: visited_style.map_or(ptr::null(), |p| Arc::into_raw(p)) as *const _,
            flags,
            effective_zoom,
            % for style_struct in data.style_structs:
            ${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
            % endfor
        }
    }

    fn to_outer(self, pseudo: Option<<&PseudoElement>) -> Arc<ComputedValues> {
        let pseudo_ty = match pseudo {
            Some(p) => p.pseudo_type(),
            None => structs::PseudoStyleType::NotPseudo,
        };
        unsafe {
            let mut arc = UniqueArc::<ComputedValues>::new_uninit();
            bindings::Gecko_ComputedStyle_Init(
                arc.as_mut_ptr() as *mut _,
                &self,
                pseudo_ty,
            );
            // We're simulating move semantics by having C++ do a memcpy and
            // then forgetting it on this end.
            forget(self);
            UniqueArc::assume_init(arc).shareable()
        }
    }
}

impl ops::Deref for ComputedValues {
    type Target = ComputedValuesInner;
    #[inline]
    fn deref(&self) -> &ComputedValuesInner {
        &self.0.mSource
    }
}

impl ops::DerefMut for ComputedValues {
    #[inline]
    fn deref_mut(&mut self) -> &mut ComputedValuesInner {
        &mut self.0.mSource
    }
}

impl ComputedValuesInner {
    /// Returns true if the value of the `content` property would make a
    /// pseudo-element not rendered.
    #[inline]
    pub fn ineffective_content_property(&self) -> bool {
        self.get_counters().ineffective_content_property()
    }

    #[inline]
    fn visited_style_ptr(&self) -> *const ComputedValues {
        self.visited_style as *const _
    }

    /// Returns the visited style, if any.
    pub fn visited_style(&self) -> Option<<&ComputedValues> {
        unsafe { self.visited_style_ptr().as_ref() }
    }

    % for style_struct in data.style_structs:
    #[inline]
    fn ${style_struct.name_lower}_ptr(&self) -> *const style_structs::${style_struct.name} {
        // This is sound because the wrapper we create is repr(transparent).
        self.${style_struct.gecko_name} as *const _
    }

    #[inline]
    pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
        unsafe { Arc::from_raw_addrefed(self.${style_struct.name_lower}_ptr()) }
    }
    #[inline]
    pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
        unsafe { &*self.${style_struct.name_lower}_ptr() }
    }

    #[inline]
    pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
        unsafe {
            let mut arc = Arc::from_raw(self.${style_struct.name_lower}_ptr());
            let ptr = Arc::make_mut(&mut arc) as *mut _;
            // Sound for the same reason _ptr() is sound.
            self.${style_struct.gecko_name} = Arc::into_raw(arc) as *const _;
            &mut *ptr
        }
    }
    % endfor
}

<%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, "From::from(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 {
        From::from(self.${gecko_ffi_name}.clone())
    }
</%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_ffi_name} = other.${gecko_ffi_name}.clone();
    }

    #[allow(non_snake_case)]
    pub fn reset_${ident}(&mut self, other: &Self) {
        self.copy_${ident}_from(other)
    }
</%def>

<%!
def get_gecko_property(ffi_name, self_param = "self"):
    return "%s.%s" % (self_param, ffi_name)

def set_gecko_property(ffi_name, expr):
    return "self.%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 crate::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_camel_case(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, cast_type='u8')">
    #[allow(non_snake_case)]
    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        use crate::properties::longhands::${ident}::computed_value::T as Keyword;
        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts

        // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).
        // And they are convert to signed integer in Rust bindings. We need to cast then
        // as signed type when we have both signed/unsigned integer in order to use them
        // as match's arms.
        // Also, to use same implementation here we use casted constant if we have only singed values.
        % if keyword.gecko_enum_prefix is None:
        % for value in keyword.values_for('gecko'):
        const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =
            structs::${keyword.gecko_constant(value)} as ${cast_type};
        % endfor

        match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
            % for value in keyword.values_for('gecko'):
            ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},
            % endfor
            % if keyword.gecko_inexhaustive:
            _ => panic!("Found unexpected value in style struct for ${ident} property"),
            % endif
        }
        % else:
        match ${get_gecko_property(gecko_ffi_name)} {
            % for value in keyword.values_for('gecko'):
            structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
            % endfor
            % if keyword.gecko_inexhaustive:
            _ => panic!("Found unexpected value in style struct for ${ident} property"),
            % endif
        }
        % endif
    }
</%def>

<%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)">
<%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
<%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"></%call>
<%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
</%def>

<%def name="impl_simple(ident, gecko_ffi_name)">
<%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
<%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
</%def>

<%def name="impl_border_width(ident, gecko_ffi_name, inherit_from)">
    #[allow(non_snake_case)]
    pub fn set_${ident}(&mut self, v: Au) {
        let value = v.0;
        self.${inherit_from} = value;
        self.${gecko_ffi_name} = value;
    }

    #[allow(non_snake_case)]
    pub fn copy_${ident}_from(&mut self, other: &Self) {
        self.${inherit_from} = other.${inherit_from};
        // NOTE: This is needed to easily handle the `unset` and `initial`
        // keywords, which are implemented calling this function.
        //
        // In practice, this means that we may have an incorrect value here, but
        // we'll adjust that properly in the style fixup phase.
        //
        // FIXME(emilio): We could clean this up a bit special-casing the reset_
        // function below.
        self.${gecko_ffi_name} = other.${inherit_from};
    }

    #[allow(non_snake_case)]
    pub fn reset_${ident}(&mut self, other: &Self) {
        self.copy_${ident}_from(other)
    }

    #[allow(non_snake_case)]
    pub fn clone_${ident}(&self) -> Au {
        Au(self.${gecko_ffi_name})
    }
</%def>

<%def name="impl_style_struct(style_struct)">
/// A wrapper for ${style_struct.gecko_ffi_name}, to be able to manually construct / destruct /
/// clone it.
#[repr(transparent)]
pub struct ${style_struct.gecko_struct_name}(ManuallyDrop<structs::${style_struct.gecko_ffi_name}>);

impl ops::Deref for ${style_struct.gecko_struct_name} {
    type Target = structs::${style_struct.gecko_ffi_name};
    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl ops::DerefMut for ${style_struct.gecko_struct_name} {
    #[inline]
    fn deref_mut(&mut self) -> &mut <Self as ops::Deref>::Target {
        &mut self.0
    }
}

impl ${style_struct.gecko_struct_name} {
    #[allow(dead_code, unused_variables)]
    pub fn default(document: &structs::Document) -> Arc<Self> {
% if style_struct.document_dependent:
        unsafe {
            let mut result = UniqueArc::<Self>::new_uninit();
            Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
                result.as_mut_ptr() as *mut _,
                document,
            );
            UniqueArc::assume_init(result).shareable()
        }
% else:
        lazy_static! {
            static ref DEFAULT: Arc<${style_struct.gecko_struct_name}> = unsafe {
                let mut result = UniqueArc::<${style_struct.gecko_struct_name}>::new_uninit();
                Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
                    result.as_mut_ptr() as *mut _,
                    std::ptr::null(),
                );
                let arc = UniqueArc::assume_init(result).shareable();
                arc.mark_as_intentionally_leaked();
                arc
            };
        };
        DEFAULT.clone()
% endif
    }
}

impl Drop for ${style_struct.gecko_struct_name} {
    fn drop(&mut self) {
        unsafe {
            Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut **self);
        }
    }
}
impl Clone for ${style_struct.gecko_struct_name} {
    fn clone(&self) -> Self {
        unsafe {
            let mut result = MaybeUninit::<Self>::uninit();
            // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
            // these looks like Valgrind false-positives at a quick glance.
            ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &**self);
            result.assume_init()
        }
    }
}
</%def>

<%def name="impl_font_settings(ident, gecko_type, tag_type, value_type, gecko_value_type)">
    <% gecko_ffi_name = to_camel_case_lower(ident) %>

    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
        let iter = v.0.iter().map(|other| structs::${gecko_type} {
            mTag: other.tag.0,
            mValue: other.value as ${gecko_value_type},
        });
        self.mFont.${gecko_ffi_name}.clear();
        self.mFont.${gecko_ffi_name}.extend(iter);
    }

    <% impl_simple_copy(ident, "mFont." + gecko_ffi_name) %>

    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        use crate::values::generics::font::{FontSettings, FontTag, ${tag_type}};

        FontSettings(
            self.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| {
                ${tag_type} {
                    tag: FontTag(gecko_font_setting.mTag),
                    value: gecko_font_setting.mValue as ${value_type},
                }
            }).collect()
        )
    }
</%def>

<%def name="impl_trait(style_struct_name, skip_longhands='')">
<%
    style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
    longhands = [x for x in style_struct.longhands
                if not (skip_longhands == "*" or x.name in skip_longhands.split())]

    def longhand_method(longhand):
        args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)

        if longhand.logical:
            return
        # get the method and pass additional keyword or type-specific arguments
        if longhand.keyword:
            method = impl_keyword
            args.update(keyword=longhand.keyword)
            if "font" in longhand.ident:
                args.update(cast_type=longhand.cast_type)
        else:
            method = impl_simple

        method(**args)
%>
impl ${style_struct.gecko_struct_name} {
    /*
     * Manually-Implemented Methods.
     */
    ${caller.body().strip()}

    /*
     * Auto-Generated Methods.
     */
    <%
    for longhand in longhands:
        longhand_method(longhand)
    %>
}
</%def>

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

SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
%>

#[allow(dead_code)]
fn static_assert() {
    // 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
}


<% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
                                     for x in SIDES
                                     for y in ["style", "width"]]) %>

<%self:impl_trait style_struct_name="Border"
                  skip_longhands="${skip_border_longhands}">
    % for side in SIDES:
    pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
        self.mBorderStyle[${side.index}] = v;

        // This is needed because the initial mComputedBorder value is set to
        // zero.
        //
        // In order to compute stuff, we start from the initial struct, and keep
        // going down the tree applying properties.
        //
        // That means, effectively, that when we set border-style to something
        // non-hidden, we should use the initial border instead.
        //
        // Servo stores the initial border-width in the initial struct, and then
        // adjusts as needed in the fixup phase. This means that the initial
        // struct is technically not valid without fixups, and that you lose
        // pretty much any sharing of the initial struct, which is kind of
        // unfortunate.
        //
        // Gecko has two fields for this, one that stores the "specified"
        // border, and other that stores the actual computed one. That means
        // that when we set border-style, border-width may change and we need to
        // sync back to the specified one. This is what this function does.
        //
        // Note that this doesn't impose any dependency in the order of
        // computation of the properties. This is only relevant if border-style
        // is specified, but border-width isn't. If border-width is specified at
        // some point, the two mBorder and mComputedBorder fields would be the
        // same already.
        //
        // Once we're here, we know that we'll run style fixups, so it's fine to
        // just copy the specified border here, we'll adjust it if it's
        // incorrect later.
        self.mComputedBorder.${side.ident} = self.mBorder.${side.ident};
    }

    pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) {
        self.set_border_${side.ident}_style(other.mBorderStyle[${side.index}]);
    }

    pub fn reset_border_${side.ident}_style(&mut self, other: &Self) {
        self.copy_border_${side.ident}_style_from(other);
    }

    #[inline]
    pub fn clone_border_${side.ident}_style(&self) -> BorderStyle {
        self.mBorderStyle[${side.index}]
    }

    ${impl_border_width("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, "mBorder.%s" % side.ident)}

    pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
        self.mComputedBorder.${side.ident} != 0
    }
    % endfor
</%self:impl_trait>

<%self:impl_trait style_struct_name="Margin"></%self:impl_trait>
<%self:impl_trait style_struct_name="Padding"></%self:impl_trait>
<%self:impl_trait style_struct_name="Page"></%self:impl_trait>

<%self:impl_trait style_struct_name="Position">
    pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
        debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
        self.mJustifyItems.computed = v;
    }
</%self:impl_trait>

<%self:impl_trait style_struct_name="Outline"
                  skip_longhands="outline-style outline-width">

    pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
        self.mOutlineStyle = v;
        // NB: This is needed to correctly handling the initial value of
        // outline-width when outline-style changes, see the
        // update_border_${side.ident} comment for more details.
        self.mActualOutlineWidth = self.mOutlineWidth;
    }

    pub fn copy_outline_style_from(&mut self, other: &Self) {
        self.set_outline_style(other.mOutlineStyle);
    }

    pub fn reset_outline_style(&mut self, other: &Self) {
        self.copy_outline_style_from(other)
    }

    pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
        self.mOutlineStyle.clone()
    }

    ${impl_border_width("outline_width", "mActualOutlineWidth", "mOutlineWidth")}

    pub fn outline_has_nonzero_width(&self) -> bool {
        self.mActualOutlineWidth != 0
    }
</%self:impl_trait>

<% skip_font_longhands = """font-size -x-lang font-feature-settings font-variation-settings""" %>
<%self:impl_trait style_struct_name="Font"
    skip_longhands="${skip_font_longhands}">

    // Negative numbers are invalid at parse time, but <integer> is still an
    // i32.
    <% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
    <% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>

    pub fn unzoom_fonts(&mut self, device: &Device) {
        use crate::values::generics::NonNegative;
        self.mSize = NonNegative(device.unzoom_text(self.mSize.0));
        self.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.mScriptUnconstrainedSize.0));
        self.mFont.size = NonNegative(device.unzoom_text(self.mFont.size.0));
    }

    pub fn copy_font_size_from(&mut self, other: &Self) {
        self.mScriptUnconstrainedSize = other.mScriptUnconstrainedSize;

        self.mSize = other.mScriptUnconstrainedSize;
        // NOTE: Intentionally not copying from mFont.size. The cascade process
        // recomputes the used size as needed.
        self.mFont.size = other.mSize;
        self.mFontSizeKeyword = other.mFontSizeKeyword;

        // TODO(emilio): Should we really copy over these two?
        self.mFontSizeFactor = other.mFontSizeFactor;
        self.mFontSizeOffset = other.mFontSizeOffset;
    }

    pub fn reset_font_size(&mut self, other: &Self) {
        self.copy_font_size_from(other)
    }

    pub fn set_font_size(&mut self, v: FontSize) {
        let computed_size = v.computed_size;
        self.mScriptUnconstrainedSize = computed_size;

        // These two may be changed from Cascade::fixup_font_stuff.
        self.mSize = computed_size;
        // NOTE: Intentionally not copying from used_size. The cascade process
        // recomputes the used size as needed.
        self.mFont.size = computed_size;

        self.mFontSizeKeyword = v.keyword_info.kw;
        self.mFontSizeFactor = v.keyword_info.factor;
        self.mFontSizeOffset = v.keyword_info.offset;
    }

    pub fn clone_font_size(&self) -> FontSize {
        use crate::values::specified::font::KeywordInfo;

        FontSize {
            computed_size: self.mSize,
            used_size: self.mFont.size,
            keyword_info: KeywordInfo {
                kw: self.mFontSizeKeyword,
                factor: self.mFontSizeFactor,
                offset: self.mFontSizeOffset,
            }
        }
    }

    #[allow(non_snake_case)]
    pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
        let ptr = v.0.as_ptr();
        forget(v);
        unsafe {
            Gecko_nsStyleFont_SetLang(&mut **self, ptr);
        }
    }

    #[allow(non_snake_case)]
    pub fn copy__x_lang_from(&mut self, other: &Self) {
        unsafe {
            Gecko_nsStyleFont_CopyLangFrom(&mut **self, &**other);
        }
    }

    #[allow(non_snake_case)]
    pub fn reset__x_lang(&mut self, other: &Self) {
        self.copy__x_lang_from(other)
    }

    #[allow(non_snake_case)]
    pub fn clone__x_lang(&self) -> longhands::_x_lang::computed_value::T {
        longhands::_x_lang::computed_value::T(unsafe {
            Atom::from_raw(self.mLanguage.mRawPtr)
        })
    }
</%self:impl_trait>

<%def name="impl_coordinated_property_copy(type, ident, gecko_ffi_name)">
    #[allow(non_snake_case)]
    pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
        self.m${to_camel_case(type)}s.ensure_len(other.m${to_camel_case(type)}s.len());

        let count = other.m${to_camel_case(type)}${gecko_ffi_name}Count;
        self.m${to_camel_case(type)}${gecko_ffi_name}Count = count;

        let iter = self.m${to_camel_case(type)}s.iter_mut().take(count as usize).zip(
            other.m${to_camel_case(type)}s.iter()
        );

        for (ours, others) in iter {
            ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}.clone();
        }
    }
    #[allow(non_snake_case)]
    pub fn reset_${type}_${ident}(&mut self, other: &Self) {
        self.copy_${type}_${ident}_from(other)
    }
</%def>

<%def name="impl_coordinated_property_count(type, ident, gecko_ffi_name)">
    #[allow(non_snake_case)]
    pub fn ${type}_${ident}_count(&self) -> usize {
        self.m${to_camel_case(type)}${gecko_ffi_name}Count as usize
    }
</%def>

<%def name="impl_coordinated_property(type, ident, gecko_ffi_name)">
    #[allow(non_snake_case)]
    pub fn set_${type}_${ident}<I>(&mut self, v: I)
    where
        I: IntoIterator<Item = longhands::${type}_${ident}::computed_value::single_value::T>,
        I::IntoIter: ExactSizeIterator + Clone
    {
        let v = v.into_iter();
        debug_assert_ne!(v.len(), 0);
        let input_len = v.len();
        self.m${to_camel_case(type)}s.ensure_len(input_len);

        self.m${to_camel_case(type)}${gecko_ffi_name}Count = input_len as u32;
        for (gecko, servo) in self.m${to_camel_case(type)}s.iter_mut().take(input_len as usize).zip(v) {
            gecko.m${gecko_ffi_name} = servo;
        }
    }
    #[allow(non_snake_case)]
    pub fn ${type}_${ident}_at(&self, index: usize)
        -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
        self.m${to_camel_case(type)}s[index % self.${type}_${ident}_count()].m${gecko_ffi_name}.clone()
    }
    ${impl_coordinated_property_copy(type, ident, gecko_ffi_name)}
    ${impl_coordinated_property_count(type, ident, gecko_ffi_name)}
</%def>

<% skip_box_longhands= """display contain""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
    #[inline]
    pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
        self.mDisplay = v;
        self.mOriginalDisplay = v;
    }

    #[inline]
    pub fn copy_display_from(&mut self, other: &Self) {
        self.set_display(other.mDisplay);
    }

    #[inline]
    pub fn reset_display(&mut self, other: &Self) {
        self.copy_display_from(other)
    }

    #[inline]
    pub fn set_adjusted_display(
        &mut self,
        v: longhands::display::computed_value::T,
        _is_item_or_root: bool
    ) {
        self.mDisplay = v;
    }

    #[inline]
    pub fn clone_display(&self) -> longhands::display::computed_value::T {
        self.mDisplay
    }

    #[inline]
    pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {
        self.mContain = v;
        self.mEffectiveContainment = v;
    }

    #[inline]
    pub fn copy_contain_from(&mut self, other: &Self) {
        self.set_contain(other.mContain);
    }

    #[inline]
    pub fn reset_contain(&mut self, other: &Self) {
        self.copy_contain_from(other)
    }

    #[inline]
    pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
        self.mContain
    }

    #[inline]
    pub fn set_effective_containment(
        &mut self,
        v: longhands::contain::computed_value::T
    ) {
        self.mEffectiveContainment = v;
    }

    #[inline]
    pub fn clone_effective_containment(&self) -> longhands::contain::computed_value::T {
        self.mEffectiveContainment
    }
</%self:impl_trait>

<%def name="simple_image_array_property(name, shorthand, field_name)">
    <%
        image_layers_field = "mImage" if shorthand == "background" else "mMask"
        copy_simple_image_array_property(name, shorthand, image_layers_field, field_name)
    %>

    pub fn set_${shorthand}_${name}<I>(&mut self, v: I)
        where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,
              I::IntoIter: ExactSizeIterator
    {
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
        let v = v.into_iter();

        unsafe {
          Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
                                        LayerType::${shorthand.title()});
        }

        self.${image_layers_field}.${field_name}Count = v.len() as u32;
        for (servo, geckolayer) in v.zip(self.${image_layers_field}.mLayers.iter_mut()) {
            geckolayer.${field_name} = {
                ${caller.body()}
            };
        }
    }
</%def>

<%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)">
    pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;

        let count = other.${layers_field_name}.${field_name}Count;
        unsafe {
            Gecko_EnsureImageLayersLength(&mut self.${layers_field_name},
                                          count as usize,
                                          LayerType::${shorthand.title()});
        }
        // FIXME(emilio): This may be bogus in the same way as bug 1426246.
        for (layer, other) in self.${layers_field_name}.mLayers.iter_mut()
                                  .zip(other.${layers_field_name}.mLayers.iter())
                                  .take(count as usize) {
            layer.${field_name} = other.${field_name}.clone();
        }
        self.${layers_field_name}.${field_name}Count = count;
    }

    pub fn reset_${shorthand}_${name}(&mut self, other: &Self) {
        self.copy_${shorthand}_${name}_from(other)
    }
</%def>

<%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)">
    <%
        ident = "%s_%s" % (shorthand, name)
        style_struct = next(x for x in data.style_structs if x.name == struct_name)
        longhand = next(x for x in style_struct.longhands if x.ident == ident)
        keyword = longhand.keyword
    %>

    <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %>

    pub fn set_${ident}<I>(&mut self, v: I)
    where
        I: IntoIterator<Item=longhands::${ident}::computed_value::single_value::T>,
        I::IntoIter: ExactSizeIterator,
    {
        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;

        let v = v.into_iter();

        unsafe {
          Gecko_EnsureImageLayersLength(&mut self.${layer_field_name}, v.len(),
                                        LayerType::${shorthand.title()});
        }

        self.${layer_field_name}.${field_name}Count = v.len() as u32;
        for (servo, geckolayer) in v.zip(self.${layer_field_name}.mLayers.iter_mut()) {
            geckolayer.${field_name} = {
                match servo {
                    % for value in keyword.values_for("gecko"):
                    Keyword::${to_camel_case(value)} =>
                        structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
                    % endfor
                }
            };
        }
    }

    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;

        % if keyword.needs_cast():
        % for value in keyword.values_for('gecko'):
        const ${keyword.casted_constant_name(value, "u8")} : u8 =
            structs::${keyword.gecko_constant(value)} as u8;
        % endfor
        % endif

        longhands::${ident}::computed_value::List(
            self.${layer_field_name}.mLayers.iter()
                .take(self.${layer_field_name}.${field_name}Count as usize)
                .map(|ref layer| {
                    match layer.${field_name} {
                        % for value in longhand.keyword.values_for("gecko"):
                        % if keyword.needs_cast():
                        ${keyword.casted_constant_name(value, "u8")}
                        % else:
                        structs::${keyword.gecko_constant(value)}
                        % endif
                            => Keyword::${to_camel_case(value)},
                        % endfor
                        % if keyword.gecko_inexhaustive:
                        _ => panic!("Found unexpected value in style struct for ${ident} property"),
                        % endif
                    }
                }).collect()
        )
    }
</%def>

<%def name="impl_common_image_layer_properties(shorthand)">
    <%
        if shorthand == "background":
            image_layers_field = "mImage"
            struct_name = "Background"
        else:
            image_layers_field = "mMask"
            struct_name = "SVG"
    %>

    <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
        use crate::values::specified::background::BackgroundRepeatKeyword;
        use crate::gecko_bindings::structs::nsStyleImageLayers_Repeat;
        use crate::gecko_bindings::structs::StyleImageLayerRepeat;

        fn to_ns(repeat: BackgroundRepeatKeyword) -> StyleImageLayerRepeat {
            match repeat {
                BackgroundRepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,
                BackgroundRepeatKeyword::Space => StyleImageLayerRepeat::Space,
                BackgroundRepeatKeyword::Round => StyleImageLayerRepeat::Round,
                BackgroundRepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat,
            }
        }

        let repeat_x = to_ns(servo.0);
        let repeat_y = to_ns(servo.1);
        nsStyleImageLayers_Repeat {
              mXRepeat: repeat_x,
              mYRepeat: repeat_y,
        }
    </%self:simple_image_array_property>

    pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {
        use crate::properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
        use crate::values::specified::background::BackgroundRepeatKeyword;
        use crate::gecko_bindings::structs::StyleImageLayerRepeat;

        fn to_servo(repeat: StyleImageLayerRepeat) -> BackgroundRepeatKeyword {
            match repeat {
                StyleImageLayerRepeat::Repeat => BackgroundRepeatKeyword::Repeat,
                StyleImageLayerRepeat::Space => BackgroundRepeatKeyword::Space,
                StyleImageLayerRepeat::Round => BackgroundRepeatKeyword::Round,
                StyleImageLayerRepeat::NoRepeat => BackgroundRepeatKeyword::NoRepeat,
                _ => panic!("Found unexpected value in style struct for ${shorthand}_repeat property"),
            }
        }

        longhands::${shorthand}_repeat::computed_value::List(
            self.${image_layers_field}.mLayers.iter()
                .take(self.${image_layers_field}.mRepeatCount as usize)
                .map(|ref layer| {
                    T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat))
                }).collect()
        )
    }

    <% impl_simple_image_array_property("clip", shorthand, image_layers_field, "mClip", struct_name) %>
    <% impl_simple_image_array_property("origin", shorthand, image_layers_field, "mOrigin", struct_name) %>

    % for (orientation, keyword) in [("x", "horizontal"), ("y", "vertical")]:
    pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;

        let count = other.${image_layers_field}.mPosition${orientation.upper()}Count;

        unsafe {
            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
                                          count as usize,
                                          LayerType::${shorthand.capitalize()});
        }

        for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
                                  .zip(other.${image_layers_field}.mLayers.iter())
                                  .take(count as usize) {
            layer.mPosition.${keyword} = other.mPosition.${keyword}.clone();
        }
        self.${image_layers_field}.mPosition${orientation.upper()}Count = count;
    }

    pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) {
        self.copy_${shorthand}_position_${orientation}_from(other)
    }

    pub fn clone_${shorthand}_position_${orientation}(&self)
        -> longhands::${shorthand}_position_${orientation}::computed_value::T {
        longhands::${shorthand}_position_${orientation}::computed_value::List(
            self.${image_layers_field}.mLayers.iter()
                .take(self.${image_layers_field}.mPosition${orientation.upper()}Count as usize)
                .map(|position| position.mPosition.${keyword}.clone())
                .collect()
        )
    }

    pub fn set_${shorthand}_position_${orientation[0]}<I>(&mut self,
                                     v: I)
        where I: IntoIterator<Item = longhands::${shorthand}_position_${orientation[0]}
                                              ::computed_value::single_value::T>,
              I::IntoIter: ExactSizeIterator
    {
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;

        let v = v.into_iter();

        unsafe {
            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
                                        LayerType::${shorthand.capitalize()});
        }

        self.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;
        for (servo, geckolayer) in v.zip(self.${image_layers_field}
                                                           .mLayers.iter_mut()) {
            geckolayer.mPosition.${keyword} = servo;
        }
    }
    % endfor

    <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
        servo
    </%self:simple_image_array_property>

    pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {
        longhands::${shorthand}_size::computed_value::List(
            self.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect()
        )
    }

    pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
        unsafe {
            let count = other.${image_layers_field}.mImageCount;
            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
                                          count as usize,
                                          LayerType::${shorthand.capitalize()});

            for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
                                      .zip(other.${image_layers_field}.mLayers.iter())
                                      .take(count as usize) {
                layer.mImage = other.mImage.clone();
            }
            self.${image_layers_field}.mImageCount = count;
        }
    }

    pub fn reset_${shorthand}_image(&mut self, other: &Self) {
        self.copy_${shorthand}_image_from(other)
    }

    #[allow(unused_variables)]
    pub fn set_${shorthand}_image<I>(&mut self, images: I)
        where I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,
              I::IntoIter: ExactSizeIterator
    {
        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;

        let images = images.into_iter();

        unsafe {
            Gecko_EnsureImageLayersLength(
                &mut self.${image_layers_field},
                images.len(),
                LayerType::${shorthand.title()},
            );
        }

        self.${image_layers_field}.mImageCount = images.len() as u32;
        for (image, geckoimage) in images.zip(self.${image_layers_field}
                                                  .mLayers.iter_mut()) {
            geckoimage.mImage = image;
        }
    }

    pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {
        longhands::${shorthand}_image::computed_value::List(
            self.${image_layers_field}.mLayers.iter()
                .take(self.${image_layers_field}.mImageCount as usize)
                .map(|layer| layer.mImage.clone())
                .collect()
        )
    }

    <%
        fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize"
        if shorthand == "background":
            fill_fields += " mAttachment mBlendMode"
        else:
            # mSourceURI uses mImageCount
            fill_fields += " mMaskMode mComposite"
    %>
    pub fn fill_arrays(&mut self) {
        use crate::gecko_bindings::bindings::Gecko_FillAllImageLayers;
        use std::cmp;
        let mut max_len = 1;
        % for member in fill_fields.split():
            max_len = cmp::max(max_len, self.${image_layers_field}.${member}Count);
        % endfor
        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_FillAllImageLayers(&mut self.${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-repeat
                                  background-image background-clip
                                  background-origin background-attachment
                                  background-size background-position
                                  background-blend-mode
                                  background-position-x
                                  background-position-y""" %>
<%self:impl_trait style_struct_name="Background"
                  skip_longhands="${skip_background_longhands}">

    <% impl_common_image_layer_properties("background") %>
    <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
    <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
</%self:impl_trait>

<%self:impl_trait style_struct_name="List">
</%self:impl_trait>

<%self:impl_trait style_struct_name="Table">
</%self:impl_trait>

<%self:impl_trait style_struct_name="Effects">
</%self:impl_trait>

<%self:impl_trait style_struct_name="InheritedBox">
</%self:impl_trait>

<%self:impl_trait style_struct_name="InheritedTable">
</%self:impl_trait>

<%self:impl_trait style_struct_name="InheritedText">
</%self:impl_trait>

<%self:impl_trait style_struct_name="Text">
</%self:impl_trait>

<% skip_svg_longhands = """
mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image
"""
%>
<%self:impl_trait style_struct_name="SVG"
                  skip_longhands="${skip_svg_longhands}">
    <% impl_common_image_layer_properties("mask") %>
    <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
    <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
</%self:impl_trait>

<%self:impl_trait style_struct_name="InheritedSVG">
</%self:impl_trait>

<%self:impl_trait style_struct_name="InheritedUI">
    #[inline]
    pub fn color_scheme_bits(&self) -> values::specified::color::ColorSchemeFlags {
        self.mColorScheme.bits
    }
</%self:impl_trait>

<%self:impl_trait style_struct_name="Column"
                  skip_longhands="column-rule-width column-rule-style">
    pub fn set_column_rule_style(&mut self, v: longhands::column_rule_style::computed_value::T) {
        self.mColumnRuleStyle = v;
        // NB: This is needed to correctly handling the initial value of
        // column-rule-width when colun-rule-style changes, see the
        // update_border_${side.ident} comment for more details.
        self.mActualColumnRuleWidth = self.mColumnRuleWidth;
    }

    pub fn copy_column_rule_style_from(&mut self, other: &Self) {
        self.set_column_rule_style(other.mColumnRuleStyle);
    }

    pub fn reset_column_rule_style(&mut self, other: &Self) {
        self.copy_column_rule_style_from(other)
    }

    pub fn clone_column_rule_style(&self) -> longhands::column_rule_style::computed_value::T {
        self.mColumnRuleStyle.clone()
    }

    ${impl_border_width("column_rule_width", "mActualColumnRuleWidth", "mColumnRuleWidth")}

    pub fn column_rule_has_nonzero_width(&self) -> bool {
        self.mActualColumnRuleWidth != 0
    }
</%self:impl_trait>

<%self:impl_trait style_struct_name="Counters">
    pub fn ineffective_content_property(&self) -> bool {
        !self.mContent.is_items()
    }
</%self:impl_trait>

<% skip_ui_longhands = """animation-name animation-delay animation-duration
                          animation-direction animation-fill-mode
                          animation-play-state animation-iteration-count
                          animation-timing-function animation-composition animation-timeline
                          transition-behavior transition-duration transition-delay
                          transition-timing-function transition-property
                          scroll-timeline-name scroll-timeline-axis
                          view-timeline-name view-timeline-axis view-timeline-inset""" %>

<%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}">
    ${impl_coordinated_property('transition', 'behavior', 'Behavior')}
    ${impl_coordinated_property('transition', 'delay', 'Delay')}
    ${impl_coordinated_property('transition', 'duration', 'Duration')}
    ${impl_coordinated_property('transition', 'timing_function', 'TimingFunction')}
    ${impl_coordinated_property('transition', 'property', 'Property')}

    pub fn transition_combined_duration_at(&self, index: usize) -> Time {
        // https://drafts.csswg.org/css-transitions/#transition-combined-duration
        Time::from_seconds(
            self.transition_duration_at(index).seconds().max(0.0) +
            self.transition_delay_at(index).seconds()
        )
    }

    /// Returns whether there are any transitions specified.
    pub fn specifies_transitions(&self) -> bool {
        if self.mTransitionPropertyCount == 1 &&
            self.transition_combined_duration_at(0).seconds() <= 0.0f32 {
            return false;
        }
        self.mTransitionPropertyCount > 0
    }

    /// Returns whether animation-timeline is initial value. We need this information to resolve
    /// animation-duration.
    pub fn has_initial_animation_timeline(&self) -> bool {
        self.mAnimationTimelineCount == 1 && self.animation_timeline_at(0).is_auto()
    }

    pub fn animations_equals(&self, other: &Self) -> bool {
        return self.mAnimationNameCount == other.mAnimationNameCount
            && self.mAnimationDelayCount == other.mAnimationDelayCount
            && self.mAnimationDirectionCount == other.mAnimationDirectionCount
            && self.mAnimationDurationCount == other.mAnimationDurationCount
            && self.mAnimationFillModeCount == other.mAnimationFillModeCount
            && self.mAnimationIterationCountCount == other.mAnimationIterationCountCount
            && self.mAnimationPlayStateCount == other.mAnimationPlayStateCount
            && self.mAnimationTimingFunctionCount == other.mAnimationTimingFunctionCount
            && self.mAnimationCompositionCount == other.mAnimationCompositionCount
            && self.mAnimationTimelineCount == other.mAnimationTimelineCount
            && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.mAnimations, &other.mAnimations) }
    }

    ${impl_coordinated_property('animation', 'name', 'Name')}
    ${impl_coordinated_property('animation', 'delay', 'Delay')}
    ${impl_coordinated_property('animation', 'duration', 'Duration')}
    ${impl_coordinated_property('animation', 'direction', 'Direction')}
    ${impl_coordinated_property('animation', 'fill_mode', 'FillMode')}
    ${impl_coordinated_property('animation', 'play_state', 'PlayState')}
    ${impl_coordinated_property('animation', 'composition', 'Composition')}
    ${impl_coordinated_property('animation', 'iteration_count', 'IterationCount')}
    ${impl_coordinated_property('animation', 'timeline', 'Timeline')}
    ${impl_coordinated_property('animation', 'timing_function', 'TimingFunction')}

    ${impl_coordinated_property('scroll_timeline', 'name', 'Name')}
    ${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}

    pub fn scroll_timelines_equals(&self, other: &Self) -> bool {
        self.mScrollTimelineNameCount == other.mScrollTimelineNameCount
            && self.mScrollTimelineAxisCount == other.mScrollTimelineAxisCount
            && unsafe {
                bindings::Gecko_StyleScrollTimelinesEquals(
                    &self.mScrollTimelines,
                    &other.mScrollTimelines,
                )
            }
    }

    ${impl_coordinated_property('view_timeline', 'name', 'Name')}
    ${impl_coordinated_property('view_timeline', 'axis', 'Axis')}
    ${impl_coordinated_property('view_timeline', 'inset', 'Inset')}

    pub fn view_timelines_equals(&self, other: &Self) -> bool {
        self.mViewTimelineNameCount == other.mViewTimelineNameCount
            && self.mViewTimelineAxisCount == other.mViewTimelineAxisCount
            && self.mViewTimelineInsetCount == other.mViewTimelineInsetCount
            && unsafe {
                bindings::Gecko_StyleViewTimelinesEquals(
                    &self.mViewTimelines,
                    &other.mViewTimelines,
                )
            }
    }
</%self:impl_trait>

<%self:impl_trait style_struct_name="XUL">
</%self:impl_trait>

% for style_struct in data.style_structs:
${impl_style_struct(style_struct)}
% endfor

/// Assert that the initial values set in Gecko style struct constructors
/// match the values returned by `get_initial_value()` for each longhand.
#[cfg(feature = "gecko")]
#[inline]
pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
    if cfg!(debug_assertions) {
        let data = data.borrow();
        let cv = data.stylist.device().default_computed_values();
        <%
            # Skip properties with initial values that change at computed
            # value time, or whose initial value depends on the document
            # / other prefs.
            SKIPPED = [
                "border-top-width",
                "border-bottom-width",
                "border-left-width",
                "border-right-width",
                "column-rule-width",
                "font-family",
                "font-size",
                "outline-width",
                "color",
            ]
            TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
        %>
        % for property in TO_TEST:
        assert_eq!(
            cv.clone_${property.ident}(),
            longhands::${property.ident}::get_initial_value(),
            concat!(
                "initial value in Gecko style struct for ",
                stringify!(${property.ident}),
                " must match longhands::",
                stringify!(${property.ident}),
                "::get_initial_value()"
            )
        );
        % endfor
    }
}

[ Dauer der Verarbeitung: 0.45 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge