diff --git a/components/style/gecko_bindings/sugar/mod.rs b/components/style/gecko_bindings/sugar/mod.rs index b4c94dcbe70..623125c396e 100644 --- a/components/style/gecko_bindings/sugar/mod.rs +++ b/components/style/gecko_bindings/sugar/mod.rs @@ -9,6 +9,7 @@ mod ns_css_shadow_array; mod ns_style_auto_array; pub mod ns_style_coord; mod ns_t_array; +mod ns_timing_function; pub mod ownership; pub mod refptr; mod style_complex_color; diff --git a/components/style/gecko_bindings/sugar/ns_style_auto_array.rs b/components/style/gecko_bindings/sugar/ns_style_auto_array.rs index 5730382d7d1..9c41a0b6ec4 100644 --- a/components/style/gecko_bindings/sugar/ns_style_auto_array.rs +++ b/components/style/gecko_bindings/sugar/ns_style_auto_array.rs @@ -4,10 +4,25 @@ //! Rust helpers for Gecko's `nsStyleAutoArray`. +use gecko_bindings::bindings::Gecko_EnsureStyleAnimationArrayLength; use gecko_bindings::structs::nsStyleAutoArray; use std::iter::{once, Chain, Once, IntoIterator}; +use std::ops::Index; use std::slice::{Iter, IterMut}; +impl Index for nsStyleAutoArray { + type Output = T; + fn index(&self, index: usize) -> &T { + if index > self.len() { + panic!("out of range") + } + match index { + 0 => &self.mFirstElement, + _ => &self.mOtherElements[index - 1], + } + } +} + impl nsStyleAutoArray { /// Mutably iterate over the array elements. pub fn iter_mut(&mut self) -> Chain, IterMut> { @@ -26,6 +41,14 @@ impl nsStyleAutoArray { pub fn len(&self) -> usize { 1 + self.mOtherElements.len() } + + /// Ensures that the array has length at least the given length. + pub fn ensure_len(&mut self, len: usize) { + unsafe { + Gecko_EnsureStyleAnimationArrayLength(self as *mut nsStyleAutoArray as *mut _, + len); + } + } } impl<'a, T> IntoIterator for &'a mut nsStyleAutoArray { diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs new file mode 100644 index 00000000000..8dc7c2b792d --- /dev/null +++ b/components/style/gecko_bindings/sugar/ns_timing_function.rs @@ -0,0 +1,70 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use euclid::point::TypedPoint2D; +use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type}; +use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd; +use properties::longhands::transition_timing_function::single_value::computed_value::T as TransitionTimingFunction; +use std::mem; + +impl From for nsTimingFunction { + fn from(function: TransitionTimingFunction) -> nsTimingFunction { + let mut tf: nsTimingFunction = unsafe { mem::zeroed() }; + + match function { + TransitionTimingFunction::Steps(steps, StartEnd::Start) => { + tf.mType = nsTimingFunction_Type::StepStart; + unsafe { + tf.__bindgen_anon_1.__bindgen_anon_1.as_mut().mSteps = steps; + } + }, + TransitionTimingFunction::Steps(steps, StartEnd::End) => { + tf.mType = nsTimingFunction_Type::StepEnd; + unsafe { + tf.__bindgen_anon_1.__bindgen_anon_1.as_mut().mSteps = steps; + } + }, + TransitionTimingFunction::CubicBezier(p1, p2) => { + tf.mType = nsTimingFunction_Type::CubicBezier; + let ref mut gecko_cubic_bezier = + unsafe { tf.__bindgen_anon_1.mFunc.as_mut() }; + gecko_cubic_bezier.mX1 = p1.x; + gecko_cubic_bezier.mY1 = p1.y; + gecko_cubic_bezier.mX2 = p2.x; + gecko_cubic_bezier.mY2 = p2.y; + }, + // FIXME: we need to add more types once TransitionTimingFunction + // has more types. + } + tf + } +} + +impl From for TransitionTimingFunction { + fn from(function: nsTimingFunction) -> TransitionTimingFunction { + match function.mType { + nsTimingFunction_Type::StepStart => { + TransitionTimingFunction::Steps(unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mSteps }, + StartEnd::Start) + }, + nsTimingFunction_Type::StepEnd => { + TransitionTimingFunction::Steps(unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mSteps }, + StartEnd::End) + }, + // FIXME: As above, we need to fix here. + nsTimingFunction_Type::Ease | + nsTimingFunction_Type::Linear | + nsTimingFunction_Type::EaseIn | + nsTimingFunction_Type::EaseOut | + nsTimingFunction_Type::EaseInOut | + nsTimingFunction_Type::CubicBezier => { + TransitionTimingFunction::CubicBezier( + TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 }, + unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY1 }), + TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX2 }, + unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY2 })) + }, + } + } +} diff --git a/components/style/properties/data.py b/components/style/properties/data.py index bbef9df1c4e..db4d2051173 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -60,12 +60,12 @@ class Keyword(object): def gecko_constant(self, value): moz_stripped = value.replace("-moz-", '') if self.gecko_strip_moz_prefix else value - parts = moz_stripped.split('-') + mapped = self.consts_map.get(value) if self.gecko_enum_prefix: - parts = [p.title() for p in parts] + parts = moz_stripped.split('-') + parts = mapped if mapped else [p.title() for p in parts] return self.gecko_enum_prefix + "::" + "".join(parts) else: - mapped = self.consts_map.get(value) suffix = mapped if mapped else moz_stripped.replace("-", "_") return self.gecko_constant_prefix + "_" + suffix.upper() diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 60bd2ddbdb4..31d87340066 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -452,12 +452,6 @@ impl Debug for ${style_struct.gecko_struct_name} { force_stub += ["font-variant"] # These have unusual representations in gecko. force_stub += ["list-style-type"] - # In a nsTArray, have to be done manually, but probably not too much work - # (the "filling them", not the "making them work") - force_stub += ["animation-name", "animation-duration", - "animation-timing-function", "animation-iteration-count", - "animation-direction", "animation-play-state", - "animation-fill-mode", "animation-delay"] # These are part of shorthands so we must include them in stylo builds, # but we haven't implemented the stylo glue for the longhand @@ -1023,7 +1017,81 @@ fn static_assert() { +<%def name="impl_copy_animation_value(ident, gecko_ffi_name)"> + #[allow(non_snake_case)] + pub fn copy_animation_${ident}_from(&mut self, other: &Self) { + unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) }; + self.gecko.mAnimation${gecko_ffi_name}Count = other.gecko.mAnimation${gecko_ffi_name}Count; + for (index, animation) in self.gecko.mAnimations.iter_mut().enumerate() { + animation.m${gecko_ffi_name} = other.gecko.mAnimations[index].m${gecko_ffi_name}; + } + } + + +<%def name="impl_animation_count(ident, gecko_ffi_name)"> + #[allow(non_snake_case)] + pub fn animation_${ident}_count(&self) -> usize { + self.gecko.mAnimation${gecko_ffi_name}Count as usize + } + + +<%def name="impl_animation_time_value(ident, gecko_ffi_name)"> + #[allow(non_snake_case)] + pub fn set_animation_${ident}(&mut self, v: longhands::animation_${ident}::computed_value::T) { + unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; + self.gecko.mAnimation${gecko_ffi_name}Count = v.0.len() as u32; + for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { + gecko.m${gecko_ffi_name} = servo.seconds() * 1000.; + } + } + #[allow(non_snake_case)] + pub fn animation_${ident}_at(&self, index: usize) + -> longhands::animation_${ident}::computed_value::SingleComputedValue { + use values::specified::Time; + Time(self.gecko.mAnimations[index].m${gecko_ffi_name} / 1000.) + } + ${impl_animation_count(ident, gecko_ffi_name)} + ${impl_copy_animation_value(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: longhands::animation_${ident}::computed_value::T) { + use properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword; + use gecko_bindings::structs; + + unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; + self.gecko.mAnimation${gecko_ffi_name}Count = v.0.len() as u32; + + for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { + let result = match servo { + % for value in keyword.gecko_values(): + Keyword::${to_rust_ident(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 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.gecko_values(): + structs::${keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)}, + % endfor + x => panic!("Found unexpected value for animation-${ident}: {:?}", x), + } + } + ${impl_animation_count(ident, gecko_ffi_name)} + ${impl_copy_animation_value(ident, gecko_ffi_name)} + + <% skip_box_longhands= """display overflow-y vertical-align + animation-name animation-delay animation-duration + animation-direction animation-fill-mode animation-play-state + animation-iteration-count animation-timing-function -moz-binding page-break-before page-break-after scroll-snap-points-x scroll-snap-points-y transform scroll-snap-type-y perspective-origin transform-origin""" %> @@ -1294,6 +1362,88 @@ fn static_assert() { unsafe { self.gecko.mSpecifiedTransform.set(&other.gecko.mSpecifiedTransform); } } + pub fn set_animation_name(&mut self, v: longhands::animation_name::computed_value::T) { + use nsstring::nsCString; + unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; + self.gecko.mAnimationNameCount = v.0.len() as u32; + for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { + gecko.mName.assign_utf8(&nsCString::from(servo.0.to_string())); + } + } + pub fn animation_name_at(&self, index: usize) + -> longhands::animation_name::computed_value::SingleComputedValue { + use Atom; + use properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName; + // XXX: Is there any effective ways? + AnimationName(Atom::from(String::from_utf16_lossy(&self.gecko.mAnimations[index].mName[..]))) + } + pub fn copy_animation_name_from(&mut self, other: &Self) { + unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) }; + self.gecko.mAnimationNameCount = other.gecko.mAnimationNameCount; + for (index, animation) in self.gecko.mAnimations.iter_mut().enumerate() { + animation.mName.assign(&other.gecko.mAnimations[index].mName); + } + } + ${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)} + + pub fn set_animation_iteration_count(&mut self, v: longhands::animation_iteration_count::computed_value::T) { + use std::f32; + use properties::longhands::animation_iteration_count::single_value::SpecifiedValue as AnimationIterationCount; + + unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; + self.gecko.mAnimationIterationCountCount = v.0.len() as u32; + for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { + match servo { + AnimationIterationCount::Number(n) => gecko.mIterationCount = n, + AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY, + } + } + } + pub fn animation_iteration_count_at(&self, index: usize) + -> longhands::animation_iteration_count::computed_value::SingleComputedValue { + use properties::longhands::animation_iteration_count::single_value::computed_value::T + as 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')} + + pub fn set_animation_timing_function(&mut self, v: longhands::animation_timing_function::computed_value::T) { + unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; + + self.gecko.mAnimationTimingFunctionCount = v.0.len() as u32; + for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { + gecko.mTimingFunction = servo.into(); + } + } + ${impl_animation_count('timing_function', 'TimingFunction')} + pub fn animation_timing_function_at(&self, index: usize) + -> longhands::animation_timing_function::computed_value::SingleComputedValue { + self.gecko.mAnimations[index].mTimingFunction.into() + } + pub fn copy_animation_timing_function_from(&mut self, other: &Self) { + unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) }; + self.gecko.mAnimationTimingFunctionCount = other.gecko.mAnimationTimingFunctionCount; + for (index, animation) in self.gecko.mAnimations.iter_mut().enumerate() { + animation.mTimingFunction = other.gecko.mAnimations[index].mTimingFunction; + } + } + <% scroll_snap_type_keyword = Keyword("scroll-snap-type", "none mandatory proximity") %> ${impl_keyword('scroll_snap_type_y', 'mScrollSnapTypeY', scroll_snap_type_keyword, need_clone=False)} diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index a363e05deb4..c0954cd693f 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -891,11 +891,14 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", impl ComputedValueAsSpecified for SpecifiedValue {} +<% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %> ${helpers.single_keyword("animation-direction", "normal reverse alternate alternate-reverse", need_index=True, animatable=False, vector=True, + gecko_enum_prefix="PlaybackDirection", + custom_consts=animation_direction_custom_consts, spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", allowed_in_keyframe_block=False)} @@ -915,6 +918,7 @@ ${helpers.single_keyword("animation-fill-mode", need_index=True, animatable=False, vector=True, + gecko_enum_prefix="FillMode", spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", allowed_in_keyframe_block=False)}