tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gecko.mako.rs (51334B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 // `data` comes from components/style/properties.mako.rs; see build.rs for more details.
      6 
      7 <%!
      8    from data import to_camel_case, to_camel_case_lower
      9    from data import Keyword
     10 %>
     11 <%namespace name="helpers" file="/helpers.mako.rs" />
     12 
     13 use crate::Atom;
     14 use crate::logical_geometry::PhysicalSide;
     15 use crate::computed_value_flags::*;
     16 use crate::custom_properties::ComputedCustomProperties;
     17 use crate::gecko_bindings::bindings;
     18 % for style_struct in data.style_structs:
     19 use crate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};
     20 use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
     21 use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
     22 % endfor
     23 use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
     24 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
     25 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
     26 use crate::gecko_bindings::structs;
     27 use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
     28 use crate::gecko::data::PerDocumentStyleData;
     29 use crate::logical_geometry::WritingMode;
     30 use crate::media_queries::Device;
     31 use crate::properties::longhands;
     32 use crate::rule_tree::StrongRuleNode;
     33 use crate::selector_parser::PseudoElement;
     34 use servo_arc::{Arc, UniqueArc};
     35 use std::mem::{forget, MaybeUninit, ManuallyDrop};
     36 use std::{ops, ptr};
     37 use crate::values;
     38 use crate::values::computed::{Time, Zoom};
     39 use crate::values::computed::font::FontSize;
     40 
     41 
     42 pub mod style_structs {
     43    % for style_struct in data.style_structs:
     44    pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     45 
     46    unsafe impl Send for ${style_struct.name} {}
     47    unsafe impl Sync for ${style_struct.name} {}
     48    % endfor
     49 }
     50 
     51 /// FIXME(emilio): This is completely duplicated with the other properties code.
     52 pub type ComputedValuesInner = structs::ServoComputedData;
     53 
     54 #[repr(C)]
     55 pub struct ComputedValues(structs::mozilla::ComputedStyle);
     56 
     57 impl ComputedValues {
     58    #[inline]
     59    pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle {
     60        &self.0
     61    }
     62 
     63    pub fn new(
     64        pseudo: Option<&PseudoElement>,
     65        custom_properties: ComputedCustomProperties,
     66        writing_mode: WritingMode,
     67        effective_zoom: Zoom,
     68        flags: ComputedValueFlags,
     69        rules: Option<StrongRuleNode>,
     70        visited_style: Option<Arc<ComputedValues>>,
     71        % for style_struct in data.style_structs:
     72        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
     73        % endfor
     74    ) -> Arc<Self> {
     75        ComputedValuesInner::new(
     76            custom_properties,
     77            writing_mode,
     78            effective_zoom,
     79            flags,
     80            rules,
     81            visited_style,
     82            % for style_struct in data.style_structs:
     83            ${style_struct.ident},
     84            % endfor
     85        ).to_outer(pseudo)
     86    }
     87 
     88    pub fn default_values(doc: &structs::Document) -> Arc<Self> {
     89        ComputedValuesInner::new(
     90            ComputedCustomProperties::default(),
     91            WritingMode::empty(), // FIXME(bz): This seems dubious
     92            Zoom::ONE,
     93            ComputedValueFlags::empty(),
     94            /* rules = */ None,
     95            /* visited_style = */ None,
     96            % for style_struct in data.style_structs:
     97            style_structs::${style_struct.name}::default(doc),
     98            % endfor
     99        ).to_outer(None)
    100    }
    101 
    102    /// Converts the computed values to an Arc<> from a reference.
    103    pub fn to_arc(&self) -> Arc<Self> {
    104        // SAFETY: We're guaranteed to be allocated as an Arc<> since the
    105        // functions above are the only ones that create ComputedValues
    106        // instances in Gecko (and that must be the case since ComputedValues'
    107        // member is private).
    108        unsafe { Arc::from_raw_addrefed(self) }
    109    }
    110 
    111    #[inline]
    112    pub fn is_pseudo_style(&self) -> bool {
    113        self.0.mPseudoType != PseudoStyleType::NotPseudo
    114    }
    115 
    116    #[inline]
    117    pub fn pseudo(&self) -> Option<PseudoElement> {
    118        if !self.is_pseudo_style() {
    119            return None;
    120        }
    121        PseudoElement::from_pseudo_type(self.0.mPseudoType, None)
    122    }
    123 
    124    #[inline]
    125    pub fn is_first_line_style(&self) -> bool {
    126        self.pseudo() == Some(PseudoElement::FirstLine)
    127    }
    128 
    129    /// Returns true if the display property is changed from 'none' to others.
    130    pub fn is_display_property_changed_from_none(
    131        &self,
    132        old_values: Option<&ComputedValues>
    133    ) -> bool {
    134        use crate::properties::longhands::display::computed_value::T as Display;
    135 
    136        old_values.map_or(false, |old| {
    137            let old_display_style = old.get_box().clone_display();
    138            let new_display_style = self.get_box().clone_display();
    139            old_display_style == Display::None &&
    140            new_display_style != Display::None
    141        })
    142    }
    143 
    144 }
    145 
    146 impl Drop for ComputedValues {
    147    fn drop(&mut self) {
    148        // XXX this still relies on the destructor of ComputedValuesInner to run on the rust side,
    149        // that's pretty wild.
    150        unsafe {
    151            bindings::Gecko_ComputedStyle_Destroy(&mut self.0);
    152        }
    153    }
    154 }
    155 
    156 unsafe impl Sync for ComputedValues {}
    157 unsafe impl Send for ComputedValues {}
    158 
    159 impl Drop for ComputedValuesInner {
    160    fn drop(&mut self) {
    161        % for style_struct in data.style_structs:
    162        let _ = unsafe { Arc::from_raw(self.${style_struct.name_lower}_ptr()) };
    163        % endfor
    164        if !self.visited_style.is_null() {
    165            let _ = unsafe { Arc::from_raw(self.visited_style_ptr()) };
    166        }
    167    }
    168 }
    169 
    170 impl ComputedValuesInner {
    171    pub fn new(
    172        custom_properties: ComputedCustomProperties,
    173        writing_mode: WritingMode,
    174        effective_zoom: Zoom,
    175        flags: ComputedValueFlags,
    176        rules: Option<StrongRuleNode>,
    177        visited_style: Option<Arc<ComputedValues>>,
    178        % for style_struct in data.style_structs:
    179        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
    180        % endfor
    181    ) -> Self {
    182        Self {
    183            custom_properties,
    184            writing_mode,
    185            rules,
    186            visited_style: visited_style.map_or(ptr::null(), |p| Arc::into_raw(p)) as *const _,
    187            flags,
    188            effective_zoom,
    189            % for style_struct in data.style_structs:
    190            ${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
    191            % endfor
    192        }
    193    }
    194 
    195    fn to_outer(self, pseudo: Option<&PseudoElement>) -> Arc<ComputedValues> {
    196        let pseudo_ty = match pseudo {
    197            Some(p) => p.pseudo_type_and_argument().0,
    198            None => structs::PseudoStyleType::NotPseudo,
    199        };
    200        unsafe {
    201            let mut arc = UniqueArc::<ComputedValues>::new_uninit();
    202            bindings::Gecko_ComputedStyle_Init(
    203                arc.as_mut_ptr() as *mut _,
    204                &self,
    205                pseudo_ty,
    206            );
    207            // We're simulating move semantics by having C++ do a memcpy and
    208            // then forgetting it on this end.
    209            forget(self);
    210            UniqueArc::assume_init(arc).shareable()
    211        }
    212    }
    213 }
    214 
    215 impl ops::Deref for ComputedValues {
    216    type Target = ComputedValuesInner;
    217    #[inline]
    218    fn deref(&self) -> &ComputedValuesInner {
    219        &self.0.mSource
    220    }
    221 }
    222 
    223 impl ops::DerefMut for ComputedValues {
    224    #[inline]
    225    fn deref_mut(&mut self) -> &mut ComputedValuesInner {
    226        &mut self.0.mSource
    227    }
    228 }
    229 
    230 impl ComputedValuesInner {
    231    /// Returns true if the value of the `content` property would make a
    232    /// pseudo-element not rendered.
    233    #[inline]
    234    pub fn ineffective_content_property(&self) -> bool {
    235        self.get_counters().ineffective_content_property()
    236    }
    237 
    238    #[inline]
    239    fn visited_style_ptr(&self) -> *const ComputedValues {
    240        self.visited_style as *const _
    241    }
    242 
    243    /// Returns the visited style, if any.
    244    pub fn visited_style(&self) -> Option<&ComputedValues> {
    245        unsafe { self.visited_style_ptr().as_ref() }
    246    }
    247 
    248    % for style_struct in data.style_structs:
    249    #[inline]
    250    fn ${style_struct.name_lower}_ptr(&self) -> *const style_structs::${style_struct.name} {
    251        // This is sound because the wrapper we create is repr(transparent).
    252        self.${style_struct.gecko_name} as *const _
    253    }
    254 
    255    #[inline]
    256    pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
    257        unsafe { &*self.${style_struct.name_lower}_ptr() }
    258    }
    259    % endfor
    260 }
    261 
    262 <%def name="impl_simple_setter(ident, gecko_ffi_name)">
    263    #[allow(non_snake_case)]
    264    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
    265        ${set_gecko_property(gecko_ffi_name, "From::from(v)")}
    266    }
    267 </%def>
    268 
    269 <%def name="impl_simple_clone(ident, gecko_ffi_name)">
    270    #[allow(non_snake_case)]
    271    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
    272        From::from(self.${gecko_ffi_name}.clone())
    273    }
    274 </%def>
    275 
    276 <%def name="impl_physical_sides(ident, props)">
    277    pub fn get_${ident}(&self, s: PhysicalSide) -> &longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T {
    278        match s {
    279            PhysicalSide::Top => &self.${data.longhands_by_name[props[0]].gecko_ffi_name},
    280            PhysicalSide::Right => &self.${data.longhands_by_name[props[1]].gecko_ffi_name},
    281            PhysicalSide::Bottom => &self.${data.longhands_by_name[props[2]].gecko_ffi_name},
    282            PhysicalSide::Left => &self.${data.longhands_by_name[props[3]].gecko_ffi_name},
    283        }
    284    }
    285    pub fn set_${ident}(&mut self, s: PhysicalSide, v: longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T) {
    286        match s {
    287            PhysicalSide::Top => self.set_${data.longhands_by_name[props[0]].ident}(v),
    288            PhysicalSide::Right => self.set_${data.longhands_by_name[props[1]].ident}(v),
    289            PhysicalSide::Bottom => self.set_${data.longhands_by_name[props[2]].ident}(v),
    290            PhysicalSide::Left => self.set_${data.longhands_by_name[props[3]].ident}(v),
    291        }
    292    }
    293 </%def>
    294 
    295 <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
    296    #[allow(non_snake_case)]
    297    pub fn copy_${ident}_from(&mut self, other: &Self) {
    298        self.${gecko_ffi_name} = other.${gecko_ffi_name}.clone();
    299    }
    300 
    301    #[allow(non_snake_case)]
    302    pub fn reset_${ident}(&mut self, other: &Self) {
    303        self.copy_${ident}_from(other)
    304    }
    305 </%def>
    306 
    307 <%!
    308 def get_gecko_property(ffi_name, self_param = "self"):
    309    return "%s.%s" % (self_param, ffi_name)
    310 
    311 def set_gecko_property(ffi_name, expr):
    312    return "self.%s = %s;" % (ffi_name, expr)
    313 %>
    314 
    315 <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')">
    316    #[allow(non_snake_case)]
    317    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
    318        use crate::properties::longhands::${ident}::computed_value::T as Keyword;
    319        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
    320        let result = match v {
    321            % for value in keyword.values_for('gecko'):
    322                Keyword::${to_camel_case(value)} =>
    323                    structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
    324            % endfor
    325        };
    326        ${set_gecko_property(gecko_ffi_name, "result")}
    327    }
    328 </%def>
    329 
    330 <%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')">
    331    #[allow(non_snake_case)]
    332    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
    333        use crate::properties::longhands::${ident}::computed_value::T as Keyword;
    334        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
    335 
    336        // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).
    337        // And they are convert to signed integer in Rust bindings. We need to cast then
    338        // as signed type when we have both signed/unsigned integer in order to use them
    339        // as match's arms.
    340        // Also, to use same implementation here we use casted constant if we have only singed values.
    341        % if keyword.gecko_enum_prefix is None:
    342        % for value in keyword.values_for('gecko'):
    343        const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =
    344            structs::${keyword.gecko_constant(value)} as ${cast_type};
    345        % endfor
    346 
    347        match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
    348            % for value in keyword.values_for('gecko'):
    349            ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},
    350            % endfor
    351            % if keyword.gecko_inexhaustive:
    352            _ => panic!("Found unexpected value in style struct for ${ident} property"),
    353            % endif
    354        }
    355        % else:
    356        match ${get_gecko_property(gecko_ffi_name)} {
    357            % for value in keyword.values_for('gecko'):
    358            structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
    359            % endfor
    360            % if keyword.gecko_inexhaustive:
    361            _ => panic!("Found unexpected value in style struct for ${ident} property"),
    362            % endif
    363        }
    364        % endif
    365    }
    366 </%def>
    367 
    368 <%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)">
    369 <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
    370 <%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"></%call>
    371 <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
    372 </%def>
    373 
    374 <%def name="impl_simple(ident, gecko_ffi_name)">
    375 <%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
    376 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
    377 <%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
    378 </%def>
    379 
    380 <%def name="impl_border_width(ident, gecko_ffi_name, inherit_from)">
    381    #[allow(non_snake_case)]
    382    pub fn set_${ident}(&mut self, v: Au) {
    383        let value = v.0;
    384        self.${inherit_from} = value;
    385        self.${gecko_ffi_name} = value;
    386    }
    387 
    388    #[allow(non_snake_case)]
    389    pub fn copy_${ident}_from(&mut self, other: &Self) {
    390        self.${inherit_from} = other.${inherit_from};
    391        // NOTE: This is needed to easily handle the `unset` and `initial`
    392        // keywords, which are implemented calling this function.
    393        //
    394        // In practice, this means that we may have an incorrect value here, but
    395        // we'll adjust that properly in the style fixup phase.
    396        //
    397        // FIXME(emilio): We could clean this up a bit special-casing the reset_
    398        // function below.
    399        self.${gecko_ffi_name} = other.${inherit_from};
    400    }
    401 
    402    #[allow(non_snake_case)]
    403    pub fn reset_${ident}(&mut self, other: &Self) {
    404        self.copy_${ident}_from(other)
    405    }
    406 
    407    #[allow(non_snake_case)]
    408    pub fn clone_${ident}(&self) -> Au {
    409        Au(self.${gecko_ffi_name})
    410    }
    411 </%def>
    412 
    413 <%def name="impl_style_struct(style_struct)">
    414 /// A wrapper for ${style_struct.gecko_ffi_name}, to be able to manually construct / destruct /
    415 /// clone it.
    416 #[repr(transparent)]
    417 pub struct ${style_struct.gecko_struct_name}(ManuallyDrop<structs::${style_struct.gecko_ffi_name}>);
    418 
    419 impl ops::Deref for ${style_struct.gecko_struct_name} {
    420    type Target = structs::${style_struct.gecko_ffi_name};
    421    #[inline]
    422    fn deref(&self) -> &Self::Target {
    423        &self.0
    424    }
    425 }
    426 
    427 impl ops::DerefMut for ${style_struct.gecko_struct_name} {
    428    #[inline]
    429    fn deref_mut(&mut self) -> &mut <Self as ops::Deref>::Target {
    430        &mut self.0
    431    }
    432 }
    433 
    434 impl ${style_struct.gecko_struct_name} {
    435    #[allow(dead_code, unused_variables)]
    436    pub fn default(document: &structs::Document) -> Arc<Self> {
    437 % if style_struct.document_dependent:
    438        unsafe {
    439            let mut result = UniqueArc::<Self>::new_uninit();
    440            Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
    441                result.as_mut_ptr() as *mut _,
    442                document,
    443            );
    444            UniqueArc::assume_init(result).shareable()
    445        }
    446 % else:
    447        static DEFAULT: std::sync::LazyLock<Arc<${style_struct.gecko_struct_name}>> = std::sync::LazyLock::new(|| unsafe {
    448            let mut result = UniqueArc::<${style_struct.gecko_struct_name}>::new_uninit();
    449            Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
    450                result.as_mut_ptr() as *mut _,
    451                std::ptr::null(),
    452            );
    453            let arc = UniqueArc::assume_init(result).shareable();
    454            arc.mark_as_intentionally_leaked();
    455            arc
    456        });
    457        DEFAULT.clone()
    458 % endif
    459    }
    460 }
    461 
    462 impl Drop for ${style_struct.gecko_struct_name} {
    463    fn drop(&mut self) {
    464        unsafe {
    465            Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut **self);
    466        }
    467    }
    468 }
    469 impl Clone for ${style_struct.gecko_struct_name} {
    470    fn clone(&self) -> Self {
    471        unsafe {
    472            let mut result = MaybeUninit::<Self>::uninit();
    473            // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
    474            // these looks like Valgrind false-positives at a quick glance.
    475            ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
    476            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &**self);
    477            result.assume_init()
    478        }
    479    }
    480 }
    481 </%def>
    482 
    483 <%def name="impl_font_settings(ident, gecko_type, tag_type, value_type, gecko_value_type)">
    484    <% gecko_ffi_name = to_camel_case_lower(ident) %>
    485 
    486    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
    487        let iter = v.0.iter().map(|other| structs::${gecko_type} {
    488            mTag: other.tag.0,
    489            mValue: other.value as ${gecko_value_type},
    490        });
    491        self.mFont.${gecko_ffi_name}.clear();
    492        self.mFont.${gecko_ffi_name}.extend(iter);
    493    }
    494 
    495    <% impl_simple_copy(ident, "mFont." + gecko_ffi_name) %>
    496 
    497    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
    498        use crate::values::generics::font::{FontSettings, FontTag, ${tag_type}};
    499 
    500        FontSettings(
    501            self.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| {
    502                ${tag_type} {
    503                    tag: FontTag(gecko_font_setting.mTag),
    504                    value: gecko_font_setting.mValue as ${value_type},
    505                }
    506            }).collect()
    507        )
    508    }
    509 </%def>
    510 
    511 <%def name="impl_trait(style_struct_name, skip_longhands='')">
    512 <%
    513    style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
    514    longhands = [x for x in style_struct.longhands
    515                if not (skip_longhands == "*" or x.name in skip_longhands.split())]
    516 
    517    def longhand_method(longhand):
    518        args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
    519 
    520        if longhand.logical:
    521            return
    522        # get the method and pass additional keyword or type-specific arguments
    523        if longhand.keyword:
    524            method = impl_keyword
    525            args.update(keyword=longhand.keyword)
    526            if "font" in longhand.ident:
    527                args.update(cast_type=longhand.cast_type)
    528        else:
    529            method = impl_simple
    530 
    531        method(**args)
    532 %>
    533 impl ${style_struct.gecko_struct_name} {
    534    /*
    535     * Manually-Implemented Methods.
    536     */
    537    ${caller.body().strip()}
    538 
    539    /*
    540     * Auto-Generated Methods.
    541     */
    542    <%
    543    for longhand in longhands:
    544        longhand_method(longhand)
    545    %>
    546 }
    547 </%def>
    548 
    549 <%!
    550 class Side(object):
    551    def __init__(self, name, index):
    552        self.name = name
    553        self.ident = name.lower()
    554        self.index = index
    555 
    556 SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
    557 %>
    558 
    559 #[allow(dead_code)]
    560 fn static_assert() {
    561    // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
    562    % for side in SIDES:
    563    { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
    564    % endfor
    565 }
    566 
    567 
    568 <%self:impl_trait style_struct_name="Border">
    569 </%self:impl_trait>
    570 
    571 <%self:impl_trait style_struct_name="Margin">
    572    ${impl_physical_sides("margin", ["margin-top", "margin-right", "margin-bottom", "margin-left"])}
    573 </%self:impl_trait>
    574 <%self:impl_trait style_struct_name="Padding"></%self:impl_trait>
    575 <%self:impl_trait style_struct_name="Page"></%self:impl_trait>
    576 
    577 <%self:impl_trait style_struct_name="Position">
    578    ${impl_physical_sides("inset", ["top", "right", "bottom", "left"])}
    579    pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
    580        debug_assert_ne!(v, values::specified::JustifyItems::legacy());
    581        self.mJustifyItems.computed = v;
    582    }
    583 </%self:impl_trait>
    584 
    585 <%self:impl_trait style_struct_name="Outline">
    586 </%self:impl_trait>
    587 
    588 <% skip_font_longhands = """font-size -x-lang font-feature-settings font-variation-settings""" %>
    589 <%self:impl_trait style_struct_name="Font"
    590    skip_longhands="${skip_font_longhands}">
    591 
    592    // Negative numbers are invalid at parse time, but <integer> is still an
    593    // i32.
    594    <% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
    595    <% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
    596 
    597    pub fn unzoom_fonts(&mut self, device: &Device) {
    598        use crate::values::generics::NonNegative;
    599        self.mSize = NonNegative(device.unzoom_text(self.mSize.0));
    600        self.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.mScriptUnconstrainedSize.0));
    601        self.mFont.size = NonNegative(device.unzoom_text(self.mFont.size.0));
    602    }
    603 
    604    pub fn copy_font_size_from(&mut self, other: &Self) {
    605        self.mScriptUnconstrainedSize = other.mScriptUnconstrainedSize;
    606 
    607        self.mSize = other.mScriptUnconstrainedSize;
    608        // NOTE: Intentionally not copying from mFont.size. The cascade process
    609        // recomputes the used size as needed.
    610        self.mFont.size = other.mSize;
    611        self.mFontSizeKeyword = other.mFontSizeKeyword;
    612 
    613        // TODO(emilio): Should we really copy over these two?
    614        self.mFontSizeFactor = other.mFontSizeFactor;
    615        self.mFontSizeOffset = other.mFontSizeOffset;
    616    }
    617 
    618    pub fn reset_font_size(&mut self, other: &Self) {
    619        self.copy_font_size_from(other)
    620    }
    621 
    622    pub fn set_font_size(&mut self, v: FontSize) {
    623        let computed_size = v.computed_size;
    624        self.mScriptUnconstrainedSize = computed_size;
    625 
    626        // These two may be changed from Cascade::fixup_font_stuff.
    627        self.mSize = computed_size;
    628        // NOTE: Intentionally not copying from used_size. The cascade process
    629        // recomputes the used size as needed.
    630        self.mFont.size = computed_size;
    631 
    632        self.mFontSizeKeyword = v.keyword_info.kw;
    633        self.mFontSizeFactor = v.keyword_info.factor;
    634        self.mFontSizeOffset = v.keyword_info.offset;
    635    }
    636 
    637    pub fn clone_font_size(&self) -> FontSize {
    638        use crate::values::specified::font::KeywordInfo;
    639 
    640        FontSize {
    641            computed_size: self.mSize,
    642            used_size: self.mFont.size,
    643            keyword_info: KeywordInfo {
    644                kw: self.mFontSizeKeyword,
    645                factor: self.mFontSizeFactor,
    646                offset: self.mFontSizeOffset,
    647            }
    648        }
    649    }
    650 
    651    #[allow(non_snake_case)]
    652    pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
    653        let ptr = v.0.as_ptr();
    654        forget(v);
    655        unsafe {
    656            Gecko_nsStyleFont_SetLang(&mut **self, ptr);
    657        }
    658    }
    659 
    660    #[allow(non_snake_case)]
    661    pub fn copy__x_lang_from(&mut self, other: &Self) {
    662        unsafe {
    663            Gecko_nsStyleFont_CopyLangFrom(&mut **self, &**other);
    664        }
    665    }
    666 
    667    #[allow(non_snake_case)]
    668    pub fn reset__x_lang(&mut self, other: &Self) {
    669        self.copy__x_lang_from(other)
    670    }
    671 
    672    #[allow(non_snake_case)]
    673    pub fn clone__x_lang(&self) -> longhands::_x_lang::computed_value::T {
    674        longhands::_x_lang::computed_value::T(unsafe {
    675            Atom::from_raw(self.mLanguage.mRawPtr)
    676        })
    677    }
    678 </%self:impl_trait>
    679 
    680 <%def name="impl_coordinated_property_copy(type, ident, gecko_ffi_name)">
    681    #[allow(non_snake_case)]
    682    pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
    683        self.m${to_camel_case(type)}s.ensure_len(other.m${to_camel_case(type)}s.len());
    684 
    685        let count = other.m${to_camel_case(type)}${gecko_ffi_name}Count;
    686        self.m${to_camel_case(type)}${gecko_ffi_name}Count = count;
    687 
    688        let iter = self.m${to_camel_case(type)}s.iter_mut().take(count as usize).zip(
    689            other.m${to_camel_case(type)}s.iter()
    690        );
    691 
    692        for (ours, others) in iter {
    693            ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}.clone();
    694        }
    695    }
    696    #[allow(non_snake_case)]
    697    pub fn reset_${type}_${ident}(&mut self, other: &Self) {
    698        self.copy_${type}_${ident}_from(other)
    699    }
    700 </%def>
    701 
    702 <%def name="impl_coordinated_property_count(type, ident, gecko_ffi_name)">
    703    #[allow(non_snake_case)]
    704    pub fn ${type}_${ident}_count(&self) -> usize {
    705        self.m${to_camel_case(type)}${gecko_ffi_name}Count as usize
    706    }
    707 </%def>
    708 
    709 <%def name="impl_coordinated_property(type, ident, gecko_ffi_name)">
    710    #[allow(non_snake_case)]
    711    pub fn set_${type}_${ident}<I>(&mut self, v: I)
    712    where
    713        I: IntoIterator<Item = longhands::${type}_${ident}::computed_value::single_value::T>,
    714        I::IntoIter: ExactSizeIterator + Clone
    715    {
    716        let v = v.into_iter();
    717        debug_assert_ne!(v.len(), 0);
    718        let input_len = v.len();
    719        self.m${to_camel_case(type)}s.ensure_len(input_len);
    720 
    721        self.m${to_camel_case(type)}${gecko_ffi_name}Count = input_len as u32;
    722        for (gecko, servo) in self.m${to_camel_case(type)}s.iter_mut().take(input_len as usize).zip(v) {
    723            gecko.m${gecko_ffi_name} = servo;
    724        }
    725    }
    726    #[allow(non_snake_case)]
    727    pub fn ${type}_${ident}_at(&self, index: usize)
    728        -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
    729        self.m${to_camel_case(type)}s[index % self.${type}_${ident}_count()].m${gecko_ffi_name}.clone()
    730    }
    731    ${impl_coordinated_property_copy(type, ident, gecko_ffi_name)}
    732    ${impl_coordinated_property_count(type, ident, gecko_ffi_name)}
    733 </%def>
    734 
    735 <% skip_box_longhands= """display contain""" %>
    736 <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
    737    #[inline]
    738    pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
    739        self.mDisplay = v;
    740        self.mOriginalDisplay = v;
    741    }
    742 
    743    #[inline]
    744    pub fn copy_display_from(&mut self, other: &Self) {
    745        self.set_display(other.mDisplay);
    746    }
    747 
    748    #[inline]
    749    pub fn reset_display(&mut self, other: &Self) {
    750        self.copy_display_from(other)
    751    }
    752 
    753    #[inline]
    754    pub fn set_adjusted_display(
    755        &mut self,
    756        v: longhands::display::computed_value::T,
    757        _is_item_or_root: bool
    758    ) {
    759        self.mDisplay = v;
    760    }
    761 
    762    #[inline]
    763    pub fn clone_display(&self) -> longhands::display::computed_value::T {
    764        self.mDisplay
    765    }
    766 
    767    #[inline]
    768    pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {
    769        self.mContain = v;
    770        self.mEffectiveContainment = v;
    771    }
    772 
    773    #[inline]
    774    pub fn copy_contain_from(&mut self, other: &Self) {
    775        self.set_contain(other.mContain);
    776    }
    777 
    778    #[inline]
    779    pub fn reset_contain(&mut self, other: &Self) {
    780        self.copy_contain_from(other)
    781    }
    782 
    783    #[inline]
    784    pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
    785        self.mContain
    786    }
    787 
    788    #[inline]
    789    pub fn set_effective_containment(
    790        &mut self,
    791        v: longhands::contain::computed_value::T
    792    ) {
    793        self.mEffectiveContainment = v;
    794    }
    795 
    796    #[inline]
    797    pub fn clone_effective_containment(&self) -> longhands::contain::computed_value::T {
    798        self.mEffectiveContainment
    799    }
    800 </%self:impl_trait>
    801 
    802 <%def name="simple_image_array_property(name, shorthand, field_name)">
    803    <%
    804        image_layers_field = "mImage" if shorthand == "background" else "mMask"
    805        copy_simple_image_array_property(name, shorthand, image_layers_field, field_name)
    806    %>
    807 
    808    pub fn set_${shorthand}_${name}<I>(&mut self, v: I)
    809        where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,
    810              I::IntoIter: ExactSizeIterator
    811    {
    812        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
    813        let v = v.into_iter();
    814 
    815        unsafe {
    816          Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
    817                                        LayerType::${shorthand.title()});
    818        }
    819 
    820        self.${image_layers_field}.${field_name}Count = v.len() as u32;
    821        for (servo, geckolayer) in v.zip(self.${image_layers_field}.mLayers.iter_mut()) {
    822            geckolayer.${field_name} = {
    823                ${caller.body()}
    824            };
    825        }
    826    }
    827 </%def>
    828 
    829 <%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)">
    830    pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
    831        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
    832 
    833        let count = other.${layers_field_name}.${field_name}Count;
    834        unsafe {
    835            Gecko_EnsureImageLayersLength(&mut self.${layers_field_name},
    836                                          count as usize,
    837                                          LayerType::${shorthand.title()});
    838        }
    839        // FIXME(emilio): This may be bogus in the same way as bug 1426246.
    840        for (layer, other) in self.${layers_field_name}.mLayers.iter_mut()
    841                                  .zip(other.${layers_field_name}.mLayers.iter())
    842                                  .take(count as usize) {
    843            layer.${field_name} = other.${field_name}.clone();
    844        }
    845        self.${layers_field_name}.${field_name}Count = count;
    846    }
    847 
    848    pub fn reset_${shorthand}_${name}(&mut self, other: &Self) {
    849        self.copy_${shorthand}_${name}_from(other)
    850    }
    851 </%def>
    852 
    853 <%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)">
    854    <%
    855        ident = "%s_%s" % (shorthand, name)
    856        style_struct = next(x for x in data.style_structs if x.name == struct_name)
    857        longhand = next(x for x in style_struct.longhands if x.ident == ident)
    858        keyword = longhand.keyword
    859    %>
    860 
    861    <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %>
    862 
    863    pub fn set_${ident}<I>(&mut self, v: I)
    864    where
    865        I: IntoIterator<Item=longhands::${ident}::computed_value::single_value::T>,
    866        I::IntoIter: ExactSizeIterator,
    867    {
    868        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
    869        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
    870 
    871        let v = v.into_iter();
    872 
    873        unsafe {
    874          Gecko_EnsureImageLayersLength(&mut self.${layer_field_name}, v.len(),
    875                                        LayerType::${shorthand.title()});
    876        }
    877 
    878        self.${layer_field_name}.${field_name}Count = v.len() as u32;
    879        for (servo, geckolayer) in v.zip(self.${layer_field_name}.mLayers.iter_mut()) {
    880            geckolayer.${field_name} = {
    881                match servo {
    882                    % for value in keyword.values_for("gecko"):
    883                    Keyword::${to_camel_case(value)} =>
    884                        structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
    885                    % endfor
    886                }
    887            };
    888        }
    889    }
    890 
    891    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
    892        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
    893 
    894        % if keyword.needs_cast():
    895        % for value in keyword.values_for('gecko'):
    896        const ${keyword.casted_constant_name(value, "u8")} : u8 =
    897            structs::${keyword.gecko_constant(value)} as u8;
    898        % endfor
    899        % endif
    900 
    901        longhands::${ident}::computed_value::List(
    902            self.${layer_field_name}.mLayers.iter()
    903                .take(self.${layer_field_name}.${field_name}Count as usize)
    904                .map(|ref layer| {
    905                    match layer.${field_name} {
    906                        % for value in longhand.keyword.values_for("gecko"):
    907                        % if keyword.needs_cast():
    908                        ${keyword.casted_constant_name(value, "u8")}
    909                        % else:
    910                        structs::${keyword.gecko_constant(value)}
    911                        % endif
    912                            => Keyword::${to_camel_case(value)},
    913                        % endfor
    914                        % if keyword.gecko_inexhaustive:
    915                        _ => panic!("Found unexpected value in style struct for ${ident} property"),
    916                        % endif
    917                    }
    918                }).collect()
    919        )
    920    }
    921 </%def>
    922 
    923 <%def name="impl_common_image_layer_properties(shorthand)">
    924    <%
    925        if shorthand == "background":
    926            image_layers_field = "mImage"
    927            struct_name = "Background"
    928        else:
    929            image_layers_field = "mMask"
    930            struct_name = "SVG"
    931    %>
    932 
    933    <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
    934        use crate::values::specified::background::BackgroundRepeatKeyword;
    935        use crate::gecko_bindings::structs::nsStyleImageLayers_Repeat;
    936        use crate::gecko_bindings::structs::StyleImageLayerRepeat;
    937 
    938        fn to_ns(repeat: BackgroundRepeatKeyword) -> StyleImageLayerRepeat {
    939            match repeat {
    940                BackgroundRepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,
    941                BackgroundRepeatKeyword::Space => StyleImageLayerRepeat::Space,
    942                BackgroundRepeatKeyword::Round => StyleImageLayerRepeat::Round,
    943                BackgroundRepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat,
    944            }
    945        }
    946 
    947        let repeat_x = to_ns(servo.0);
    948        let repeat_y = to_ns(servo.1);
    949        nsStyleImageLayers_Repeat {
    950              mXRepeat: repeat_x,
    951              mYRepeat: repeat_y,
    952        }
    953    </%self:simple_image_array_property>
    954 
    955    pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {
    956        use crate::properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
    957        use crate::values::specified::background::BackgroundRepeatKeyword;
    958        use crate::gecko_bindings::structs::StyleImageLayerRepeat;
    959 
    960        fn to_servo(repeat: StyleImageLayerRepeat) -> BackgroundRepeatKeyword {
    961            match repeat {
    962                StyleImageLayerRepeat::Repeat => BackgroundRepeatKeyword::Repeat,
    963                StyleImageLayerRepeat::Space => BackgroundRepeatKeyword::Space,
    964                StyleImageLayerRepeat::Round => BackgroundRepeatKeyword::Round,
    965                StyleImageLayerRepeat::NoRepeat => BackgroundRepeatKeyword::NoRepeat,
    966                _ => panic!("Found unexpected value in style struct for ${shorthand}_repeat property"),
    967            }
    968        }
    969 
    970        longhands::${shorthand}_repeat::computed_value::List(
    971            self.${image_layers_field}.mLayers.iter()
    972                .take(self.${image_layers_field}.mRepeatCount as usize)
    973                .map(|ref layer| {
    974                    T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat))
    975                }).collect()
    976        )
    977    }
    978 
    979    <% impl_simple_image_array_property("clip", shorthand, image_layers_field, "mClip", struct_name) %>
    980    <% impl_simple_image_array_property("origin", shorthand, image_layers_field, "mOrigin", struct_name) %>
    981 
    982    % for (orientation, keyword) in [("x", "horizontal"), ("y", "vertical")]:
    983    pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
    984        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
    985 
    986        let count = other.${image_layers_field}.mPosition${orientation.upper()}Count;
    987 
    988        unsafe {
    989            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
    990                                          count as usize,
    991                                          LayerType::${shorthand.capitalize()});
    992        }
    993 
    994        for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
    995                                  .zip(other.${image_layers_field}.mLayers.iter())
    996                                  .take(count as usize) {
    997            layer.mPosition.${keyword} = other.mPosition.${keyword}.clone();
    998        }
    999        self.${image_layers_field}.mPosition${orientation.upper()}Count = count;
   1000    }
   1001 
   1002    pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) {
   1003        self.copy_${shorthand}_position_${orientation}_from(other)
   1004    }
   1005 
   1006    pub fn clone_${shorthand}_position_${orientation}(&self)
   1007        -> longhands::${shorthand}_position_${orientation}::computed_value::T {
   1008        longhands::${shorthand}_position_${orientation}::computed_value::List(
   1009            self.${image_layers_field}.mLayers.iter()
   1010                .take(self.${image_layers_field}.mPosition${orientation.upper()}Count as usize)
   1011                .map(|position| position.mPosition.${keyword}.clone())
   1012                .collect()
   1013        )
   1014    }
   1015 
   1016    pub fn set_${shorthand}_position_${orientation[0]}<I>(&mut self,
   1017                                     v: I)
   1018        where I: IntoIterator<Item = longhands::${shorthand}_position_${orientation[0]}
   1019                                              ::computed_value::single_value::T>,
   1020              I::IntoIter: ExactSizeIterator
   1021    {
   1022        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
   1023 
   1024        let v = v.into_iter();
   1025 
   1026        unsafe {
   1027            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
   1028                                        LayerType::${shorthand.capitalize()});
   1029        }
   1030 
   1031        self.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;
   1032        for (servo, geckolayer) in v.zip(self.${image_layers_field}
   1033                                                           .mLayers.iter_mut()) {
   1034            geckolayer.mPosition.${keyword} = servo;
   1035        }
   1036    }
   1037    % endfor
   1038 
   1039    <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
   1040        servo
   1041    </%self:simple_image_array_property>
   1042 
   1043    pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {
   1044        longhands::${shorthand}_size::computed_value::List(
   1045            self.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect()
   1046        )
   1047    }
   1048 
   1049    pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
   1050        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
   1051        unsafe {
   1052            let count = other.${image_layers_field}.mImageCount;
   1053            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
   1054                                          count as usize,
   1055                                          LayerType::${shorthand.capitalize()});
   1056 
   1057            for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
   1058                                      .zip(other.${image_layers_field}.mLayers.iter())
   1059                                      .take(count as usize) {
   1060                layer.mImage = other.mImage.clone();
   1061            }
   1062            self.${image_layers_field}.mImageCount = count;
   1063        }
   1064    }
   1065 
   1066    pub fn reset_${shorthand}_image(&mut self, other: &Self) {
   1067        self.copy_${shorthand}_image_from(other)
   1068    }
   1069 
   1070    #[allow(unused_variables)]
   1071    pub fn set_${shorthand}_image<I>(&mut self, images: I)
   1072        where I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,
   1073              I::IntoIter: ExactSizeIterator
   1074    {
   1075        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
   1076 
   1077        let images = images.into_iter();
   1078 
   1079        unsafe {
   1080            Gecko_EnsureImageLayersLength(
   1081                &mut self.${image_layers_field},
   1082                images.len(),
   1083                LayerType::${shorthand.title()},
   1084            );
   1085        }
   1086 
   1087        self.${image_layers_field}.mImageCount = images.len() as u32;
   1088        for (image, geckoimage) in images.zip(self.${image_layers_field}
   1089                                                  .mLayers.iter_mut()) {
   1090            geckoimage.mImage = image;
   1091        }
   1092    }
   1093 
   1094    pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {
   1095        longhands::${shorthand}_image::computed_value::List(
   1096            self.${image_layers_field}.mLayers.iter()
   1097                .take(self.${image_layers_field}.mImageCount as usize)
   1098                .map(|layer| layer.mImage.clone())
   1099                .collect()
   1100        )
   1101    }
   1102 
   1103    <%
   1104        fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize"
   1105        if shorthand == "background":
   1106            fill_fields += " mAttachment mBlendMode"
   1107        else:
   1108            # mSourceURI uses mImageCount
   1109            fill_fields += " mMaskMode mComposite"
   1110    %>
   1111    pub fn fill_arrays(&mut self) {
   1112        use crate::gecko_bindings::bindings::Gecko_FillAllImageLayers;
   1113        use std::cmp;
   1114        let mut max_len = 1;
   1115        % for member in fill_fields.split():
   1116            max_len = cmp::max(max_len, self.${image_layers_field}.${member}Count);
   1117        % endfor
   1118        unsafe {
   1119            // While we could do this manually, we'd need to also manually
   1120            // run all the copy constructors, so we just delegate to gecko
   1121            Gecko_FillAllImageLayers(&mut self.${image_layers_field}, max_len);
   1122        }
   1123    }
   1124 </%def>
   1125 
   1126 // TODO: Gecko accepts lists in most background-related properties. We just use
   1127 // the first element (which is the common case), but at some point we want to
   1128 // add support for parsing these lists in servo and pushing to nsTArray's.
   1129 <% skip_background_longhands = """background-repeat
   1130                                  background-image background-clip
   1131                                  background-origin background-attachment
   1132                                  background-size background-position
   1133                                  background-blend-mode
   1134                                  background-position-x
   1135                                  background-position-y""" %>
   1136 <%self:impl_trait style_struct_name="Background"
   1137                  skip_longhands="${skip_background_longhands}">
   1138 
   1139    <% impl_common_image_layer_properties("background") %>
   1140    <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
   1141    <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
   1142 </%self:impl_trait>
   1143 
   1144 <%self:impl_trait style_struct_name="List">
   1145 </%self:impl_trait>
   1146 
   1147 <%self:impl_trait style_struct_name="Table">
   1148 </%self:impl_trait>
   1149 
   1150 <%self:impl_trait style_struct_name="Effects">
   1151 </%self:impl_trait>
   1152 
   1153 <%self:impl_trait style_struct_name="InheritedBox">
   1154 </%self:impl_trait>
   1155 
   1156 <%self:impl_trait style_struct_name="InheritedTable">
   1157 </%self:impl_trait>
   1158 
   1159 <%self:impl_trait style_struct_name="InheritedText">
   1160 </%self:impl_trait>
   1161 
   1162 <%self:impl_trait style_struct_name="Text">
   1163 </%self:impl_trait>
   1164 
   1165 <% skip_svg_longhands = """
   1166 mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image
   1167 """
   1168 %>
   1169 <%self:impl_trait style_struct_name="SVG"
   1170                  skip_longhands="${skip_svg_longhands}">
   1171    <% impl_common_image_layer_properties("mask") %>
   1172    <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
   1173    <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
   1174 </%self:impl_trait>
   1175 
   1176 <%self:impl_trait style_struct_name="InheritedSVG">
   1177 </%self:impl_trait>
   1178 
   1179 <%self:impl_trait style_struct_name="InheritedUI">
   1180    #[inline]
   1181    pub fn color_scheme_bits(&self) -> values::specified::color::ColorSchemeFlags {
   1182        self.mColorScheme.bits
   1183    }
   1184 </%self:impl_trait>
   1185 
   1186 <%self:impl_trait style_struct_name="Column">
   1187 </%self:impl_trait>
   1188 
   1189 <%self:impl_trait style_struct_name="Counters">
   1190    pub fn ineffective_content_property(&self) -> bool {
   1191        !self.mContent.is_items()
   1192    }
   1193 </%self:impl_trait>
   1194 
   1195 <% skip_ui_longhands = """animation-name animation-delay animation-duration
   1196                          animation-direction animation-fill-mode
   1197                          animation-play-state animation-iteration-count
   1198                          animation-timing-function animation-composition animation-timeline
   1199                          transition-behavior transition-duration transition-delay
   1200                          transition-timing-function transition-property
   1201                          scroll-timeline-name scroll-timeline-axis
   1202                          view-timeline-name view-timeline-axis view-timeline-inset""" %>
   1203 
   1204 <%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}">
   1205    ${impl_coordinated_property('transition', 'behavior', 'Behavior')}
   1206    ${impl_coordinated_property('transition', 'delay', 'Delay')}
   1207    ${impl_coordinated_property('transition', 'duration', 'Duration')}
   1208    ${impl_coordinated_property('transition', 'timing_function', 'TimingFunction')}
   1209    ${impl_coordinated_property('transition', 'property', 'Property')}
   1210 
   1211    pub fn transition_combined_duration_at(&self, index: usize) -> Time {
   1212        // https://drafts.csswg.org/css-transitions/#transition-combined-duration
   1213        Time::from_seconds(
   1214            self.transition_duration_at(index).seconds().max(0.0) +
   1215            self.transition_delay_at(index).seconds()
   1216        )
   1217    }
   1218 
   1219    /// Returns whether there are any transitions specified.
   1220    pub fn specifies_transitions(&self) -> bool {
   1221        if self.mTransitionPropertyCount == 1 &&
   1222            self.transition_combined_duration_at(0).seconds() <= 0.0f32 {
   1223            return false;
   1224        }
   1225        self.mTransitionPropertyCount > 0
   1226    }
   1227 
   1228    /// Returns whether animation-timeline is initial value. We need this information to resolve
   1229    /// animation-duration.
   1230    pub fn has_initial_animation_timeline(&self) -> bool {
   1231        self.mAnimationTimelineCount == 1 && self.animation_timeline_at(0).is_auto()
   1232    }
   1233 
   1234    pub fn animations_equals(&self, other: &Self) -> bool {
   1235        return self.mAnimationNameCount == other.mAnimationNameCount
   1236            && self.mAnimationDelayCount == other.mAnimationDelayCount
   1237            && self.mAnimationDirectionCount == other.mAnimationDirectionCount
   1238            && self.mAnimationDurationCount == other.mAnimationDurationCount
   1239            && self.mAnimationFillModeCount == other.mAnimationFillModeCount
   1240            && self.mAnimationIterationCountCount == other.mAnimationIterationCountCount
   1241            && self.mAnimationPlayStateCount == other.mAnimationPlayStateCount
   1242            && self.mAnimationTimingFunctionCount == other.mAnimationTimingFunctionCount
   1243            && self.mAnimationCompositionCount == other.mAnimationCompositionCount
   1244            && self.mAnimationTimelineCount == other.mAnimationTimelineCount
   1245            && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.mAnimations, &other.mAnimations) }
   1246    }
   1247 
   1248    ${impl_coordinated_property('animation', 'name', 'Name')}
   1249    ${impl_coordinated_property('animation', 'delay', 'Delay')}
   1250    ${impl_coordinated_property('animation', 'duration', 'Duration')}
   1251    ${impl_coordinated_property('animation', 'direction', 'Direction')}
   1252    ${impl_coordinated_property('animation', 'fill_mode', 'FillMode')}
   1253    ${impl_coordinated_property('animation', 'play_state', 'PlayState')}
   1254    ${impl_coordinated_property('animation', 'composition', 'Composition')}
   1255    ${impl_coordinated_property('animation', 'iteration_count', 'IterationCount')}
   1256    ${impl_coordinated_property('animation', 'timeline', 'Timeline')}
   1257    ${impl_coordinated_property('animation', 'timing_function', 'TimingFunction')}
   1258 
   1259    ${impl_coordinated_property('scroll_timeline', 'name', 'Name')}
   1260    ${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}
   1261 
   1262    pub fn scroll_timelines_equals(&self, other: &Self) -> bool {
   1263        self.mScrollTimelineNameCount == other.mScrollTimelineNameCount
   1264            && self.mScrollTimelineAxisCount == other.mScrollTimelineAxisCount
   1265            && unsafe {
   1266                bindings::Gecko_StyleScrollTimelinesEquals(
   1267                    &self.mScrollTimelines,
   1268                    &other.mScrollTimelines,
   1269                )
   1270            }
   1271    }
   1272 
   1273    ${impl_coordinated_property('view_timeline', 'name', 'Name')}
   1274    ${impl_coordinated_property('view_timeline', 'axis', 'Axis')}
   1275    ${impl_coordinated_property('view_timeline', 'inset', 'Inset')}
   1276 
   1277    pub fn view_timelines_equals(&self, other: &Self) -> bool {
   1278        self.mViewTimelineNameCount == other.mViewTimelineNameCount
   1279            && self.mViewTimelineAxisCount == other.mViewTimelineAxisCount
   1280            && self.mViewTimelineInsetCount == other.mViewTimelineInsetCount
   1281            && unsafe {
   1282                bindings::Gecko_StyleViewTimelinesEquals(
   1283                    &self.mViewTimelines,
   1284                    &other.mViewTimelines,
   1285                )
   1286            }
   1287    }
   1288 </%self:impl_trait>
   1289 
   1290 <%self:impl_trait style_struct_name="XUL">
   1291 </%self:impl_trait>
   1292 
   1293 % for style_struct in data.style_structs:
   1294 ${impl_style_struct(style_struct)}
   1295 % endfor
   1296 
   1297 /// Assert that the initial values set in Gecko style struct constructors
   1298 /// match the values returned by `get_initial_value()` for each longhand.
   1299 #[cfg(feature = "gecko")]
   1300 #[inline]
   1301 pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
   1302    if cfg!(debug_assertions) {
   1303        let data = data.borrow();
   1304        let cv = data.stylist.device().default_computed_values();
   1305        <%
   1306            # Skip properties with initial values that change at computed
   1307            # value time, or whose initial value depends on the document
   1308            # / other prefs.
   1309            SKIPPED = [
   1310                "border-top-width",
   1311                "border-bottom-width",
   1312                "border-left-width",
   1313                "border-right-width",
   1314                "column-rule-width",
   1315                "font-family",
   1316                "font-size",
   1317                "outline-width",
   1318                "color",
   1319            ]
   1320            TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
   1321        %>
   1322        % for property in TO_TEST:
   1323        assert_eq!(
   1324            cv.clone_${property.ident}(),
   1325            longhands::${property.ident}::get_initial_value(),
   1326            concat!(
   1327                "initial value in Gecko style struct for ",
   1328                stringify!(${property.ident}),
   1329                " must match longhands::",
   1330                stringify!(${property.ident}),
   1331                "::get_initial_value()"
   1332            )
   1333        );
   1334        % endfor
   1335    }
   1336 }