Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  gecko.mako.rs

  Sprache: Rust
 

/* 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, Keyword, SYSTEM_FONT_LONGHANDS %>
<%namespace name="helpers" file="/helpers.mako.rs" />

use crate::Atom;
use crate::logical_geometry::PhysicalSide;
use crate::computed_value_flags::*;
use crate::custom_properties::ComputedCustomProperties;
use crate::device::Device;
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::{self, PseudoStyleType};
use crate::gecko::data::PerDocumentStyleData;
use crate::logical_geometry::WritingMode;
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::{Time, Zoom};
use crate::values::computed::font::FontSize;
use crate::dom::AttributeReferences;


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 (cratefn as_gecko_computed_style(&self) -> &structs::ComputedStyle {
        &self.0
    }

    pub fn new(
        pseudo: Option<&PseudoElement>,
        custom_properties: ComputedCustomProperties,
        attributes_referenced: AttributeReferences,
        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(
            pseudo,
            custom_properties,
            attributes_referenced,
            writing_mode,
            effective_zoom,
            flags,
            rules,
            visited_style,
            % for style_struct in data.style_structs:
            ${style_struct.ident},
            % endfor
        ).to_outer()
    }

    pub fn default_values(doc: &structs::Document) -> Arc<Self> {
        ComputedValuesInner::new(
            /* pseudo = */ None,
            ComputedCustomProperties::default(),
            AttributeReferences::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()
    }

    /// 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.pseudo_type != PseudoStyleType::NotPseudo
    }

    #[inline]
    pub fn pseudo(&self) -> Option<PseudoElement> {
        if !self.is_pseudo_style() {
            return None;
        }
        PseudoElement::from_pseudo_type(self.pseudo_type, 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
        })
    }

    /// Calls the given function for each cached lazy pseudo-element style.
    pub fn each_cached_lazy_pseudo<F>(&selfmut f: F)
    where
        F: FnMut(&Self),
    {
        thin_vec::auto_thin_vec!(let array: [*const structs::ComputedStyle; 4]);
        unsafe {
            bindings::Gecko_GetCachedLazyPseudoStyles(
                self.as_gecko_computed_style(),
                array.as_mut().as_mut_ptr(),
            );
        }
        for style in array.iter() {
            // ComputedValues is a newtype around ComputedStyle, so same layout
            let values: &ComputedValues = unsafe { &*(*style as *const ComputedValues) };
            f(values);
        }
    }

}

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 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(
        pseudo: Option<&PseudoElement>,
        custom_properties: ComputedCustomProperties,
        attribute_references: AttributeReferences,
        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 {
        let pseudo_type = match pseudo {
            Some(p) => p.pseudo_type(),
            None => PseudoStyleType::NotPseudo,
        };
        Self {
            custom_properties,
            attribute_references,
            writing_mode,
            rules,
            visited_style: visited_style.map_or(ptr::null(), Arc::into_raw) as *const _,
            flags,
            pseudo_type,
            effective_zoom,
            % for style_struct in data.style_structs:
            ${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
            % endfor
        }
    }

    // Share ComputedValues but with different flags.
    pub fn clone_with_flags(&self, flags: ComputedValueFlags, pseudo: Option<&PseudoElement>) -> Arc<ComputedValues> {
        Self::new(
            pseudo,
            self.custom_properties.clone(),
            self.attribute_references.clone(),
            self.writing_mode.clone(),
            self.effective_zoom.clone(),
            flags,
            self.rules.clone(),
            if self.visited_style.is_null() {
                None
            } else {
                Some(unsafe { Arc::from_raw_addrefed(self.visited_style as *const _) })
            },
            % for style_struct in data.style_structs:
            unsafe { Arc::from_raw_addrefed(self.${style_struct.gecko_name} as *const _) },
            % endfor
        ).to_outer()
    }

    fn to_outer(self) -> Arc<ComputedValues> {
        unsafe {
            let mut arc = UniqueArc::<ComputedValues>::new_uninit();
            bindings::Gecko_ComputedStyle_Init(
                arc.as_mut_ptr() as *mut _,
                &self
            );
            // 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 get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
        unsafe { &*self.${style_struct.name_lower}_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_eq(ident, gecko_ffi_name)">
    #[allow(non_snake_case)]
    pub fn ${ident}_equals(&self, other: &Self) -> bool {
        self.${gecko_ffi_name} == other.${gecko_ffi_name}
    }
</%def>

<%def name="impl_fallback_eq(ident)">
    #[allow(non_snake_case)]
    pub fn ${ident}_equals(&self, other: &Self) -> bool {
        // TODO: Could be more efficient
        self.clone_${ident}() == other.clone_${ident}()
    }
</%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_physical_sides(ident, props)">
    pub fn get_${ident}(&self, s: PhysicalSide) -> &longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T {
        match s {
            PhysicalSide::Top => &self.${data.longhands_by_name[props[0]].gecko_ffi_name},
            PhysicalSide::Right => &self.${data.longhands_by_name[props[1]].gecko_ffi_name},
            PhysicalSide::Bottom => &self.${data.longhands_by_name[props[2]].gecko_ffi_name},
            PhysicalSide::Left => &self.${data.longhands_by_name[props[3]].gecko_ffi_name},
        }
    }
    pub fn set_${ident}(&mut self, s: PhysicalSide, v: longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T) {
        match s {
            PhysicalSide::Top => self.set_${data.longhands_by_name[props[0]].ident}(v),
            PhysicalSide::Right => self.set_${data.longhands_by_name[props[1]].ident}(v),
            PhysicalSide::Bottom => self.set_${data.longhands_by_name[props[2]].ident}(v),
            PhysicalSide::Left => self.set_${data.longhands_by_name[props[3]].ident}(v),
        }
    }
</%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>
<%call expr="impl_simple_eq(ident, gecko_ffi_name)"></%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>
<%call expr="impl_simple_eq(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})
    }

    ${impl_simple_eq(ident, 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:
        static DEFAULT: std::sync::LazyLock<Arc<${style_struct.gecko_struct_name}>> = std::sync::LazyLock::new(|| 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(), 01);
            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &**an style='color:red'>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)}
    ${impl_fallback_eq(ident)}

    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
}


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

<%self:impl_trait style_struct_name="Margin">
    ${impl_physical_sides("margin", ["margin-top""margin-right""margin-bottom""margin-left"])}
</%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">
    ${impl_physical_sides("inset", ["top""right""bottom""left"])}
    pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
        debug_assert_ne!(v, values::specified::JustifyItems::legacy());
        self.mJustifyItems.computed = v;
    }
</%self:impl_trait>

<%self:impl_trait style_struct_name="Outline">
</%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 font_size_equals(&self, other: &Self) -> bool {
        self.mSize == other.mSize
    }

    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)
        })
    }

    #[allow(non_snake_case)]
    pub fn _x_lang_equals(&self, other: &Self) -> bool {
        self.mLanguage.mRawPtr == other.mLanguage.mRawPtr
    }
