/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // `data` comes from components/style/properties.mako.rs; see build.rs for more details. <%! from data import to_camel_case, to_camel_case_lower from data import Keyword %> <%namespace name="helpers" file="/helpers.mako.rs" /> use crate::Atom; use app_units::Au; use crate::computed_value_flags::*; use crate::custom_properties::CustomPropertiesMap; use crate::gecko_bindings::bindings; % for style_struct in data.style_structs: use crate::gecko_bindings::structs::${style_struct.gecko_ffi_name}; 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_CopyCounterStyle; use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; use crate::gecko_bindings::structs; use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::gecko_bindings::structs::mozilla::PseudoStyleType; use crate::gecko::data::PerDocumentStyleData; use crate::gecko::values::round_border_to_device_pixels; use crate::logical_geometry::WritingMode; use crate::media_queries::Device; use crate::properties::longhands; use crate::rule_tree::StrongRuleNode; use crate::selector_parser::PseudoElement; use servo_arc::{Arc, RawOffsetArc, UniqueArc}; use std::mem::{forget, MaybeUninit}; use std::{cmp, ops, ptr}; use crate::values::{self, CustomIdent, KeyframesName}; use crate::values::computed::{Percentage, TransitionProperty}; use crate::values::computed::BorderStyle; use crate::values::computed::font::FontSize; use crate::values::generics::column::ColumnCount; pub mod style_structs { % for style_struct in data.style_structs: pub use super::${style_struct.gecko_struct_name} as ${style_struct.name}; unsafe impl Send for ${style_struct.name} {} unsafe impl Sync for ${style_struct.name} {} % endfor } /// FIXME(emilio): This is completely duplicated with the other properties code. pub type ComputedValuesInner = structs::ServoComputedData; #[repr(C)] pub struct ComputedValues(structs::mozilla::ComputedStyle); impl ComputedValues { #[inline] pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle { &self.0 } pub fn new( pseudo: Option<<&PseudoElement>, custom_properties: Option>, writing_mode: WritingMode, flags: ComputedValueFlags, rules: Option, visited_style: Option>, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor ) -> Arc { ComputedValuesInner::new( custom_properties, writing_mode, flags, rules, visited_style, % for style_struct in data.style_structs: ${style_struct.ident}, % endfor ).to_outer(pseudo) } pub fn default_values(doc: &structs::Document) -> Arc { ComputedValuesInner::new( /* custom_properties = */ None, /* writing_mode = */ WritingMode::empty(), // FIXME(bz): This seems dubious ComputedValueFlags::empty(), /* rules = */ None, /* visited_style = */ None, % for style_struct in data.style_structs: style_structs::${style_struct.name}::default(doc), % endfor ).to_outer(None) } /// Converts the computed values to an Arc<> from a reference. pub fn to_arc(&self) -> Arc { // SAFETY: We're guaranteed to be allocated as an Arc<> since the // functions above are the only ones that create ComputedValues // instances in Gecko (and that must be the case since ComputedValues' // member is private). unsafe { Arc::from_raw_addrefed(self) } } #[inline] pub fn is_pseudo_style(&self) -> bool { self.0.mPseudoType != PseudoStyleType::NotPseudo } #[inline] pub fn pseudo(&self) -> Option { if !self.is_pseudo_style() { return None; } PseudoElement::from_pseudo_type(self.0.mPseudoType) } #[inline] pub fn is_first_line_style(&self) -> bool { self.pseudo() == Some(PseudoElement::FirstLine) } /// Returns true if the display property is changed from 'none' to others. pub fn is_display_property_changed_from_none( &self, old_values: Option<<&ComputedValues> ) -> bool { use crate::properties::longhands::display::computed_value::T as Display; old_values.map_or(false, |old| { let old_display_style = old.get_box().clone_display(); let new_display_style = self.get_box().clone_display(); old_display_style == Display::None && new_display_style != Display::None }) } } impl Drop for ComputedValues { fn drop(&mut self) { unsafe { bindings::Gecko_ComputedStyle_Destroy(&mut self.0); } } } unsafe impl Sync for ComputedValues {} unsafe impl Send for ComputedValues {} impl Clone for ComputedValues { fn clone(&self) -> Self { unreachable!() } } impl Clone for ComputedValuesInner { fn clone(&self) -> Self { ComputedValuesInner { % for style_struct in data.style_structs: ${style_struct.gecko_name}: self.${style_struct.gecko_name}.clone(), % endfor custom_properties: self.custom_properties.clone(), writing_mode: self.writing_mode.clone(), flags: self.flags.clone(), rules: self.rules.clone(), visited_style: self.visited_style.clone(), } } } impl ComputedValuesInner { pub fn new( custom_properties: Option>, writing_mode: WritingMode, flags: ComputedValueFlags, rules: Option, visited_style: Option>, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor ) -> Self { Self { custom_properties, writing_mode, rules, visited_style: visited_style.map(Arc::into_raw_offset), flags, % for style_struct in data.style_structs: ${style_struct.gecko_name}: Arc::into_raw_offset(${style_struct.ident}), % endfor } } fn to_outer( self, pseudo: Option<<&PseudoElement>, ) -> Arc { let pseudo_ty = match pseudo { Some(p) => p.pseudo_type(), None => structs::PseudoStyleType::NotPseudo, }; unsafe { let mut arc = UniqueArc::::new_uninit(); bindings::Gecko_ComputedStyle_Init( arc.as_mut_ptr() as *mut _, &self, pseudo_ty, ); // We're simulating move semantics by having C++ do a memcpy and // then forgetting it on this end. forget(self); UniqueArc::assume_init(arc).shareable() } } } impl ops::Deref for ComputedValues { type Target = ComputedValuesInner; fn deref(&self) -> &ComputedValuesInner { &self.0.mSource } } impl ops::DerefMut for ComputedValues { 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() } % for style_struct in data.style_structs: #[inline] pub fn clone_${style_struct.name_lower}(&self) -> Arc { Arc::from_raw_offset(self.${style_struct.gecko_name}.clone()) } #[inline] pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} { &self.${style_struct.gecko_name} } pub fn ${style_struct.name_lower}_arc(&self) -> &RawOffsetArc { &self.${style_struct.gecko_name} } #[inline] pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} { RawOffsetArc::make_mut(&mut self.${style_struct.gecko_name}) } % endfor /// Gets the raw visited style. Useful for memory reporting. pub fn get_raw_visited_style(&self) -> &Option> { &self.visited_style } } <%def name="declare_style_struct(style_struct)"> pub use crate::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name}; impl ${style_struct.gecko_struct_name} { pub fn gecko(&self) -> &${style_struct.gecko_ffi_name} { &self.gecko } pub fn gecko_mut(&mut self) -> &mut ${style_struct.gecko_ffi_name} { &mut self.gecko } } <%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 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.${gecko_ffi_name}.clone()) } <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)"> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}.clone(); } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <%! def get_gecko_property(ffi_name, self_param = "self"): return "%s.gecko.%s" % (self_param, ffi_name) def set_gecko_property(ffi_name, expr): return "self.gecko.%s = %s;" % (ffi_name, expr) %> <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use 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 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 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 expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"> <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"> <%def name="impl_simple(ident, gecko_ffi_name)"> <%call expr="impl_simple_setter(ident, gecko_ffi_name)"> <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> <%call expr="impl_simple_clone(ident, gecko_ffi_name)"> <%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None, round_to_pixels=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { let value = { % if round_to_pixels: let au_per_device_px = Au(self.gecko.mTwipsPerPixel); round_border_to_device_pixels(Au::from(v), au_per_device_px).0 % else: v.0.to_i32_au() % endif }; % if inherit_from: self.gecko.${inherit_from} = value; % endif self.gecko.${gecko_ffi_name} = value; } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { % if inherit_from: self.gecko.${inherit_from} = other.gecko.${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.${gecko_ffi_name} = other.gecko.${inherit_from}; % else: self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; % endif } #[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) -> longhands::${ident}::computed_value::T { Au(self.gecko.${gecko_ffi_name}).into() } <%def name="impl_split_style_coord(ident, gecko_ffi_name, index)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { self.gecko.${gecko_ffi_name}.${index} = v; } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name}.${index} = other.gecko.${gecko_ffi_name}.${index}.clone(); } #[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) -> longhands::${ident}::computed_value::T { self.gecko.${gecko_ffi_name}.${index}.clone() } <%def name="copy_sides_style_coord(ident)"> <% gecko_ffi_name = "m" + to_camel_case(ident) %> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { % for side in SIDES: self.gecko.${gecko_ffi_name}.data_at_mut(${side.index}) .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${side.index})); % endfor ${ caller.body() } } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <%def name="impl_corner_style_coord(ident, gecko_ffi_name, corner)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { self.gecko.${gecko_ffi_name}.${corner} = v; } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name}.${corner} = other.gecko.${gecko_ffi_name}.${corner}.clone(); } #[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) -> longhands::${ident}::computed_value::T { self.gecko.${gecko_ffi_name}.${corner}.clone() } <%def name="impl_logical(name, **kwargs)"> ${helpers.logical_setter(name)} <%def name="impl_style_struct(style_struct)"> impl ${style_struct.gecko_struct_name} { #[allow(dead_code, unused_variables)] pub fn default(document: &structs::Document) -> Arc { unsafe { let mut result = UniqueArc::::new_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::(result.as_mut_ptr(), 0, 1); Gecko_Construct_Default_${style_struct.gecko_ffi_name}( result.as_mut_ptr() as *mut _, document, ); UniqueArc::assume_init(result).shareable() } } } impl Drop for ${style_struct.gecko_struct_name} { fn drop(&mut self) { unsafe { Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut *self.gecko); } } } impl Clone for ${style_struct.gecko_struct_name} { fn clone(&self) -> Self { unsafe { let mut result = MaybeUninit::::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::(result.as_mut_ptr(), 0, 1); Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &*self.gecko); result.assume_init() } } } <%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { self.gecko.${gecko_ffi_name} = From::from(v) } <% impl_simple_copy(ident, gecko_ffi_name) %> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { From::from(self.gecko.${gecko_ffi_name}) } <%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.gecko.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter); } pub fn copy_${ident}_from(&mut self, other: &Self) { let iter = other.gecko.mFont.${gecko_ffi_name}.iter().map(|s| *s); self.gecko.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter); } pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use crate::values::generics::font::{FontSettings, FontTag, ${tag_type}}; FontSettings( self.gecko.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::>().into_boxed_slice() ) } <%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) # get the method and pass additional keyword or type-specific arguments if longhand.logical: method = impl_logical args.update(name=longhand.name) elif 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) %> } <%! 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)] CORNERS = ["top_left", "top_right", "bottom_right", "bottom_left"] %> #[allow(dead_code)] fn static_assert() { // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate. % for side in SIDES: { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; } % endfor } <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y) for x in SIDES for y in ["color", "style", "width"]] + ["border-{0}-radius".format(x.replace("_", "-")) for x in CORNERS]) %> <%self:impl_trait style_struct_name="Border" skip_longhands="${skip_border_longhands} border-image-repeat"> % for side in SIDES: pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) { self.gecko.mBorderStyle[${side.index}] = v; // This is needed because the initial mComputedBorder value is set to // zero. // // In order to compute stuff, we start from the initial struct, and keep // going down the tree applying properties. // // That means, effectively, that when we set border-style to something // non-hidden, we should use the initial border instead. // // Servo stores the initial border-width in the initial struct, and then // adjusts as needed in the fixup phase. This means that the initial // struct is technically not valid without fixups, and that you lose // pretty much any sharing of the initial struct, which is kind of // unfortunate. // // Gecko has two fields for this, one that stores the "specified" // border, and other that stores the actual computed one. That means // that when we set border-style, border-width may change and we need to // sync back to the specified one. This is what this function does. // // Note that this doesn't impose any dependency in the order of // computation of the properties. This is only relevant if border-style // is specified, but border-width isn't. If border-width is specified at // some point, the two mBorder and mComputedBorder fields would be the // same already. // // Once we're here, we know that we'll run style fixups, so it's fine to // just copy the specified border here, we'll adjust it if it's // incorrect later. self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident}; } pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) { self.set_border_${side.ident}_style(other.gecko.mBorderStyle[${side.index}]); } pub fn reset_border_${side.ident}_style(&mut self, other: &Self) { self.copy_border_${side.ident}_style_from(other); } #[inline] pub fn clone_border_${side.ident}_style(&self) -> BorderStyle { self.gecko.mBorderStyle[${side.index}] } <% impl_simple("border_%s_color" % side.ident, "mBorder%sColor" % side.name) %> <% impl_non_negative_length("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, inherit_from="mBorder.%s" % side.ident, round_to_pixels=True) %> pub fn border_${side.ident}_has_nonzero_width(&self) -> bool { self.gecko.mComputedBorder.${side.ident} != 0 } % endfor % for corner in CORNERS: <% impl_corner_style_coord("border_%s_radius" % corner, "mBorderRadius", corner) %> % endfor <% border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"] %> pub fn set_border_image_repeat(&mut self, v: longhands::border_image_repeat::computed_value::T) { use crate::values::specified::border::BorderImageRepeatKeyword; use crate::gecko_bindings::structs::StyleBorderImageRepeat; % for i, side in enumerate(["H", "V"]): self.gecko.mBorderImageRepeat${side} = match v.${i} { % for keyword in border_image_repeat_keywords: BorderImageRepeatKeyword::${keyword} => StyleBorderImageRepeat::${keyword}, % endfor }; % endfor } pub fn copy_border_image_repeat_from(&mut self, other: &Self) { self.gecko.mBorderImageRepeatH = other.gecko.mBorderImageRepeatH; self.gecko.mBorderImageRepeatV = other.gecko.mBorderImageRepeatV; } pub fn reset_border_image_repeat(&mut self, other: &Self) { self.copy_border_image_repeat_from(other) } pub fn clone_border_image_repeat(&self) -> longhands::border_image_repeat::computed_value::T { use crate::values::specified::border::BorderImageRepeatKeyword; use crate::gecko_bindings::structs::StyleBorderImageRepeat; % for side in ["H", "V"]: let servo_${side.lower()} = match self.gecko.mBorderImageRepeat${side} { % for keyword in border_image_repeat_keywords: StyleBorderImageRepeat::${keyword} => BorderImageRepeatKeyword::${keyword}, % endfor }; % endfor longhands::border_image_repeat::computed_value::T(servo_h, servo_v) } <% skip_scroll_margin_longhands = " ".join(["scroll-margin-%s" % x.ident for x in SIDES]) %> <% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %> <%self:impl_trait style_struct_name="Margin" skip_longhands="${skip_margin_longhands} ${skip_scroll_margin_longhands}"> % for side in SIDES: <% impl_split_style_coord("margin_%s" % side.ident, "mMargin", side.index) %> <% impl_split_style_coord("scroll_margin_%s" % side.ident, "mScrollMargin", side.index) %> % endfor <% skip_scroll_padding_longhands = " ".join(["scroll-padding-%s" % x.ident for x in SIDES]) %> <% skip_padding_longhands = " ".join(["padding-%s" % x.ident for x in SIDES]) %> <%self:impl_trait style_struct_name="Padding" skip_longhands="${skip_padding_longhands} ${skip_scroll_padding_longhands}"> % for side in SIDES: <% impl_split_style_coord("padding_%s" % side.ident, "mPadding", side.index) %> <% impl_split_style_coord("scroll_padding_%s" % side.ident, "mScrollPadding", side.index) %> % endfor <%self:impl_trait style_struct_name="Page"> <% skip_position_longhands = " ".join(x.ident for x in SIDES) %> <%self:impl_trait style_struct_name="Position" skip_longhands="${skip_position_longhands} masonry-auto-flow"> % for side in SIDES: <% impl_split_style_coord(side.ident, "mOffset", side.index) %> % endfor pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) { debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY); self.gecko.mJustifyItems.computed = v; } ${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")} <%self:impl_trait style_struct_name="Outline" skip_longhands="outline-style outline-width"> pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) { self.gecko.mOutlineStyle = v; // NB: This is needed to correctly handling the initial value of // outline-width when outline-style changes, see the // update_border_${side.ident} comment for more details. self.gecko.mActualOutlineWidth = self.gecko.mOutlineWidth; } pub fn copy_outline_style_from(&mut self, other: &Self) { self.set_outline_style(other.gecko.mOutlineStyle); } pub fn reset_outline_style(&mut self, other: &Self) { self.copy_outline_style_from(other) } pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T { self.gecko.mOutlineStyle.clone() } <% impl_non_negative_length("outline_width", "mActualOutlineWidth", inherit_from="mOutlineWidth", round_to_pixels=True) %> pub fn outline_has_nonzero_width(&self) -> bool { self.gecko.mActualOutlineWidth != 0 } <% skip_font_longhands = """font-family font-size font-size-adjust font-weight font-style font-stretch font-synthesis -x-lang font-variant-alternates font-variant-east-asian font-variant-ligatures font-variant-numeric font-language-override font-feature-settings font-variation-settings -moz-min-font-size-ratio -x-text-zoom""" %> <%self:impl_trait style_struct_name="Font" skip_longhands="${skip_font_longhands}"> // Negative numbers are invalid at parse time, but 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.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0)); self.gecko.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.gecko.mScriptUnconstrainedSize.0)); self.gecko.mFont.size = NonNegative(device.unzoom_text(self.gecko.mFont.size.0)); } pub fn copy_font_size_from(&mut self, other: &Self) { self.gecko.mScriptUnconstrainedSize = other.gecko.mScriptUnconstrainedSize; self.gecko.mSize = other.gecko.mScriptUnconstrainedSize; // NOTE: Intentionally not copying from mFont.size. The cascade process // recomputes the used size as needed. self.gecko.mFont.size = other.gecko.mSize; self.gecko.mFontSizeKeyword = other.gecko.mFontSizeKeyword; // TODO(emilio): Should we really copy over these two? self.gecko.mFontSizeFactor = other.gecko.mFontSizeFactor; self.gecko.mFontSizeOffset = other.gecko.mFontSizeOffset; } pub fn reset_font_size(&mut self, other: &Self) { self.copy_font_size_from(other) } pub fn set_font_size(&mut self, v: FontSize) { let computed_size = v.computed_size; self.gecko.mScriptUnconstrainedSize = computed_size; // These two may be changed from Cascade::fixup_font_stuff. self.gecko.mSize = computed_size; // NOTE: Intentionally not copying from used_size. The cascade process // recomputes the used size as needed. self.gecko.mFont.size = computed_size; self.gecko.mFontSizeKeyword = v.keyword_info.kw; self.gecko.mFontSizeFactor = v.keyword_info.factor; self.gecko.mFontSizeOffset = v.keyword_info.offset; } pub fn clone_font_size(&self) -> FontSize { use crate::values::specified::font::KeywordInfo; FontSize { computed_size: self.gecko.mSize, used_size: self.gecko.mFont.size, keyword_info: KeywordInfo { kw: self.gecko.mFontSizeKeyword, factor: self.gecko.mFontSizeFactor, offset: self.gecko.mFontSizeOffset, } } } ${impl_simple('font_weight', 'mFont.weight')} ${impl_simple('font_stretch', 'mFont.stretch')} ${impl_simple('font_style', 'mFont.style')} ${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")} ${impl_simple("font_variant_alternates", "mFont.variantAlternates")} ${impl_simple("font_size_adjust", "mFont.sizeAdjust")} ${impl_simple("font_family", "mFont.family")} #[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.gecko, ptr); } } #[allow(non_snake_case)] pub fn copy__x_lang_from(&mut self, other: &Self) { unsafe { Gecko_nsStyleFont_CopyLangFrom(&mut *self.gecko, &*other.gecko); } } #[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.gecko.mLanguage.mRawPtr) }) } #[allow(non_snake_case)] pub fn set__x_text_zoom(&mut self, v: longhands::_x_text_zoom::computed_value::T) { self.gecko.mAllowZoomAndMinSize = v.0; } #[allow(non_snake_case)] pub fn copy__x_text_zoom_from(&mut self, other: &Self) { self.gecko.mAllowZoomAndMinSize = other.gecko.mAllowZoomAndMinSize; } #[allow(non_snake_case)] pub fn reset__x_text_zoom(&mut self, other: &Self) { self.copy__x_text_zoom_from(other) } #[allow(non_snake_case)] pub fn clone__x_text_zoom(&self) -> longhands::_x_text_zoom::computed_value::T { longhands::_x_text_zoom::computed_value::T(self.gecko.mAllowZoomAndMinSize) } <% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %> ${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")} ${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")} ${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")} #[allow(non_snake_case)] pub fn clone__moz_min_font_size_ratio( &self, ) -> longhands::_moz_min_font_size_ratio::computed_value::T { Percentage(self.gecko.mMinFontSizeRatio as f32 / 100.) } #[allow(non_snake_case)] pub fn set__moz_min_font_size_ratio(&mut self, v: longhands::_moz_min_font_size_ratio::computed_value::T) { let scaled = v.0 * 100.; let percentage = if scaled > 255. { 255. } else if scaled < 0. { 0. } else { scaled }; self.gecko.mMinFontSizeRatio = percentage as u8; } ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')} <%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn copy_${type}_${ident}_from(&mut self, other: &Self) { self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len()); let count = other.gecko.m${type.capitalize()}${gecko_ffi_name}Count; self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = count; let iter = self.gecko.m${type.capitalize()}s.iter_mut().take(count as usize).zip( other.gecko.m${type.capitalize()}s.iter() ); for (ours, others) in iter { ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}.clone(); } } #[allow(non_snake_case)] pub fn reset_${type}_${ident}(&mut self, other: &Self) { self.copy_${type}_${ident}_from(other) } <%def name="impl_animation_or_transition_count(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn ${type}_${ident}_count(&self) -> usize { self.gecko.m${type.capitalize()}${gecko_ffi_name}Count as usize } <%def name="impl_animation_or_transition_time_value(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${type}_${ident}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { let v = v.into_iter(); debug_assert_ne!(v.len(), 0); let input_len = v.len(); self.gecko.m${type.capitalize()}s.ensure_len(input_len); self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = input_len as u32; for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().take(input_len as usize).zip(v) { gecko.m${gecko_ffi_name} = servo.seconds() * 1000.; } } #[allow(non_snake_case)] pub fn ${type}_${ident}_at(&self, index: usize) -> longhands::${type}_${ident}::computed_value::SingleComputedValue { use crate::values::computed::Time; Time::from_seconds(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.) } ${impl_animation_or_transition_count(type, ident, gecko_ffi_name)} ${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)} <%def name="impl_animation_or_transition_timing_function(type)"> pub fn set_${type}_timing_function(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { let v = v.into_iter(); debug_assert_ne!(v.len(), 0); let input_len = v.len(); self.gecko.m${type.capitalize()}s.ensure_len(input_len); self.gecko.m${type.capitalize()}TimingFunctionCount = input_len as u32; for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().take(input_len as usize).zip(v) { gecko.mTimingFunction = servo; } } ${impl_animation_or_transition_count(type, 'timing_function', 'TimingFunction')} ${impl_copy_animation_or_transition_value(type, 'timing_function', "TimingFunction")} pub fn ${type}_timing_function_at(&self, index: usize) -> longhands::${type}_timing_function::computed_value::SingleComputedValue { self.gecko.m${type.capitalize()}s[index].mTimingFunction.clone() } <%def name="impl_transition_time_value(ident, gecko_ffi_name)"> ${impl_animation_or_transition_time_value('transition', ident, gecko_ffi_name)} <%def name="impl_transition_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('transition', ident, gecko_ffi_name)} <%def name="impl_copy_animation_value(ident, gecko_ffi_name)"> ${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)} <%def name="impl_animation_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('animation', ident, gecko_ffi_name)} <%def name="impl_animation_time_value(ident, gecko_ffi_name)"> ${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)} <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pub fn set_animation_${ident}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword; let v = v.into_iter(); debug_assert_ne!(v.len(), 0); let input_len = v.len(); self.gecko.mAnimations.ensure_len(input_len); self.gecko.mAnimation${gecko_ffi_name}Count = input_len as u32; for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) { let result = match servo { % for value in keyword.values_for("gecko"): Keyword::${to_camel_case(value)} => structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)}, % endfor }; gecko.m${gecko_ffi_name} = result; } } #[allow(non_snake_case)] pub fn animation_${ident}_at(&self, index: usize) -> longhands::animation_${ident}::computed_value::SingleComputedValue { use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword; match self.gecko.mAnimations[index].m${gecko_ffi_name} ${keyword.maybe_cast("u32")} { % 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 for animation-${ident}"), % endif } } ${impl_animation_count(ident, gecko_ffi_name)} ${impl_copy_animation_value(ident, gecko_ffi_name)} <% 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.gecko.mDisplay = v; self.gecko.mOriginalDisplay = v; } #[inline] pub fn copy_display_from(&mut self, other: &Self) { self.set_display(other.gecko.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.gecko.mDisplay = v; } #[inline] pub fn clone_display(&self) -> longhands::display::computed_value::T { self.gecko.mDisplay } #[inline] pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) { self.gecko.mContain = v; self.gecko.mEffectiveContainment = v; } #[inline] pub fn copy_contain_from(&mut self, other: &Self) { self.set_contain(other.gecko.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.gecko.mContain } #[inline] pub fn set_effective_containment( &mut self, v: longhands::contain::computed_value::T ) { self.gecko.mEffectiveContainment = v; } #[inline] pub fn clone_effective_containment(&self) -> longhands::contain::computed_value::T { self.gecko.mEffectiveContainment } <%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}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter(); unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.len(), LayerType::${shorthand.title()}); } self.gecko.${image_layers_field}.${field_name}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field}.mLayers.iter_mut()) { geckolayer.${field_name} = { ${caller.body()} }; } } <%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)"> pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) { use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let count = other.gecko.${layers_field_name}.${field_name}Count; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${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.gecko.${layers_field_name}.mLayers.iter_mut() .zip(other.gecko.${layers_field_name}.mLayers.iter()) .take(count as usize) { layer.${field_name} = other.${field_name}.clone(); } self.gecko.${layers_field_name}.${field_name}Count = count; } pub fn reset_${shorthand}_${name}(&mut self, other: &Self) { self.copy_${shorthand}_${name}_from(other) } <%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}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator, { use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword; use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter(); unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${layer_field_name}, v.len(), LayerType::${shorthand.title()}); } self.gecko.${layer_field_name}.${field_name}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${layer_field_name}.mLayers.iter_mut()) { geckolayer.${field_name} = { match servo { % for value in keyword.values_for("gecko"): Keyword::${to_camel_case(value)} => structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')}, % endfor } }; } } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword; % if keyword.needs_cast(): % for value in keyword.values_for('gecko'): const ${keyword.casted_constant_name(value, "u8")} : u8 = structs::${keyword.gecko_constant(value)} as u8; % endfor % endif longhands::${ident}::computed_value::List( self.gecko.${layer_field_name}.mLayers.iter() .take(self.gecko.${layer_field_name}.${field_name}Count as usize) .map(|ref layer| { match layer.${field_name} { % for value in longhand.keyword.values_for("gecko"): % if keyword.needs_cast(): ${keyword.casted_constant_name(value, "u8")} % else: structs::${keyword.gecko_constant(value)} % endif => Keyword::${to_camel_case(value)}, % endfor % if keyword.gecko_inexhaustive: _ => panic!("Found unexpected value in style struct for ${ident} property"), % endif } }).collect() ) } <%def 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, } 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.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${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.gecko.${image_layers_field}.mPosition${orientation.upper()}Count; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, count as usize, LayerType::${shorthand.capitalize()}); } for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) .take(count as usize) { layer.mPosition.${keyword} = other.mPosition.${keyword}.clone(); } self.gecko.${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.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count as usize) .map(|position| position.mPosition.${keyword}.clone()) .collect() ) } pub fn set_${shorthand}_position_${orientation[0]}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter(); unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.len(), LayerType::${shorthand.capitalize()}); } self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { geckolayer.mPosition.${keyword} = servo; } } % endfor <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize"> servo pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T { longhands::${shorthand}_size::computed_value::List( self.gecko.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect() ) } pub fn copy_${shorthand}_image_from(&mut self, other: &Self) { use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { let count = other.gecko.${image_layers_field}.mImageCount; Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, count as usize, LayerType::${shorthand.capitalize()}); for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) .take(count as usize) { layer.mImage = other.mImage.clone(); } self.gecko.${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(&mut self, images: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let images = images.into_iter(); unsafe { Gecko_EnsureImageLayersLength( &mut self.gecko.${image_layers_field}, images.len(), LayerType::${shorthand.title()}, ); } self.gecko.${image_layers_field}.mImageCount = images.len() as u32; for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { geckoimage.mImage = image; } } pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T { longhands::${shorthand}_image::computed_value::List( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${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.gecko.${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.gecko.${image_layers_field}, max_len); } } // 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 style_struct_name="List" skip_longhands="list-style-type"> pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) { use nsstring::{nsACString, nsCStr}; use self::longhands::list_style_type::computed_value::T; match v { T::None => unsafe { bindings::Gecko_SetCounterStyleToNone(&mut self.gecko.mCounterStyle) } T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle), T::String(s) => unsafe { bindings::Gecko_SetCounterStyleToString( &mut self.gecko.mCounterStyle, &nsCStr::from(&s) as &nsACString, ) } } } pub fn copy_list_style_type_from(&mut self, other: &Self) { unsafe { Gecko_CopyCounterStyle(&mut self.gecko.mCounterStyle, &other.gecko.mCounterStyle); } } pub fn reset_list_style_type(&mut self, other: &Self) { self.copy_list_style_type_from(other) } pub fn clone_list_style_type(&self) -> longhands::list_style_type::computed_value::T { use self::longhands::list_style_type::computed_value::T; use crate::values::Either; use crate::values::generics::CounterStyle; use crate::gecko_bindings::bindings; let name = unsafe { bindings::Gecko_CounterStyle_GetName(&self.gecko.mCounterStyle) }; if !name.is_null() { let name = unsafe { Atom::from_raw(name) }; if name == atom!("none") { return T::None; } } let result = CounterStyle::from_gecko_value(&self.gecko.mCounterStyle); match result { Either::First(counter_style) => T::CounterStyle(counter_style), Either::Second(string) => T::String(string), } } <%self:impl_trait style_struct_name="Table"> <%self:impl_trait style_struct_name="Effects"> <%self:impl_trait style_struct_name="InheritedBox"> <%self:impl_trait style_struct_name="InheritedTable" skip_longhands="border-spacing"> pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) { self.gecko.mBorderSpacingCol = v.horizontal().0; self.gecko.mBorderSpacingRow = v.vertical().0; } pub fn copy_border_spacing_from(&mut self, other: &Self) { self.gecko.mBorderSpacingCol = other.gecko.mBorderSpacingCol; self.gecko.mBorderSpacingRow = other.gecko.mBorderSpacingRow; } pub fn reset_border_spacing(&mut self, other: &Self) { self.copy_border_spacing_from(other) } pub fn clone_border_spacing(&self) -> longhands::border_spacing::computed_value::T { longhands::border_spacing::computed_value::T::new( Au(self.gecko.mBorderSpacingCol).into(), Au(self.gecko.mBorderSpacingRow).into() ) } <%self:impl_trait style_struct_name="InheritedText" skip_longhands="-webkit-text-stroke-width"> ${impl_non_negative_length('_webkit_text_stroke_width', 'mWebkitTextStrokeWidth')} <%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter"> pub fn set_initial_letter(&mut self, v: longhands::initial_letter::computed_value::T) { use crate::values::generics::text::InitialLetter; match v { InitialLetter::Normal => { self.gecko.mInitialLetterSize = 0.; self.gecko.mInitialLetterSink = 0; }, InitialLetter::Specified(size, sink) => { self.gecko.mInitialLetterSize = size; if let Some(sink) = sink { self.gecko.mInitialLetterSink = sink; } else { self.gecko.mInitialLetterSink = size.floor() as i32; } } } } pub fn copy_initial_letter_from(&mut self, other: &Self) { self.gecko.mInitialLetterSize = other.gecko.mInitialLetterSize; self.gecko.mInitialLetterSink = other.gecko.mInitialLetterSink; } pub fn reset_initial_letter(&mut self, other: &Self) { self.copy_initial_letter_from(other) } pub fn clone_initial_letter(&self) -> longhands::initial_letter::computed_value::T { use crate::values::generics::text::InitialLetter; if self.gecko.mInitialLetterSize == 0. && self.gecko.mInitialLetterSink == 0 { InitialLetter::Normal } else if self.gecko.mInitialLetterSize.floor() as i32 == self.gecko.mInitialLetterSink { InitialLetter::Specified(self.gecko.mInitialLetterSize, None) } else { InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink)) } } <% 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 style_struct_name="InheritedSVG"> <%self:impl_trait style_struct_name="InheritedUI"> <%self:impl_trait style_struct_name="Column" skip_longhands="column-count column-rule-width column-rule-style"> #[allow(unused_unsafe)] pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) { use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount}; self.gecko.mColumnCount = match v { ColumnCount::Integer(integer) => { cmp::min(integer.0 as u32, unsafe { nsStyleColumn_kMaxColumnCount }) }, ColumnCount::Auto => nsStyleColumn_kColumnCountAuto }; } ${impl_simple_copy('column_count', 'mColumnCount')} pub fn clone_column_count(&self) -> longhands::column_count::computed_value::T { use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount}; if self.gecko.mColumnCount != nsStyleColumn_kColumnCountAuto { debug_assert!(self.gecko.mColumnCount >= 1 && self.gecko.mColumnCount <= nsStyleColumn_kMaxColumnCount); ColumnCount::Integer((self.gecko.mColumnCount as i32).into()) } else { ColumnCount::Auto } } pub fn set_column_rule_style(&mut self, v: longhands::column_rule_style::computed_value::T) { self.gecko.mColumnRuleStyle = v; // NB: This is needed to correctly handling the initial value of // column-rule-width when colun-rule-style changes, see the // update_border_${side.ident} comment for more details. self.gecko.mActualColumnRuleWidth = self.gecko.mColumnRuleWidth; } pub fn copy_column_rule_style_from(&mut self, other: &Self) { self.set_column_rule_style(other.gecko.mColumnRuleStyle); } pub fn reset_column_rule_style(&mut self, other: &Self) { self.copy_column_rule_style_from(other) } pub fn clone_column_rule_style(&self) -> longhands::column_rule_style::computed_value::T { self.gecko.mColumnRuleStyle.clone() } <% impl_non_negative_length("column_rule_width", "mActualColumnRuleWidth", inherit_from="mColumnRuleWidth", round_to_pixels=True) %> pub fn column_rule_has_nonzero_width(&self) -> bool { self.gecko.mActualColumnRuleWidth != 0 } <%self:impl_trait style_struct_name="Counters"> pub fn ineffective_content_property(&self) -> bool { !self.gecko.mContent.is_items() } <% skip_ui_longhands = """animation-name animation-delay animation-duration animation-direction animation-fill-mode animation-play-state animation-iteration-count animation-timing-function animation-composition animation-timeline transition-duration transition-delay transition-timing-function transition-property""" %> <%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}"> ${impl_transition_time_value('delay', 'Delay')} ${impl_transition_time_value('duration', 'Duration')} ${impl_animation_or_transition_timing_function('transition')} pub fn transition_combined_duration_at(&self, index: usize) -> f32 { // https://drafts.csswg.org/css-transitions/#transition-combined-duration self.gecko.mTransitions[index % self.gecko.mTransitionDurationCount as usize].mDuration.max(0.0) + self.gecko.mTransitions[index % self.gecko.mTransitionDelayCount as usize].mDelay } pub fn set_transition_property(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; let v = v.into_iter(); if v.len() != 0 { self.gecko.mTransitions.ensure_len(v.len()); self.gecko.mTransitionPropertyCount = v.len() as u32; for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) { unsafe { gecko.mUnknownProperty.clear() }; match servo { TransitionProperty::Unsupported(ident) => { gecko.mProperty = eCSSProperty_UNKNOWN; gecko.mUnknownProperty.mRawPtr = ident.0.into_addrefed(); }, TransitionProperty::Custom(name) => { gecko.mProperty = eCSSPropertyExtra_variable; gecko.mUnknownProperty.mRawPtr = name.into_addrefed(); } _ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(), } } } else { // In gecko |none| is represented by eCSSPropertyExtra_no_properties. self.gecko.mTransitionPropertyCount = 1; self.gecko.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties; } } /// Returns whether there are any transitions specified. pub fn specifies_transitions(&self) -> bool { use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties; if self.gecko.mTransitionPropertyCount == 1 && self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties && self.transition_combined_duration_at(0) <= 0.0f32 { return false; } self.gecko.mTransitionPropertyCount > 0 } pub fn transition_property_at(&self, index: usize) -> longhands::transition_property::computed_value::SingleComputedValue { use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; let property = self.gecko.mTransitions[index].mProperty; if property == eCSSProperty_UNKNOWN { let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); TransitionProperty::Unsupported(CustomIdent(unsafe{ Atom::from_raw(atom) })) } else if property == eCSSPropertyExtra_variable { let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); TransitionProperty::Custom(unsafe{ Atom::from_raw(atom) }) } else if property == eCSSPropertyExtra_no_properties { // Actually, we don't expect TransitionProperty::Unsupported also // represents "none", but if the caller wants to convert it, it is // fine. Please use it carefully. // // FIXME(emilio): This is a hack, is this reachable? TransitionProperty::Unsupported(CustomIdent(atom!("none"))) } else { property.into() } } pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID { self.gecko.mTransitions[index].mProperty } pub fn copy_transition_property_from(&mut self, other: &Self) { use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len()); let count = other.gecko.mTransitionPropertyCount; self.gecko.mTransitionPropertyCount = count; for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) { transition.mProperty = other.gecko.mTransitions[index].mProperty; unsafe { transition.mUnknownProperty.clear() }; if transition.mProperty == eCSSProperty_UNKNOWN || transition.mProperty == eCSSPropertyExtra_variable { let atom = other.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); transition.mUnknownProperty.mRawPtr = unsafe { Atom::from_raw(atom) }.into_addrefed(); } } } pub fn reset_transition_property(&mut self, other: &Self) { self.copy_transition_property_from(other) } ${impl_transition_count('property', 'Property')} pub fn animations_equals(&self, other: &Self) -> bool { return self.gecko.mAnimationNameCount == other.gecko.mAnimationNameCount && self.gecko.mAnimationDelayCount == other.gecko.mAnimationDelayCount && self.gecko.mAnimationDirectionCount == other.gecko.mAnimationDirectionCount && self.gecko.mAnimationDurationCount == other.gecko.mAnimationDurationCount && self.gecko.mAnimationFillModeCount == other.gecko.mAnimationFillModeCount && self.gecko.mAnimationIterationCountCount == other.gecko.mAnimationIterationCountCount && self.gecko.mAnimationPlayStateCount == other.gecko.mAnimationPlayStateCount && self.gecko.mAnimationTimingFunctionCount == other.gecko.mAnimationTimingFunctionCount && self.gecko.mAnimationCompositionCount == other.gecko.mAnimationCompositionCount && self.gecko.mAnimationTimelineCount == other.gecko.mAnimationTimelineCount && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.gecko.mAnimations, &other.gecko.mAnimations) } } pub fn set_animation_name(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); debug_assert_ne!(v.len(), 0); self.gecko.mAnimations.ensure_len(v.len()); self.gecko.mAnimationNameCount = v.len() as u32; for (servo, gecko) in v.zip(self.gecko.mAnimations.iter_mut()) { let atom = servo.0.as_atom().clone(); unsafe { bindings::Gecko_SetAnimationName(gecko, atom.into_addrefed()); } } } pub fn animation_name_at(&self, index: usize) -> longhands::animation_name::computed_value::SingleComputedValue { use crate::properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName; let atom = self.gecko.mAnimations[index].mName.mRawPtr; AnimationName(KeyframesName::from_atom(unsafe { Atom::from_raw(atom) })) } pub fn copy_animation_name_from(&mut self, other: &Self) { self.gecko.mAnimationNameCount = other.gecko.mAnimationNameCount; unsafe { bindings::Gecko_CopyAnimationNames(&mut self.gecko.mAnimations, &other.gecko.mAnimations); } } pub fn reset_animation_name(&mut self, other: &Self) { self.copy_animation_name_from(other) } ${impl_animation_count('name', 'Name')} ${impl_animation_time_value('delay', 'Delay')} ${impl_animation_time_value('duration', 'Duration')} ${impl_animation_keyword('direction', 'Direction', data.longhands_by_name["animation-direction"].keyword)} ${impl_animation_keyword('fill_mode', 'FillMode', data.longhands_by_name["animation-fill-mode"].keyword)} ${impl_animation_keyword('play_state', 'PlayState', data.longhands_by_name["animation-play-state"].keyword)} ${impl_animation_keyword('composition', 'Composition', data.longhands_by_name["animation-composition"].keyword)} pub fn set_animation_iteration_count(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { use std::f32; use crate::values::generics::box_::AnimationIterationCount; let v = v.into_iter(); debug_assert_ne!(v.len(), 0); let input_len = v.len(); self.gecko.mAnimations.ensure_len(input_len); self.gecko.mAnimationIterationCountCount = input_len as u32; for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) { match servo { AnimationIterationCount::Number(n) => gecko.mIterationCount = n, AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY, } } } pub fn animation_iteration_count_at( &self, index: usize, ) -> values::computed::AnimationIterationCount { use crate::values::generics::box_::AnimationIterationCount; if self.gecko.mAnimations[index].mIterationCount.is_infinite() { AnimationIterationCount::Infinite } else { AnimationIterationCount::Number(self.gecko.mAnimations[index].mIterationCount) } } ${impl_animation_count('iteration_count', 'IterationCount')} ${impl_copy_animation_value('iteration_count', 'IterationCount')} ${impl_animation_or_transition_timing_function('animation')} pub fn set_animation_timeline(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); debug_assert_ne!(v.len(), 0); let input_len = v.len(); self.gecko.mAnimations.ensure_len(input_len); self.gecko.mAnimationTimelineCount = input_len as u32; for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) { gecko.mTimeline = servo; } } pub fn animation_timeline_at(&self, index: usize) -> values::specified::box_::AnimationTimeline { self.gecko.mAnimations[index].mTimeline.clone() } ${impl_animation_count('timeline', 'Timeline')} ${impl_copy_animation_value('timeline', 'Timeline')} <%self:impl_trait style_struct_name="XUL"> % for style_struct in data.style_structs: ${declare_style_struct(style_struct)} ${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 } }