summaryrefslogtreecommitdiffstats
path: root/servo/components/style/properties/gecko.mako.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/properties/gecko.mako.rs')
-rw-r--r--servo/components/style/properties/gecko.mako.rs1971
1 files changed, 1971 insertions, 0 deletions
diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs
new file mode 100644
index 0000000000..4fe290980d
--- /dev/null
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -0,0 +1,1971 @@
+/* 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::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::logical_geometry::WritingMode;
+use crate::media_queries::Device;
+use crate::properties::longhands;
+use crate::rule_tree::StrongRuleNode;
+use crate::selector_parser::PseudoElement;
+use servo_arc::{Arc, UniqueArc};
+use std::mem::{forget, MaybeUninit, ManuallyDrop};
+use std::{cmp, ops, ptr};
+use crate::values::{self, CustomIdent, KeyframesName};
+use crate::values::computed::{BorderStyle, Percentage, Time, TransitionProperty};
+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<Arc<CustomPropertiesMap>>,
+ writing_mode: WritingMode,
+ flags: ComputedValueFlags,
+ rules: Option<StrongRuleNode>,
+ visited_style: Option<Arc<ComputedValues>>,
+ % for style_struct in data.style_structs:
+ ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
+ % endfor
+ ) -> Arc<Self> {
+ ComputedValuesInner::new(
+ custom_properties,
+ writing_mode,
+ flags,
+ rules,
+ visited_style,
+ % for style_struct in data.style_structs:
+ ${style_struct.ident},
+ % endfor
+ ).to_outer(pseudo)
+ }
+
+ pub fn default_values(doc: &structs::Document) -> Arc<Self> {
+ ComputedValuesInner::new(
+ /* 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<Self> {
+ // SAFETY: We're guaranteed to be allocated as an Arc<> since the
+ // functions above are the only ones that create ComputedValues
+ // instances in Gecko (and that must be the case since ComputedValues'
+ // member is private).
+ unsafe { Arc::from_raw_addrefed(self) }
+ }
+
+ #[inline]
+ pub fn is_pseudo_style(&self) -> bool {
+ self.0.mPseudoType != PseudoStyleType::NotPseudo
+ }
+
+ #[inline]
+ pub fn pseudo(&self) -> Option<PseudoElement> {
+ if !self.is_pseudo_style() {
+ return None;
+ }
+ PseudoElement::from_pseudo_type(self.0.mPseudoType)
+ }
+
+ #[inline]
+ pub fn is_first_line_style(&self) -> bool {
+ self.pseudo() == Some(PseudoElement::FirstLine)
+ }
+
+ /// Returns true if the display property is changed from 'none' to others.
+ pub fn is_display_property_changed_from_none(
+ &self,
+ old_values: Option<<&ComputedValues>
+ ) -> bool {
+ use crate::properties::longhands::display::computed_value::T as Display;
+
+ old_values.map_or(false, |old| {
+ let old_display_style = old.get_box().clone_display();
+ let new_display_style = self.get_box().clone_display();
+ old_display_style == Display::None &&
+ new_display_style != Display::None
+ })
+ }
+
+}
+
+impl Drop for ComputedValues {
+ fn drop(&mut self) {
+ // XXX this still relies on the destructor of ComputedValuesInner to run on the rust side,
+ // that's pretty wild.
+ unsafe {
+ bindings::Gecko_ComputedStyle_Destroy(&mut self.0);
+ }
+ }
+}
+
+unsafe impl Sync for ComputedValues {}
+unsafe impl Send for ComputedValues {}
+
+impl Clone for ComputedValues {
+ fn clone(&self) -> Self {
+ unreachable!()
+ }
+}
+
+impl Clone for ComputedValuesInner {
+ fn clone(&self) -> Self {
+ ComputedValuesInner {
+ % for style_struct in data.style_structs:
+ ${style_struct.gecko_name}: Arc::into_raw(unsafe { Arc::from_raw_addrefed(self.${style_struct.name_lower}_ptr()) }) as *const _,
+ % endfor
+ custom_properties: self.custom_properties.clone(),
+ writing_mode: self.writing_mode.clone(),
+ flags: self.flags.clone(),
+ rules: self.rules.clone(),
+ visited_style: if self.visited_style.is_null() {
+ ptr::null()
+ } else {
+ Arc::into_raw(unsafe { Arc::from_raw_addrefed(self.visited_style_ptr()) }) as *const _
+ },
+ }
+ }
+}
+
+
+impl Drop for ComputedValuesInner {
+ fn drop(&mut self) {
+ % for style_struct in data.style_structs:
+ let _ = unsafe { Arc::from_raw(self.${style_struct.name_lower}_ptr()) };
+ % endfor
+ if !self.visited_style.is_null() {
+ let _ = unsafe { Arc::from_raw(self.visited_style_ptr()) };
+ }
+ }
+}
+
+impl ComputedValuesInner {
+ pub fn new(
+ custom_properties: Option<Arc<CustomPropertiesMap>>,
+ writing_mode: WritingMode,
+ flags: ComputedValueFlags,
+ rules: Option<StrongRuleNode>,
+ visited_style: Option<Arc<ComputedValues>>,
+ % for style_struct in data.style_structs:
+ ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
+ % endfor
+ ) -> Self {
+ Self {
+ custom_properties,
+ writing_mode,
+ rules,
+ visited_style: visited_style.map_or(ptr::null(), |p| Arc::into_raw(p)) as *const _,
+ flags,
+ % for style_struct in data.style_structs:
+ ${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
+ % endfor
+ }
+ }
+
+ fn to_outer(self, pseudo: Option<<&PseudoElement>) -> Arc<ComputedValues> {
+ let pseudo_ty = match pseudo {
+ Some(p) => p.pseudo_type(),
+ None => structs::PseudoStyleType::NotPseudo,
+ };
+ unsafe {
+ let mut arc = UniqueArc::<ComputedValues>::new_uninit();
+ bindings::Gecko_ComputedStyle_Init(
+ arc.as_mut_ptr() as *mut _,
+ &self,
+ pseudo_ty,
+ );
+ // We're simulating move semantics by having C++ do a memcpy and
+ // then forgetting it on this end.
+ forget(self);
+ UniqueArc::assume_init(arc).shareable()
+ }
+ }
+}
+
+impl ops::Deref for ComputedValues {
+ type Target = ComputedValuesInner;
+ #[inline]
+ fn deref(&self) -> &ComputedValuesInner {
+ &self.0.mSource
+ }
+}
+
+impl ops::DerefMut for ComputedValues {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut ComputedValuesInner {
+ &mut self.0.mSource
+ }
+}
+
+impl ComputedValuesInner {
+ /// Returns true if the value of the `content` property would make a
+ /// pseudo-element not rendered.
+ #[inline]
+ pub fn ineffective_content_property(&self) -> bool {
+ self.get_counters().ineffective_content_property()
+ }
+
+ #[inline]
+ fn visited_style_ptr(&self) -> *const ComputedValues {
+ self.visited_style as *const _
+ }
+
+ /// Returns the visited style, if any.
+ pub fn visited_style(&self) -> Option<<&ComputedValues> {
+ unsafe { self.visited_style_ptr().as_ref() }
+ }
+
+ % for style_struct in data.style_structs:
+ #[inline]
+ fn ${style_struct.name_lower}_ptr(&self) -> *const style_structs::${style_struct.name} {
+ // This is sound because the wrapper we create is repr(transparent).
+ self.${style_struct.gecko_name} as *const _
+ }
+
+ #[inline]
+ pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
+ unsafe { Arc::from_raw_addrefed(self.${style_struct.name_lower}_ptr()) }
+ }
+ #[inline]
+ pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
+ unsafe { &*self.${style_struct.name_lower}_ptr() }
+ }
+
+ #[inline]
+ pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
+ unsafe {
+ let mut arc = Arc::from_raw(self.${style_struct.name_lower}_ptr());
+ let ptr = Arc::make_mut(&mut arc) as *mut _;
+ // Sound for the same reason _ptr() is sound.
+ self.${style_struct.gecko_name} = Arc::into_raw(arc) as *const _;
+ &mut *ptr
+ }
+ }
+ % endfor
+}
+
+<%def name="impl_simple_setter(ident, gecko_ffi_name)">
+ #[allow(non_snake_case)]
+ pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
+ ${set_gecko_property(gecko_ffi_name, "From::from(v)")}
+ }
+</%def>
+
+<%def name="impl_simple_clone(ident, gecko_ffi_name)">
+ #[allow(non_snake_case)]
+ pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+ From::from(self.${gecko_ffi_name}.clone())
+ }
+</%def>
+
+<%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
+ #[allow(non_snake_case)]
+ pub fn copy_${ident}_from(&mut self, other: &Self) {
+ self.${gecko_ffi_name} = other.${gecko_ffi_name}.clone();
+ }
+
+ #[allow(non_snake_case)]
+ pub fn reset_${ident}(&mut self, other: &Self) {
+ self.copy_${ident}_from(other)
+ }
+</%def>
+
+<%!
+def get_gecko_property(ffi_name, self_param = "self"):
+ return "%s.%s" % (self_param, ffi_name)
+
+def set_gecko_property(ffi_name, expr):
+ return "self.%s = %s;" % (ffi_name, expr)
+%>
+
+<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')">
+ #[allow(non_snake_case)]
+ pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
+ use crate::properties::longhands::${ident}::computed_value::T as Keyword;
+ // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
+ let result = match v {
+ % for value in keyword.values_for('gecko'):
+ Keyword::${to_camel_case(value)} =>
+ structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
+ % endfor
+ };
+ ${set_gecko_property(gecko_ffi_name, "result")}
+ }
+</%def>
+
+<%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')">
+ #[allow(non_snake_case)]
+ pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+ use crate::properties::longhands::${ident}::computed_value::T as Keyword;
+ // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
+
+ // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).
+ // And they are convert to signed integer in Rust bindings. We need to cast then
+ // as signed type when we have both signed/unsigned integer in order to use them
+ // as match's arms.
+ // Also, to use same implementation here we use casted constant if we have only singed values.
+ % if keyword.gecko_enum_prefix is None:
+ % for value in keyword.values_for('gecko'):
+ const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =
+ structs::${keyword.gecko_constant(value)} as ${cast_type};
+ % endfor
+
+ match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
+ % for value in keyword.values_for('gecko'):
+ ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},
+ % endfor
+ % if keyword.gecko_inexhaustive:
+ _ => panic!("Found unexpected value in style struct for ${ident} property"),
+ % endif
+ }
+ % else:
+ match ${get_gecko_property(gecko_ffi_name)} {
+ % for value in keyword.values_for('gecko'):
+ structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
+ % endfor
+ % if keyword.gecko_inexhaustive:
+ _ => panic!("Found unexpected value in style struct for ${ident} property"),
+ % endif
+ }
+ % endif
+ }
+</%def>
+
+<%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)">
+<%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
+<%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"></%call>
+<%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
+</%def>
+
+<%def name="impl_simple(ident, gecko_ffi_name)">
+<%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
+<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
+<%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
+</%def>
+
+<%def name="impl_border_width(ident, gecko_ffi_name, inherit_from)">
+ #[allow(non_snake_case)]
+ pub fn set_${ident}(&mut self, v: Au) {
+ let value = v.0;
+ self.${inherit_from} = value;
+ self.${gecko_ffi_name} = value;
+ }
+
+ #[allow(non_snake_case)]
+ pub fn copy_${ident}_from(&mut self, other: &Self) {
+ self.${inherit_from} = other.${inherit_from};
+ // NOTE: This is needed to easily handle the `unset` and `initial`
+ // keywords, which are implemented calling this function.
+ //
+ // In practice, this means that we may have an incorrect value here, but
+ // we'll adjust that properly in the style fixup phase.
+ //
+ // FIXME(emilio): We could clean this up a bit special-casing the reset_
+ // function below.
+ self.${gecko_ffi_name} = other.${inherit_from};
+ }
+
+ #[allow(non_snake_case)]
+ pub fn reset_${ident}(&mut self, other: &Self) {
+ self.copy_${ident}_from(other)
+ }
+
+ #[allow(non_snake_case)]
+ pub fn clone_${ident}(&self) -> Au {
+ Au(self.${gecko_ffi_name})
+ }
+</%def>
+
+<%def name="impl_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_ffi_name}.${index} = v;
+ }
+ #[allow(non_snake_case)]
+ pub fn copy_${ident}_from(&mut self, other: &Self) {
+ self.${gecko_ffi_name}.${index} =
+ other.${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_ffi_name}.${index}.clone()
+ }
+</%def>
+
+<%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_ffi_name}.data_at_mut(${side.index})
+ .copy_from(&other.${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>
+
+<%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_ffi_name}.${corner} = v;
+ }
+ #[allow(non_snake_case)]
+ pub fn copy_${ident}_from(&mut self, other: &Self) {
+ self.${gecko_ffi_name}.${corner} =
+ other.${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_ffi_name}.${corner}.clone()
+ }
+</%def>
+
+<%def name="impl_logical(name, **kwargs)">
+ ${helpers.logical_setter(name)}
+</%def>
+
+<%def name="impl_style_struct(style_struct)">
+/// A wrapper for ${style_struct.gecko_ffi_name}, to be able to manually construct / destruct /
+/// clone it.
+#[repr(transparent)]
+pub struct ${style_struct.gecko_struct_name}(ManuallyDrop<structs::${style_struct.gecko_ffi_name}>);
+
+impl ops::Deref for ${style_struct.gecko_struct_name} {
+ type Target = structs::${style_struct.gecko_ffi_name};
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl ops::DerefMut for ${style_struct.gecko_struct_name} {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut <Self as ops::Deref>::Target {
+ &mut self.0
+ }
+}
+
+impl ${style_struct.gecko_struct_name} {
+ #[allow(dead_code, unused_variables)]
+ pub fn default(document: &structs::Document) -> Arc<Self> {
+ unsafe {
+ let mut result = UniqueArc::<Self>::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::<Self>(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);
+ }
+ }
+}
+impl Clone for ${style_struct.gecko_struct_name} {
+ fn clone(&self) -> Self {
+ unsafe {
+ let mut result = MaybeUninit::<Self>::uninit();
+ // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
+ // these looks like Valgrind false-positives at a quick glance.
+ ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
+ Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &**self);
+ result.assume_init()
+ }
+ }
+}
+</%def>
+
+<%def name="impl_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_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_ffi_name})
+ }
+</%def>
+
+<%def name="impl_font_settings(ident, gecko_type, tag_type, value_type, gecko_value_type)">
+ <%
+ gecko_ffi_name = to_camel_case_lower(ident)
+ %>
+
+ pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
+ let iter = v.0.iter().map(|other| structs::${gecko_type} {
+ mTag: other.tag.0,
+ mValue: other.value as ${gecko_value_type},
+ });
+ self.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter);
+ }
+
+ pub fn copy_${ident}_from(&mut self, other: &Self) {
+ let iter = other.mFont.${gecko_ffi_name}.iter().map(|s| *s);
+ self.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.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::<Vec<_>>().into_boxed_slice()
+ )
+ }
+</%def>
+
+<%def name="impl_trait(style_struct_name, skip_longhands='')">
+<%
+ style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
+ longhands = [x for x in style_struct.longhands
+ if not (skip_longhands == "*" or x.name in skip_longhands.split())]
+
+ def longhand_method(longhand):
+ args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
+
+ # 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)
+ %>
+}
+</%def>
+
+<%!
+class Side(object):
+ def __init__(self, name, index):
+ self.name = name
+ self.ident = name.lower()
+ self.index = index
+
+SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
+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 ["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.mBorderStyle[${side.index}] = v;
+
+ // This is needed because the initial mComputedBorder value is set to
+ // zero.
+ //
+ // In order to compute stuff, we start from the initial struct, and keep
+ // going down the tree applying properties.
+ //
+ // That means, effectively, that when we set border-style to something
+ // non-hidden, we should use the initial border instead.
+ //
+ // Servo stores the initial border-width in the initial struct, and then
+ // adjusts as needed in the fixup phase. This means that the initial
+ // struct is technically not valid without fixups, and that you lose
+ // pretty much any sharing of the initial struct, which is kind of
+ // unfortunate.
+ //
+ // Gecko has two fields for this, one that stores the "specified"
+ // border, and other that stores the actual computed one. That means
+ // that when we set border-style, border-width may change and we need to
+ // sync back to the specified one. This is what this function does.
+ //
+ // Note that this doesn't impose any dependency in the order of
+ // computation of the properties. This is only relevant if border-style
+ // is specified, but border-width isn't. If border-width is specified at
+ // some point, the two mBorder and mComputedBorder fields would be the
+ // same already.
+ //
+ // Once we're here, we know that we'll run style fixups, so it's fine to
+ // just copy the specified border here, we'll adjust it if it's
+ // incorrect later.
+ self.mComputedBorder.${side.ident} = self.mBorder.${side.ident};
+ }
+
+ pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) {
+ self.set_border_${side.ident}_style(other.mBorderStyle[${side.index}]);
+ }
+
+ pub fn reset_border_${side.ident}_style(&mut self, other: &Self) {
+ self.copy_border_${side.ident}_style_from(other);
+ }
+
+ #[inline]
+ pub fn clone_border_${side.ident}_style(&self) -> BorderStyle {
+ self.mBorderStyle[${side.index}]
+ }
+
+ ${impl_border_width("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, "mBorder.%s" % side.ident)}
+
+ pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
+ self.mComputedBorder.${side.ident} != 0
+ }
+ % endfor
+
+ % 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.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.mBorderImageRepeatH = other.mBorderImageRepeatH;
+ self.mBorderImageRepeatV = other.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.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)
+ }
+</%self:impl_trait>
+
+<% 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
+</%self:impl_trait>
+
+<% 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>
+
+<%self:impl_trait style_struct_name="Page">
+</%self:impl_trait>
+
+<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
+<%self:impl_trait style_struct_name="Position"
+ skip_longhands="${skip_position_longhands}
+ 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.mJustifyItems.computed = v;
+ }
+
+ ${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="Outline"
+ skip_longhands="outline-style outline-width">
+
+ pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
+ self.mOutlineStyle = v;
+ // NB: This is needed to correctly handling the initial value of
+ // outline-width when outline-style changes, see the
+ // update_border_${side.ident} comment for more details.
+ self.mActualOutlineWidth = self.mOutlineWidth;
+ }
+
+ pub fn copy_outline_style_from(&mut self, other: &Self) {
+ self.set_outline_style(other.mOutlineStyle);
+ }
+
+ pub fn reset_outline_style(&mut self, other: &Self) {
+ self.copy_outline_style_from(other)
+ }
+
+ pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
+ self.mOutlineStyle.clone()
+ }
+
+ ${impl_border_width("outline_width", "mActualOutlineWidth", "mOutlineWidth")}
+
+ pub fn outline_has_nonzero_width(&self) -> bool {
+ self.mActualOutlineWidth != 0
+ }
+</%self:impl_trait>
+
+<% skip_font_longhands = """font-family font-size font-size-adjust font-weight
+ font-style font-stretch -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""" %>
+<%self:impl_trait style_struct_name="Font"
+ skip_longhands="${skip_font_longhands}">
+
+ // Negative numbers are invalid at parse time, but <integer> is still an
+ // i32.
+ <% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
+ <% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
+
+ pub fn unzoom_fonts(&mut self, device: &Device) {
+ use crate::values::generics::NonNegative;
+ self.mSize = NonNegative(device.unzoom_text(self.mSize.0));
+ self.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.mScriptUnconstrainedSize.0));
+ self.mFont.size = NonNegative(device.unzoom_text(self.mFont.size.0));
+ }
+
+ pub fn copy_font_size_from(&mut self, other: &Self) {
+ self.mScriptUnconstrainedSize = other.mScriptUnconstrainedSize;
+
+ self.mSize = other.mScriptUnconstrainedSize;
+ // NOTE: Intentionally not copying from mFont.size. The cascade process
+ // recomputes the used size as needed.
+ self.mFont.size = other.mSize;
+ self.mFontSizeKeyword = other.mFontSizeKeyword;
+
+ // TODO(emilio): Should we really copy over these two?
+ self.mFontSizeFactor = other.mFontSizeFactor;
+ self.mFontSizeOffset = other.mFontSizeOffset;
+ }
+
+ pub fn reset_font_size(&mut self, other: &Self) {
+ self.copy_font_size_from(other)
+ }
+
+ pub fn set_font_size(&mut self, v: FontSize) {
+ let computed_size = v.computed_size;
+ self.mScriptUnconstrainedSize = computed_size;
+
+ // These two may be changed from Cascade::fixup_font_stuff.
+ self.mSize = computed_size;
+ // NOTE: Intentionally not copying from used_size. The cascade process
+ // recomputes the used size as needed.
+ self.mFont.size = computed_size;
+
+ self.mFontSizeKeyword = v.keyword_info.kw;
+ self.mFontSizeFactor = v.keyword_info.factor;
+ self.mFontSizeOffset = v.keyword_info.offset;
+ }
+
+ pub fn clone_font_size(&self) -> FontSize {
+ use crate::values::specified::font::KeywordInfo;
+
+ FontSize {
+ computed_size: self.mSize,
+ used_size: self.mFont.size,
+ keyword_info: KeywordInfo {
+ kw: self.mFontSizeKeyword,
+ factor: self.mFontSizeFactor,
+ offset: self.mFontSizeOffset,
+ }
+ }
+ }
+
+ ${impl_simple('font_weight', 'mFont.weight')}
+ ${impl_simple('font_stretch', 'mFont.stretch')}
+ ${impl_simple('font_style', 'mFont.style')}
+
+ ${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, ptr);
+ }
+ }
+
+ #[allow(non_snake_case)]
+ pub fn copy__x_lang_from(&mut self, other: &Self) {
+ unsafe {
+ Gecko_nsStyleFont_CopyLangFrom(&mut **self, &**other);
+ }
+ }
+
+ #[allow(non_snake_case)]
+ pub fn reset__x_lang(&mut self, other: &Self) {
+ self.copy__x_lang_from(other)
+ }
+
+ #[allow(non_snake_case)]
+ pub fn clone__x_lang(&self) -> longhands::_x_lang::computed_value::T {
+ longhands::_x_lang::computed_value::T(unsafe {
+ Atom::from_raw(self.mLanguage.mRawPtr)
+ })
+ }
+
+
+ ${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.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.mMinFontSizeRatio = percentage as u8;
+ }
+
+ ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')}
+</%self:impl_trait>
+
+<%def name="impl_coordinated_property_copy(type, ident, gecko_ffi_name)">
+ #[allow(non_snake_case)]
+ pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
+ self.m${to_camel_case(type)}s.ensure_len(other.m${to_camel_case(type)}s.len());
+
+ let count = other.m${to_camel_case(type)}${gecko_ffi_name}Count;
+ self.m${to_camel_case(type)}${gecko_ffi_name}Count = count;
+
+ let iter = self.m${to_camel_case(type)}s.iter_mut().take(count as usize).zip(
+ other.m${to_camel_case(type)}s.iter()
+ );
+
+ for (ours, others) in iter {
+ ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}.clone();
+ }
+ }
+ #[allow(non_snake_case)]
+ pub fn reset_${type}_${ident}(&mut self, other: &Self) {
+ self.copy_${type}_${ident}_from(other)
+ }
+</%def>
+
+<%def name="impl_coordinated_property_count(type, ident, gecko_ffi_name)">
+ #[allow(non_snake_case)]
+ pub fn ${type}_${ident}_count(&self) -> usize {
+ self.m${to_camel_case(type)}${gecko_ffi_name}Count as usize
+ }
+</%def>
+
+<%def name="impl_coordinated_property(type, ident, gecko_ffi_name)">
+ #[allow(non_snake_case)]
+ pub fn set_${type}_${ident}<I>(&mut self, v: I)
+ where
+ I: IntoIterator<Item = longhands::${type}_${ident}::computed_value::single_value::T>,
+ I::IntoIter: ExactSizeIterator + Clone
+ {
+ let v = v.into_iter();
+ debug_assert_ne!(v.len(), 0);
+ let input_len = v.len();
+ self.m${to_camel_case(type)}s.ensure_len(input_len);
+
+ self.m${to_camel_case(type)}${gecko_ffi_name}Count = input_len as u32;
+ for (gecko, servo) in self.m${to_camel_case(type)}s.iter_mut().take(input_len as usize).zip(v) {
+ gecko.m${gecko_ffi_name} = servo;
+ }
+ }
+ #[allow(non_snake_case)]
+ pub fn ${type}_${ident}_at(&self, index: usize)
+ -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
+ self.m${to_camel_case(type)}s[index % self.${type}_${ident}_count()].m${gecko_ffi_name}.clone()
+ }
+ ${impl_coordinated_property_copy(type, ident, gecko_ffi_name)}
+ ${impl_coordinated_property_count(type, ident, gecko_ffi_name)}
+</%def>
+
+<%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')">
+ #[allow(non_snake_case)]
+ pub fn set_animation_${ident}<I>(&mut self, v: I)
+ where
+ I: IntoIterator<Item = longhands::animation_${ident}::computed_value::single_value::T>,
+ 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.mAnimations.ensure_len(input_len);
+
+ self.mAnimation${gecko_ffi_name}Count = input_len as u32;
+
+ for (gecko, servo) in self.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.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_coordinated_property_copy('animation', ident, gecko_ffi_name)}
+ ${impl_coordinated_property_count('animation', ident, gecko_ffi_name)}
+</%def>
+
+<% skip_box_longhands= """display contain""" %>
+<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
+ #[inline]
+ pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
+ self.mDisplay = v;
+ self.mOriginalDisplay = v;
+ }
+
+ #[inline]
+ pub fn copy_display_from(&mut self, other: &Self) {
+ self.set_display(other.mDisplay);
+ }
+
+ #[inline]
+ pub fn reset_display(&mut self, other: &Self) {
+ self.copy_display_from(other)
+ }
+
+ #[inline]
+ pub fn set_adjusted_display(
+ &mut self,
+ v: longhands::display::computed_value::T,
+ _is_item_or_root: bool
+ ) {
+ self.mDisplay = v;
+ }
+
+ #[inline]
+ pub fn clone_display(&self) -> longhands::display::computed_value::T {
+ self.mDisplay
+ }
+
+ #[inline]
+ pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {
+ self.mContain = v;
+ self.mEffectiveContainment = v;
+ }
+
+ #[inline]
+ pub fn copy_contain_from(&mut self, other: &Self) {
+ self.set_contain(other.mContain);
+ }
+
+ #[inline]
+ pub fn reset_contain(&mut self, other: &Self) {
+ self.copy_contain_from(other)
+ }
+
+ #[inline]
+ pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
+ self.mContain
+ }
+
+ #[inline]
+ pub fn set_effective_containment(
+ &mut self,
+ v: longhands::contain::computed_value::T
+ ) {
+ self.mEffectiveContainment = v;
+ }
+
+ #[inline]
+ pub fn clone_effective_containment(&self) -> longhands::contain::computed_value::T {
+ self.mEffectiveContainment
+ }
+</%self:impl_trait>
+
+<%def name="simple_image_array_property(name, shorthand, field_name)">
+ <%
+ image_layers_field = "mImage" if shorthand == "background" else "mMask"
+ copy_simple_image_array_property(name, shorthand, image_layers_field, field_name)
+ %>
+
+ pub fn set_${shorthand}_${name}<I>(&mut self, v: I)
+ where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,
+ I::IntoIter: ExactSizeIterator
+ {
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+ let v = v.into_iter();
+
+ unsafe {
+ Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
+ LayerType::${shorthand.title()});
+ }
+
+ self.${image_layers_field}.${field_name}Count = v.len() as u32;
+ for (servo, geckolayer) in v.zip(self.${image_layers_field}.mLayers.iter_mut()) {
+ geckolayer.${field_name} = {
+ ${caller.body()}
+ };
+ }
+ }
+</%def>
+
+<%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)">
+ pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+
+ let count = other.${layers_field_name}.${field_name}Count;
+ unsafe {
+ Gecko_EnsureImageLayersLength(&mut self.${layers_field_name},
+ count as usize,
+ LayerType::${shorthand.title()});
+ }
+ // FIXME(emilio): This may be bogus in the same way as bug 1426246.
+ for (layer, other) in self.${layers_field_name}.mLayers.iter_mut()
+ .zip(other.${layers_field_name}.mLayers.iter())
+ .take(count as usize) {
+ layer.${field_name} = other.${field_name}.clone();
+ }
+ self.${layers_field_name}.${field_name}Count = count;
+ }
+
+ pub fn reset_${shorthand}_${name}(&mut self, other: &Self) {
+ self.copy_${shorthand}_${name}_from(other)
+ }
+</%def>
+
+<%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)">
+ <%
+ ident = "%s_%s" % (shorthand, name)
+ style_struct = next(x for x in data.style_structs if x.name == struct_name)
+ longhand = next(x for x in style_struct.longhands if x.ident == ident)
+ keyword = longhand.keyword
+ %>
+
+ <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %>
+
+ pub fn set_${ident}<I>(&mut self, v: I)
+ where
+ I: IntoIterator<Item=longhands::${ident}::computed_value::single_value::T>,
+ I::IntoIter: ExactSizeIterator,
+ {
+ use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+
+ let v = v.into_iter();
+
+ unsafe {
+ Gecko_EnsureImageLayersLength(&mut self.${layer_field_name}, v.len(),
+ LayerType::${shorthand.title()});
+ }
+
+ self.${layer_field_name}.${field_name}Count = v.len() as u32;
+ for (servo, geckolayer) in v.zip(self.${layer_field_name}.mLayers.iter_mut()) {
+ geckolayer.${field_name} = {
+ match servo {
+ % for value in keyword.values_for("gecko"):
+ Keyword::${to_camel_case(value)} =>
+ structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
+ % endfor
+ }
+ };
+ }
+ }
+
+ pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+ use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
+
+ % if keyword.needs_cast():
+ % for value in keyword.values_for('gecko'):
+ const ${keyword.casted_constant_name(value, "u8")} : u8 =
+ structs::${keyword.gecko_constant(value)} as u8;
+ % endfor
+ % endif
+
+ longhands::${ident}::computed_value::List(
+ self.${layer_field_name}.mLayers.iter()
+ .take(self.${layer_field_name}.${field_name}Count as usize)
+ .map(|ref layer| {
+ match layer.${field_name} {
+ % for value in longhand.keyword.values_for("gecko"):
+ % if keyword.needs_cast():
+ ${keyword.casted_constant_name(value, "u8")}
+ % else:
+ structs::${keyword.gecko_constant(value)}
+ % endif
+ => Keyword::${to_camel_case(value)},
+ % endfor
+ % if keyword.gecko_inexhaustive:
+ _ => panic!("Found unexpected value in style struct for ${ident} property"),
+ % endif
+ }
+ }).collect()
+ )
+ }
+</%def>
+
+<%def name="impl_common_image_layer_properties(shorthand)">
+ <%
+ if shorthand == "background":
+ image_layers_field = "mImage"
+ struct_name = "Background"
+ else:
+ image_layers_field = "mMask"
+ struct_name = "SVG"
+ %>
+
+ <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
+ use crate::values::specified::background::BackgroundRepeatKeyword;
+ use crate::gecko_bindings::structs::nsStyleImageLayers_Repeat;
+ use crate::gecko_bindings::structs::StyleImageLayerRepeat;
+
+ fn to_ns(repeat: BackgroundRepeatKeyword) -> StyleImageLayerRepeat {
+ match repeat {
+ BackgroundRepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,
+ BackgroundRepeatKeyword::Space => StyleImageLayerRepeat::Space,
+ BackgroundRepeatKeyword::Round => StyleImageLayerRepeat::Round,
+ BackgroundRepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat,
+ }
+ }
+
+ let repeat_x = to_ns(servo.0);
+ let repeat_y = to_ns(servo.1);
+ nsStyleImageLayers_Repeat {
+ mXRepeat: repeat_x,
+ mYRepeat: repeat_y,
+ }
+ </%self:simple_image_array_property>
+
+ pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {
+ use crate::properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
+ use crate::values::specified::background::BackgroundRepeatKeyword;
+ use crate::gecko_bindings::structs::StyleImageLayerRepeat;
+
+ fn to_servo(repeat: StyleImageLayerRepeat) -> BackgroundRepeatKeyword {
+ match repeat {
+ StyleImageLayerRepeat::Repeat => BackgroundRepeatKeyword::Repeat,
+ StyleImageLayerRepeat::Space => BackgroundRepeatKeyword::Space,
+ StyleImageLayerRepeat::Round => BackgroundRepeatKeyword::Round,
+ StyleImageLayerRepeat::NoRepeat => BackgroundRepeatKeyword::NoRepeat,
+ _ => panic!("Found unexpected value in style struct for ${shorthand}_repeat property"),
+ }
+ }
+
+ longhands::${shorthand}_repeat::computed_value::List(
+ self.${image_layers_field}.mLayers.iter()
+ .take(self.${image_layers_field}.mRepeatCount as usize)
+ .map(|ref layer| {
+ T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat))
+ }).collect()
+ )
+ }
+
+ <% impl_simple_image_array_property("clip", shorthand, image_layers_field, "mClip", struct_name) %>
+ <% impl_simple_image_array_property("origin", shorthand, image_layers_field, "mOrigin", struct_name) %>
+
+ % for (orientation, keyword) in [("x", "horizontal"), ("y", "vertical")]:
+ pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+
+ let count = other.${image_layers_field}.mPosition${orientation.upper()}Count;
+
+ unsafe {
+ Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
+ count as usize,
+ LayerType::${shorthand.capitalize()});
+ }
+
+ for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
+ .zip(other.${image_layers_field}.mLayers.iter())
+ .take(count as usize) {
+ layer.mPosition.${keyword} = other.mPosition.${keyword}.clone();
+ }
+ self.${image_layers_field}.mPosition${orientation.upper()}Count = count;
+ }
+
+ pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) {
+ self.copy_${shorthand}_position_${orientation}_from(other)
+ }
+
+ pub fn clone_${shorthand}_position_${orientation}(&self)
+ -> longhands::${shorthand}_position_${orientation}::computed_value::T {
+ longhands::${shorthand}_position_${orientation}::computed_value::List(
+ self.${image_layers_field}.mLayers.iter()
+ .take(self.${image_layers_field}.mPosition${orientation.upper()}Count as usize)
+ .map(|position| position.mPosition.${keyword}.clone())
+ .collect()
+ )
+ }
+
+ pub fn set_${shorthand}_position_${orientation[0]}<I>(&mut self,
+ v: I)
+ where I: IntoIterator<Item = longhands::${shorthand}_position_${orientation[0]}
+ ::computed_value::single_value::T>,
+ I::IntoIter: ExactSizeIterator
+ {
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+
+ let v = v.into_iter();
+
+ unsafe {
+ Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
+ LayerType::${shorthand.capitalize()});
+ }
+
+ self.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;
+ for (servo, geckolayer) in v.zip(self.${image_layers_field}
+ .mLayers.iter_mut()) {
+ geckolayer.mPosition.${keyword} = servo;
+ }
+ }
+ % endfor
+
+ <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
+ servo
+ </%self:simple_image_array_property>
+
+ pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {
+ longhands::${shorthand}_size::computed_value::List(
+ self.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect()
+ )
+ }
+
+ pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+ unsafe {
+ let count = other.${image_layers_field}.mImageCount;
+ Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
+ count as usize,
+ LayerType::${shorthand.capitalize()});
+
+ for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
+ .zip(other.${image_layers_field}.mLayers.iter())
+ .take(count as usize) {
+ layer.mImage = other.mImage.clone();
+ }
+ self.${image_layers_field}.mImageCount = count;
+ }
+ }
+
+ pub fn reset_${shorthand}_image(&mut self, other: &Self) {
+ self.copy_${shorthand}_image_from(other)
+ }
+
+ #[allow(unused_variables)]
+ pub fn set_${shorthand}_image<I>(&mut self, images: I)
+ where I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,
+ I::IntoIter: ExactSizeIterator
+ {
+ use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
+
+ let images = images.into_iter();
+
+ unsafe {
+ Gecko_EnsureImageLayersLength(
+ &mut self.${image_layers_field},
+ images.len(),
+ LayerType::${shorthand.title()},
+ );
+ }
+
+ self.${image_layers_field}.mImageCount = images.len() as u32;
+ for (image, geckoimage) in images.zip(self.${image_layers_field}
+ .mLayers.iter_mut()) {
+ geckoimage.mImage = image;
+ }
+ }
+
+ pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {
+ longhands::${shorthand}_image::computed_value::List(
+ self.${image_layers_field}.mLayers.iter()
+ .take(self.${image_layers_field}.mImageCount as usize)
+ .map(|layer| layer.mImage.clone())
+ .collect()
+ )
+ }
+
+ <%
+ fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize"
+ if shorthand == "background":
+ fill_fields += " mAttachment mBlendMode"
+ else:
+ # mSourceURI uses mImageCount
+ fill_fields += " mMaskMode mComposite"
+ %>
+ pub fn fill_arrays(&mut self) {
+ use crate::gecko_bindings::bindings::Gecko_FillAllImageLayers;
+ use std::cmp;
+ let mut max_len = 1;
+ % for member in fill_fields.split():
+ max_len = cmp::max(max_len, self.${image_layers_field}.${member}Count);
+ % endfor
+ unsafe {
+ // While we could do this manually, we'd need to also manually
+ // run all the copy constructors, so we just delegate to gecko
+ Gecko_FillAllImageLayers(&mut self.${image_layers_field}, max_len);
+ }
+ }
+</%def>
+
+// TODO: Gecko accepts lists in most background-related properties. We just use
+// the first element (which is the common case), but at some point we want to
+// add support for parsing these lists in servo and pushing to nsTArray's.
+<% skip_background_longhands = """background-repeat
+ background-image background-clip
+ background-origin background-attachment
+ background-size background-position
+ background-blend-mode
+ background-position-x
+ background-position-y""" %>
+<%self:impl_trait style_struct_name="Background"
+ skip_longhands="${skip_background_longhands}">
+
+ <% impl_common_image_layer_properties("background") %>
+ <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
+ <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="List" 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.mCounterStyle)
+ }
+ T::CounterStyle(s) => s.to_gecko_value(&mut self.mCounterStyle),
+ T::String(s) => unsafe {
+ bindings::Gecko_SetCounterStyleToString(
+ &mut self.mCounterStyle,
+ &nsCStr::from(&s) as &nsACString,
+ )
+ }
+ }
+ }
+
+ pub fn copy_list_style_type_from(&mut self, other: &Self) {
+ unsafe {
+ Gecko_CopyCounterStyle(&mut self.mCounterStyle, &other.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.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.mCounterStyle);
+ match result {
+ Either::First(counter_style) => T::CounterStyle(counter_style),
+ Either::Second(string) => T::String(string),
+ }
+ }
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="Table">
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="Effects">
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="InheritedBox">
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="InheritedTable"
+ skip_longhands="border-spacing">
+
+ pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) {
+ self.mBorderSpacingCol = v.horizontal().0;
+ self.mBorderSpacingRow = v.vertical().0;
+ }
+
+ pub fn copy_border_spacing_from(&mut self, other: &Self) {
+ self.mBorderSpacingCol = other.mBorderSpacingCol;
+ self.mBorderSpacingRow = other.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.mBorderSpacingCol).into(),
+ Au(self.mBorderSpacingRow).into()
+ )
+ }
+</%self:impl_trait>
+
+
+<%self:impl_trait style_struct_name="InheritedText">
+</%self:impl_trait>
+
+<%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.mInitialLetterSize = 0.;
+ self.mInitialLetterSink = 0;
+ },
+ InitialLetter::Specified(size, sink) => {
+ self.mInitialLetterSize = size;
+ if let Some(sink) = sink {
+ self.mInitialLetterSink = sink;
+ } else {
+ self.mInitialLetterSink = size.floor() as i32;
+ }
+ }
+ }
+ }
+
+ pub fn copy_initial_letter_from(&mut self, other: &Self) {
+ self.mInitialLetterSize = other.mInitialLetterSize;
+ self.mInitialLetterSink = other.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.mInitialLetterSize == 0. && self.mInitialLetterSink == 0 {
+ InitialLetter::Normal
+ } else if self.mInitialLetterSize.floor() as i32 == self.mInitialLetterSink {
+ InitialLetter::Specified(self.mInitialLetterSize, None)
+ } else {
+ InitialLetter::Specified(self.mInitialLetterSize, Some(self.mInitialLetterSink))
+ }
+ }
+</%self:impl_trait>
+
+<% skip_svg_longhands = """
+mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image
+"""
+%>
+<%self:impl_trait style_struct_name="SVG"
+ skip_longhands="${skip_svg_longhands}">
+ <% impl_common_image_layer_properties("mask") %>
+ <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
+ <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="InheritedSVG">
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="InheritedUI">
+</%self:impl_trait>
+
+<%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.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.mColumnCount != nsStyleColumn_kColumnCountAuto {
+ debug_assert!(self.mColumnCount >= 1 &&
+ self.mColumnCount <= nsStyleColumn_kMaxColumnCount);
+ ColumnCount::Integer((self.mColumnCount as i32).into())
+ } else {
+ ColumnCount::Auto
+ }
+ }
+
+ pub fn set_column_rule_style(&mut self, v: longhands::column_rule_style::computed_value::T) {
+ self.mColumnRuleStyle = v;
+ // NB: This is needed to correctly handling the initial value of
+ // column-rule-width when colun-rule-style changes, see the
+ // update_border_${side.ident} comment for more details.
+ self.mActualColumnRuleWidth = self.mColumnRuleWidth;
+ }
+
+ pub fn copy_column_rule_style_from(&mut self, other: &Self) {
+ self.set_column_rule_style(other.mColumnRuleStyle);
+ }
+
+ pub fn reset_column_rule_style(&mut self, other: &Self) {
+ self.copy_column_rule_style_from(other)
+ }
+
+ pub fn clone_column_rule_style(&self) -> longhands::column_rule_style::computed_value::T {
+ self.mColumnRuleStyle.clone()
+ }
+
+ ${impl_border_width("column_rule_width", "mActualColumnRuleWidth", "mColumnRuleWidth")}
+
+ pub fn column_rule_has_nonzero_width(&self) -> bool {
+ self.mActualColumnRuleWidth != 0
+ }
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="Counters">
+ pub fn ineffective_content_property(&self) -> bool {
+ !self.mContent.is_items()
+ }
+</%self:impl_trait>
+
+<% skip_ui_longhands = """animation-name animation-delay animation-duration
+ animation-direction animation-fill-mode
+ animation-play-state animation-iteration-count
+ animation-timing-function animation-composition animation-timeline
+ transition-duration transition-delay
+ transition-timing-function transition-property
+ scroll-timeline-name scroll-timeline-axis
+ view-timeline-name view-timeline-axis view-timeline-inset""" %>
+
+<%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}">
+ ${impl_coordinated_property('transition', 'delay', 'Delay')}
+ ${impl_coordinated_property('transition', 'duration', 'Duration')}
+ ${impl_coordinated_property('transition', 'timing_function', 'TimingFunction')}
+
+ pub fn transition_combined_duration_at(&self, index: usize) -> Time {
+ // https://drafts.csswg.org/css-transitions/#transition-combined-duration
+ Time::from_seconds(
+ self.transition_duration_at(index).seconds().max(0.0) +
+ self.transition_delay_at(index).seconds()
+ )
+ }
+
+ pub fn set_transition_property<I>(&mut self, v: I)
+ where
+ I: IntoIterator<Item = longhands::transition_property::computed_value::single_value::T>,
+ 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.mTransitions.ensure_len(v.len());
+ self.mTransitionPropertyCount = v.len() as u32;
+ for (servo, gecko) in v.zip(self.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.mTransitionPropertyCount = 1;
+ self.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.mTransitionPropertyCount == 1 &&
+ self.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties &&
+ self.transition_combined_duration_at(0).seconds() <= 0.0f32 {
+ return false;
+ }
+
+ self.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.mTransitions[index].mProperty;
+ if property == eCSSProperty_UNKNOWN {
+ let atom = self.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.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.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.mTransitions.ensure_len(other.mTransitions.len());
+
+ let count = other.mTransitionPropertyCount;
+ self.mTransitionPropertyCount = count;
+
+ for (index, transition) in self.mTransitions.iter_mut().enumerate().take(count as usize) {
+ transition.mProperty = other.mTransitions[index].mProperty;
+ unsafe { transition.mUnknownProperty.clear() };
+ if transition.mProperty == eCSSProperty_UNKNOWN ||
+ transition.mProperty == eCSSPropertyExtra_variable {
+ let atom = other.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_coordinated_property_count('transition', 'property', 'Property')}
+
+ pub fn animations_equals(&self, other: &Self) -> bool {
+ return self.mAnimationNameCount == other.mAnimationNameCount
+ && self.mAnimationDelayCount == other.mAnimationDelayCount
+ && self.mAnimationDirectionCount == other.mAnimationDirectionCount
+ && self.mAnimationDurationCount == other.mAnimationDurationCount
+ && self.mAnimationFillModeCount == other.mAnimationFillModeCount
+ && self.mAnimationIterationCountCount == other.mAnimationIterationCountCount
+ && self.mAnimationPlayStateCount == other.mAnimationPlayStateCount
+ && self.mAnimationTimingFunctionCount == other.mAnimationTimingFunctionCount
+ && self.mAnimationCompositionCount == other.mAnimationCompositionCount
+ && self.mAnimationTimelineCount == other.mAnimationTimelineCount
+ && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.mAnimations, &other.mAnimations) }
+ }
+
+ pub fn set_animation_name<I>(&mut self, v: I)
+ where
+ I: IntoIterator<Item = longhands::animation_name::computed_value::single_value::T>,
+ I::IntoIter: ExactSizeIterator
+ {
+ let v = v.into_iter();
+ debug_assert_ne!(v.len(), 0);
+ self.mAnimations.ensure_len(v.len());
+
+ self.mAnimationNameCount = v.len() as u32;
+ for (servo, gecko) in v.zip(self.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.mAnimations[index].mName.mRawPtr;
+ AnimationName(KeyframesName::from_atom(unsafe { Atom::from_raw(atom) }))
+ }
+ pub fn copy_animation_name_from(&mut self, other: &Self) {
+ self.mAnimationNameCount = other.mAnimationNameCount;
+ unsafe { bindings::Gecko_CopyAnimationNames(&mut self.mAnimations, &other.mAnimations); }
+ }
+
+ pub fn reset_animation_name(&mut self, other: &Self) {
+ self.copy_animation_name_from(other)
+ }
+
+ ${impl_coordinated_property_count('animation', 'name', 'Name')}
+ ${impl_coordinated_property('animation', 'delay', 'Delay')}
+ ${impl_coordinated_property('animation', '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)}
+ ${impl_coordinated_property('animation', 'iteration_count', 'IterationCount')}
+ ${impl_coordinated_property('animation', 'timeline', 'Timeline')}
+ ${impl_coordinated_property('animation', 'timing_function', 'TimingFunction')}
+
+ ${impl_coordinated_property('scroll_timeline', 'name', 'Name')}
+ ${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}
+
+ pub fn scroll_timelines_equals(&self, other: &Self) -> bool {
+ self.mScrollTimelineNameCount == other.mScrollTimelineNameCount
+ && self.mScrollTimelineAxisCount == other.mScrollTimelineAxisCount
+ && unsafe {
+ bindings::Gecko_StyleScrollTimelinesEquals(
+ &self.mScrollTimelines,
+ &other.mScrollTimelines,
+ )
+ }
+ }
+
+ ${impl_coordinated_property('view_timeline', 'name', 'Name')}
+ ${impl_coordinated_property('view_timeline', 'axis', 'Axis')}
+ ${impl_coordinated_property('view_timeline', 'inset', 'Inset')}
+
+ pub fn view_timelines_equals(&self, other: &Self) -> bool {
+ self.mViewTimelineNameCount == other.mViewTimelineNameCount
+ && self.mViewTimelineAxisCount == other.mViewTimelineAxisCount
+ && self.mViewTimelineInsetCount == other.mViewTimelineInsetCount
+ && unsafe {
+ bindings::Gecko_StyleViewTimelinesEquals(
+ &self.mViewTimelines,
+ &other.mViewTimelines,
+ )
+ }
+ }
+</%self:impl_trait>
+
+<%self:impl_trait style_struct_name="XUL">
+</%self:impl_trait>
+
+% for style_struct in data.style_structs:
+${impl_style_struct(style_struct)}
+% endfor
+
+/// Assert that the initial values set in Gecko style struct constructors
+/// match the values returned by `get_initial_value()` for each longhand.
+#[cfg(feature = "gecko")]
+#[inline]
+pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
+ if cfg!(debug_assertions) {
+ let data = data.borrow();
+ let cv = data.stylist.device().default_computed_values();
+ <%
+ # Skip properties with initial values that change at computed
+ # value time, or whose initial value depends on the document
+ # / other prefs.
+ SKIPPED = [
+ "border-top-width",
+ "border-bottom-width",
+ "border-left-width",
+ "border-right-width",
+ "column-rule-width",
+ "font-family",
+ "font-size",
+ "outline-width",
+ "color",
+ ]
+ TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
+ %>
+ % for property in TO_TEST:
+ assert_eq!(
+ cv.clone_${property.ident}(),
+ longhands::${property.ident}::get_initial_value(),
+ concat!(
+ "initial value in Gecko style struct for ",
+ stringify!(${property.ident}),
+ " must match longhands::",
+ stringify!(${property.ident}),
+ "::get_initial_value()"
+ )
+ );
+ % endfor
+ }
+}