</%self:impl_trait>


<%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()
    }
    #[allow(non_snake_case)]
    pub fn copy_${type}_${ident}_from(&mut self, other: &'color:red'>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)
    }
    #[allow(non_snake_case)]
    pub fn ${type}_${ident}_count(&self) -> usize {
        self.m${to_camel_case(type)}${gecko_ffi_name}Count as usize
    }
    #[allow(non_snake_case)]
    pub fn ${type}_${ident}_equals(&self, other: &Self) -> bool {
        let count = self.${type}_${ident}_count();
        if count != other.${type}_${ident}_count() {
            return false;
        }
        let iter = self.m${to_camel_case(type)}s.iter().take(count as usize).zip(
            other.m${to_camel_case(type)}s.iter()
        );
        for (ours, others) in iter {
            if ours.m${gecko_ffi_name} != others.m${gecko_ffi_name} {
                return false;
            }
        }
        true
    }
</%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 display_equals(&self, other: &Self) -> bool {
        self.mDisplay == other.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 contain_equals(&self, other: &Self) -> bool {
        self.mContain == other.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()}
            };
        }
    }
    ${impl_fallback_eq(f"{shorthand}_{name}")}
</%def>

<%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)">
    pub fn copy_${shorthand}_${name}_from(&mut self, other: &yle='color:red'>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: &'color:red'>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,
    {
        % if keyword:
        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
        % endif
        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} = {
                % if keyword:
                match servo {
                    % for value in keyword.values_for("gecko"):
                    Keyword::${to_camel_case(value)} =>
                        structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
                    % endfor
                }
                % else:
                // The Gecko field stores the computed value directly.
                servo
                % endif
            };
        }
    }

    ${impl_fallback_eq(ident)}

    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
        % if keyword:
        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
        % 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| {
                    % if keyword:
                    match layer.${field_name} {
                        % for value in longhand.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
                    }
                    % else:
                    layer.${field_name}
                    % 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()
        )
    }

    ${impl_fallback_eq(f"{shorthand}_position_{orientation}")}

    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: &e='color:red'>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;
        }
    }

    ${impl_fallback_eq(f"{shorthand}_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">
</%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
                          animation-range-start animation-range-end
                          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
            && self.mAnimationRangeStartCount == other.mAnimationRangeStartCount
            && self.mAnimationRangeEndCount == other.mAnimationRangeEndCount
            && 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('animation''range_start''RangeStart')}
    ${impl_coordinated_property('animation''range_end''RangeEnd')}

    ${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
    }
}

if engine == "gecko":
pub mod system_font {
    //! We deal with system fonts here
    //!
    //! System fonts can only be set as a group via the font shorthand.
    //! They resolve at compute time (not parse time -- this lets the
    //! browser respond to changes to the OS font settings).
    //!
    //! While Gecko handles these as a separate property and keyword
    //! values on each property indicating that the font should be picked
    //! from the -x-system-font property, we avoid this. Instead,
    //! each font longhand has a special SystemFont variant which contains
    //! the specified system font. When the cascade function (in helpers)
    //! detects that a value has a system font, it will resolve it, and
    //! cache it on the ComputedValues. After this, it can be just fetched
    //! whenever a font longhand on the same element needs the system font.
    //!
    //! When a longhand property is holding a SystemFont, it's serialized
    //! to an empty string as if its value comes from a shorthand with
    //! variable reference. We may want to improve this behavior at some
    //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.

    use crate::properties::longhands;
    use std::hash::{Hash, Hasher};
    use crate::values::computed::{ToComputedValue, Context};
    use crate::values::specified::font::SystemFont;
    // ComputedValues are compared at times
    // so we need these impls. We don't want to
    // add Eq to Number (which contains a float)
    // so instead we have an eq impl which skips the
    // cached values
    impl PartialEq for ComputedSystemFont {
        fn eq(&self, other: &Self) -> bool {
            self.system_font == other.system_font
        }
    }
    impl Eq for ComputedSystemFont {}

    impl Hash for ComputedSystemFont {
        fn hash<H: Hasher>(&self, hasher: &mut H) {
            self.system_font.hash(hasher)
        }
    }

    impl ToComputedValue for SystemFont {
        type ComputedValue = ComputedSystemFont;

        fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
            use crate::gecko_bindings::bindings;
            use crate::gecko_bindings::structs::nsFont;
            use crate::values::computed::font::FontSize;
            use crate::values::specified::font::KeywordInfo;
            use crate::values::generics::NonNegative;
            use std::mem;

            let mut system = mem::MaybeUninit::<nsFont>::uninit();
            let system = unsafe {
                bindings::Gecko_nsFont_InitSystem(
                    system.as_mut_ptr(),
                    *self,
                    &**cx.style().get_font(),
                    cx.device().document()
                );
                &mut *system.as_mut_ptr()
            };
            let size = NonNegative(cx.maybe_zoom_text(system.size.0));
            let ret = ComputedSystemFont {
                font_family: system.family.clone(),
                font_size: FontSize {
                    computed_size: size,
                    used_size: size,
                    keyword_info: KeywordInfo::none()
                },
                font_weight: system.weight,
                font_stretch: system.stretch,
                font_style: system.style,
                system_font: *self,
            };
            unsafe { bindings::Gecko_nsFont_Destroy(system); }
            ret
        }

        fn from_computed_value(_: &ComputedSystemFont) -> Self {
            unreachable!()
        }
    }

    #[inline]
    /// Compute and cache a system font
    ///
    /// Must be called before attempting to compute a system font
    /// specified value
    pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
        // Checking if context.cached_system_font.is_none() isn't enough,
        // if animating from one system font to another the cached system font
        // may change
        if context.cached_system_font.as_ref().is_none_or(|x| x.system_font != system) {
            let computed = system.to_computed_value(context);
            context.cached_system_font = Some(computed);
        }
    }

    #[derive(Clone, Debug)]
    pub struct ComputedSystemFont {
        % for name in SYSTEM_FONT_LONGHANDS:
            pub ${name}: longhands::${name}::computed_value::T,
        % endfor
        pub system_font: SystemFont,
    }
}
% endif

Messung V0.5 in Prozent
C=94 H=95 G=94

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.36Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-26) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002