diff --git a/components/style/animation.rs b/components/style/animation.rs index 7f0c7e6a588..1df3dc08cca 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -455,8 +455,8 @@ pub struct Animation { impl Animation { /// Whether or not this animation is cancelled by changes from a new style. fn is_cancelled_in_new_style(&self, new_style: &Arc) -> bool { - let index = new_style - .get_box() + let new_ui = new_style.get_ui(); + let index = new_ui .animation_name_iter() .position(|animation_name| Some(&self.name) == animation_name.as_atom()); let index = match index { @@ -464,7 +464,7 @@ impl Animation { None => return true, }; - new_style.get_box().animation_duration_mod(index).seconds() == 0. + new_ui.animation_duration_mod(index).seconds() == 0. } /// Given the current time, advances this animation to the next iteration, @@ -1073,10 +1073,10 @@ impl ElementAnimationSet { old_style: &ComputedValues, new_style: &Arc, ) { - let box_style = new_style.get_box(); - let timing_function = box_style.transition_timing_function_mod(index); - let duration = box_style.transition_duration_mod(index); - let delay = box_style.transition_delay_mod(index).seconds() as f64; + let style = new_style.get_ui(); + let timing_function = style.transition_timing_function_mod(index); + let duration = style.transition_duration_mod(index); + let delay = style.transition_delay_mod(index).seconds() as f64; let now = context.current_time_for_animations; // Only start a new transition if the style actually changes between @@ -1344,15 +1344,15 @@ pub fn maybe_start_animations( ) where E: TElement, { - let box_style = new_style.get_box(); - for (i, name) in box_style.animation_name_iter().enumerate() { + let style = new_style.get_ui(); + for (i, name) in style.animation_name_iter().enumerate() { let name = match name.as_atom() { Some(atom) => atom, None => continue, }; debug!("maybe_start_animations: name={}", name); - let duration = box_style.animation_duration_mod(i).seconds() as f64; + let duration = style.animation_duration_mod(i).seconds() as f64; if duration == 0. { continue; } @@ -1375,14 +1375,14 @@ pub fn maybe_start_animations( // NB: This delay may be negative, meaning that the animation may be created // in a state where we have advanced one or more iterations or even that the // animation begins in a finished state. - let delay = box_style.animation_delay_mod(i).seconds(); + let delay = style.animation_delay_mod(i).seconds(); - let iteration_state = match box_style.animation_iteration_count_mod(i) { + let iteration_state = match style.animation_iteration_count_mod(i) { AnimationIterationCount::Infinite => KeyframesIterationState::Infinite(0.0), AnimationIterationCount::Number(n) => KeyframesIterationState::Finite(0.0, n.into()), }; - let animation_direction = box_style.animation_direction_mod(i); + let animation_direction = style.animation_direction_mod(i); let initial_direction = match animation_direction { AnimationDirection::Normal | AnimationDirection::Alternate => { @@ -1396,7 +1396,7 @@ pub fn maybe_start_animations( let now = context.current_time_for_animations; let started_at = now + delay as f64; let mut starting_progress = (now - started_at) / duration; - let state = match box_style.animation_play_state_mod(i) { + let state = match style.animation_play_state_mod(i) { AnimationPlayState::Paused => AnimationState::Paused(starting_progress), AnimationPlayState::Running => AnimationState::Pending, }; @@ -1406,7 +1406,7 @@ pub fn maybe_start_animations( &keyframe_animation, context, new_style, - new_style.get_box().animation_timing_function_mod(i), + style.animation_timing_function_mod(i), resolver, ); @@ -1416,7 +1416,7 @@ pub fn maybe_start_animations( computed_steps, started_at, duration, - fill_mode: box_style.animation_fill_mode_mod(i), + fill_mode: style.animation_fill_mode_mod(i), delay: delay as f64, iteration_state, state, diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index f5113de277d..ae3c49542ae 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -1496,7 +1496,7 @@ impl<'le> TElement for GeckoElement<'le> { ) -> bool { use crate::properties::LonghandIdSet; - let after_change_box_style = after_change_style.get_box(); + let after_change_ui_style = after_change_style.get_ui(); let existing_transitions = self.css_transitions_info(); let mut transitions_to_keep = LonghandIdSet::new(); for transition_property in after_change_style.transition_properties() { @@ -1506,7 +1506,7 @@ impl<'le> TElement for GeckoElement<'le> { transitions_to_keep.insert(physical_longhand); if self.needs_transitions_update_per_property( physical_longhand, - after_change_box_style.transition_combined_duration_at(transition_property.index), + after_change_ui_style.transition_combined_duration_at(transition_property.index), before_change_style, after_change_style, &existing_transitions, diff --git a/components/style/matching.rs b/components/style/matching.rs index 58b8a10221f..2e18a8de962 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -254,8 +254,8 @@ trait PrivateMatchMethods: TElement { new_style: &ComputedValues, pseudo_element: Option, ) -> bool { - let new_box_style = new_style.get_box(); - let new_style_specifies_animations = new_box_style.specifies_animations(); + let new_ui_style = new_style.get_ui(); + let new_style_specifies_animations = new_ui_style.specifies_animations(); let has_animations = self.has_css_animations(&context.shared, pseudo_element); if !new_style_specifies_animations && !has_animations { @@ -282,7 +282,7 @@ trait PrivateMatchMethods: TElement { }, }; - let old_box_style = old_style.get_box(); + let old_ui_style = old_style.get_ui(); let keyframes_or_timeline_could_have_changed = context .shared @@ -301,12 +301,12 @@ trait PrivateMatchMethods: TElement { } // If the animations changed, well... - if !old_box_style.animations_equals(new_box_style) { + if !old_ui_style.animations_equals(new_ui_style) { return true; } - let old_display = old_box_style.clone_display(); - let new_display = new_box_style.clone_display(); + let old_display = old_style.clone_display(); + let new_display = new_style.clone_display(); // If we were display: none, we may need to trigger animations. if old_display == Display::None && new_display != Display::None { @@ -341,14 +341,13 @@ trait PrivateMatchMethods: TElement { None => return false, }; - let new_box_style = new_style.get_box(); if !self.has_css_transitions(context.shared, pseudo_element) && - !new_box_style.specifies_transitions() + !new_style.get_ui().specifies_transitions() { return false; } - if new_box_style.clone_display().is_none() || old_style.clone_display().is_none() { + if new_style.clone_display().is_none() || old_style.clone_display().is_none() { return false; } @@ -765,8 +764,8 @@ trait PrivateMatchMethods: TElement { }, } - let old_display = old_values.get_box().clone_display(); - let new_display = new_values.get_box().clone_display(); + let old_display = old_values.clone_display(); + let new_display = new_values.clone_display(); if old_display != new_display { // If we used to be a display: none element, and no longer are, our diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index bc0a78183a8..8b95449dd51 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -755,7 +755,6 @@ fn static_assert() { <%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", @@ -1181,11 +1180,7 @@ fn static_assert() { <% skip_box_longhands= """display - animation-name animation-delay animation-duration - animation-direction animation-fill-mode animation-play-state - animation-iteration-count animation-timeline animation-timing-function - clear transition-duration transition-delay - transition-timing-function transition-property + clear -webkit-line-clamp""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> #[inline] @@ -1227,245 +1222,6 @@ fn static_assert() { ) %> ${impl_keyword('clear', 'mBreakType', clear_keyword)} - ${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 - && 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 = match servo.0 { - None => atom!(""), - Some(ref name) => name.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; - if atom == atom!("").as_ptr() { - return AnimationName(None) - } - AnimationName(Some(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)} - - 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')} - #[allow(non_snake_case)] pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) { self.gecko.mLineClamp = match v { @@ -2020,7 +1776,253 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask- } -<%self:impl_trait style_struct_name="UI"> +<% skip_ui_longhands = """animation-name animation-delay animation-duration + animation-direction animation-fill-mode + animation-play-state animation-iteration-count + animation-timeline animation-timing-function + 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 + && 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 = match servo.0 { + None => atom!(""), + Some(ref name) => name.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; + if atom == atom!("").as_ptr() { + return AnimationName(None) + } + AnimationName(Some(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)} + + 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"> diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 5ffea97ba50..b62ddb25ce4 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -799,7 +799,7 @@ impl<'a> TransitionPropertyIterator<'a> { pub fn from_style(style: &'a ComputedValues) -> Self { Self { style, - index_range: 0..style.get_box().transition_property_count(), + index_range: 0..style.get_ui().transition_property_count(), longhand_iterator: None, } } @@ -832,7 +832,7 @@ impl<'a> Iterator for TransitionPropertyIterator<'a> { } let index = self.index_range.next()?; - match self.style.get_box().transition_property_at(index) { + match self.style.get_ui().transition_property_at(index) { TransitionProperty::Longhand(longhand_id) => { return Some(TransitionPropertyIteration { longhand_id, diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index f534617e5bc..dd8957fd8ff 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> -<% from data import ALL_AXES, DEFAULT_RULES_EXCEPT_KEYFRAME, Keyword, Method, to_rust_ident, to_camel_case%> +<% from data import ALL_AXES, Keyword, Method, to_rust_ident, to_camel_case%> <% data.new_style_struct("Box", inherited=False, @@ -150,193 +150,6 @@ ${helpers.predefined_type( animation_value_type="discrete", )} -<% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %> - -${helpers.predefined_type( - "transition-duration", - "Time", - "computed::Time::zero()", - engines="gecko servo", - initial_specified_value="specified::Time::zero()", - parse_method="parse_non_negative", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration", -)} - -${helpers.predefined_type( - "transition-timing-function", - "TimingFunction", - "computed::TimingFunction::ease()", - engines="gecko servo", - initial_specified_value="specified::TimingFunction::ease()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function", -)} - -${helpers.predefined_type( - "transition-property", - "TransitionProperty", - "computed::TransitionProperty::all()", - engines="gecko servo", - initial_specified_value="specified::TransitionProperty::all()", - vector=True, - allow_empty="NotInitial", - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property", -)} - -${helpers.predefined_type( - "transition-delay", - "Time", - "computed::Time::zero()", - engines="gecko servo", - initial_specified_value="specified::Time::zero()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay", -)} - -<% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %> - -${helpers.predefined_type( - "animation-name", - "AnimationName", - "computed::AnimationName::none()", - engines="gecko servo", - initial_specified_value="specified::AnimationName::none()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-name", -)} - -${helpers.predefined_type( - "animation-duration", - "Time", - "computed::Time::zero()", - engines="gecko servo", - initial_specified_value="specified::Time::zero()", - parse_method="parse_non_negative", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration", -)} - -// animation-timing-function is the exception to the rule for allowed_in_keyframe_block: -// https://drafts.csswg.org/css-animations/#keyframes -${helpers.predefined_type( - "animation-timing-function", - "TimingFunction", - "computed::TimingFunction::ease()", - engines="gecko servo", - initial_specified_value="specified::TimingFunction::ease()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function", -)} - -${helpers.predefined_type( - "animation-iteration-count", - "AnimationIterationCount", - "computed::AnimationIterationCount::one()", - engines="gecko servo", - initial_specified_value="specified::AnimationIterationCount::one()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count", -)} - -<% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %> -${helpers.single_keyword( - "animation-direction", - "normal reverse alternate alternate-reverse", - engines="gecko servo", - need_index=True, - animation_value_type="none", - vector=True, - gecko_enum_prefix="PlaybackDirection", - custom_consts=animation_direction_custom_consts, - extra_prefixes=animation_extra_prefixes, - gecko_inexhaustive=True, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, -)} - -${helpers.single_keyword( - "animation-play-state", - "running paused", - engines="gecko servo", - need_index=True, - animation_value_type="none", - vector=True, - extra_prefixes=animation_extra_prefixes, - gecko_enum_prefix="StyleAnimationPlayState", - spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state", - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, -)} - -${helpers.single_keyword( - "animation-fill-mode", - "none forwards backwards both", - engines="gecko servo", - need_index=True, - animation_value_type="none", - vector=True, - gecko_enum_prefix="FillMode", - extra_prefixes=animation_extra_prefixes, - gecko_inexhaustive=True, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, -)} - -${helpers.predefined_type( - "animation-delay", - "Time", - "computed::Time::zero()", - engines="gecko servo", - initial_specified_value="specified::Time::zero()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay", - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, -)} - -${helpers.predefined_type( - "animation-timeline", - "AnimationTimeline", - "computed::AnimationTimeline::auto()", - engines="gecko servo", - servo_pref="layout.unimplemented", - initial_specified_value="specified::AnimationTimeline::auto()", - vector=True, - need_index=True, - animation_value_type="none", - gecko_pref="layout.css.scroll-linked-animations.enabled", - spec="https://drafts.csswg.org/css-animations-2/#propdef-animation-timeline", - rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, -)} - <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %> ${helpers.predefined_type( diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs index 400eaedf577..f28964f8819 100644 --- a/components/style/properties/longhands/ui.mako.rs +++ b/components/style/properties/longhands/ui.mako.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> -<% from data import Method %> +<% from data import DEFAULT_RULES_EXCEPT_KEYFRAME, Method %> // CSS Basic User Interface Module Level 1 // https://drafts.csswg.org/css-ui-3/ @@ -104,3 +104,189 @@ ${helpers.predefined_type( animation_value_type="discrete", spec="None (Nonstandard Firefox-only property)", )} + +<% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %> + +${helpers.predefined_type( + "transition-duration", + "Time", + "computed::Time::zero()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::Time::zero()", + parse_method="parse_non_negative", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration", +)} + +${helpers.predefined_type( + "transition-timing-function", + "TimingFunction", + "computed::TimingFunction::ease()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::TimingFunction::ease()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function", +)} + +${helpers.predefined_type( + "transition-property", + "TransitionProperty", + "computed::TransitionProperty::all()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::TransitionProperty::all()", + vector=True, + allow_empty="NotInitial", + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property", +)} + +${helpers.predefined_type( + "transition-delay", + "Time", + "computed::Time::zero()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::Time::zero()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay", +)} + +<% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %> + +${helpers.predefined_type( + "animation-name", + "AnimationName", + "computed::AnimationName::none()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::AnimationName::none()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-name", +)} + +${helpers.predefined_type( + "animation-duration", + "Time", + "computed::Time::zero()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::Time::zero()", + parse_method="parse_non_negative", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration", +)} + +// animation-timing-function is the exception to the rule for allowed_in_keyframe_block: +// https://drafts.csswg.org/css-animations/#keyframes +${helpers.predefined_type( + "animation-timing-function", + "TimingFunction", + "computed::TimingFunction::ease()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::TimingFunction::ease()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function", +)} + +${helpers.predefined_type( + "animation-iteration-count", + "AnimationIterationCount", + "computed::AnimationIterationCount::one()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::AnimationIterationCount::one()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count", +)} + +<% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %> +${helpers.single_keyword( + "animation-direction", + "normal reverse alternate alternate-reverse", + engines="gecko servo-2013 servo-2020", + need_index=True, + animation_value_type="none", + vector=True, + gecko_enum_prefix="PlaybackDirection", + custom_consts=animation_direction_custom_consts, + extra_prefixes=animation_extra_prefixes, + gecko_inexhaustive=True, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, +)} + +${helpers.single_keyword( + "animation-play-state", + "running paused", + engines="gecko servo-2013 servo-2020", + need_index=True, + animation_value_type="none", + vector=True, + extra_prefixes=animation_extra_prefixes, + gecko_enum_prefix="StyleAnimationPlayState", + spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state", + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, +)} + +${helpers.single_keyword( + "animation-fill-mode", + "none forwards backwards both", + engines="gecko servo-2013 servo-2020", + need_index=True, + animation_value_type="none", + vector=True, + gecko_enum_prefix="FillMode", + extra_prefixes=animation_extra_prefixes, + gecko_inexhaustive=True, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, +)} + +${helpers.predefined_type( + "animation-delay", + "Time", + "computed::Time::zero()", + engines="gecko servo-2013 servo-2020", + initial_specified_value="specified::Time::zero()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay", + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, +)} + +${helpers.predefined_type( + "animation-timeline", + "AnimationTimeline", + "computed::AnimationTimeline::auto()", + engines="gecko", + initial_specified_value="specified::AnimationTimeline::auto()", + vector=True, + need_index=True, + animation_value_type="none", + gecko_pref="layout.css.scroll-linked-animations.enabled", + spec="https://drafts.csswg.org/css-animations-2/#propdef-animation-timeline", + rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME, +)} diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index bfe4def58d2..d0bb6bd1bf6 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2933,7 +2933,7 @@ pub mod style_structs { % endif % endfor - % if style_struct.name == "Box": + % if style_struct.name == "UI": /// Returns whether there is any animation specified with /// animation-name other than `none`. pub fn specifies_animations(&self) -> bool { @@ -3065,7 +3065,7 @@ impl ComputedValues { /// Returns whether this style's display value is equal to contents. pub fn is_display_contents(&self) -> bool { - self.get_box().clone_display().is_contents() + self.clone_display().is_contents() } /// Gets a reference to the rule node. Panic if no rule node exists. diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs index c52ae420683..df0d6f76abe 100644 --- a/components/style/properties/shorthands/box.mako.rs +++ b/components/style/properties/shorthands/box.mako.rs @@ -24,318 +24,6 @@ ${helpers.two_properties_shorthand( "(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)", )} -macro_rules! try_parse_one { - ($context: expr, $input: expr, $var: ident, $prop_module: ident) => { - if $var.is_none() { - if let Ok(value) = $input.try_parse(|i| { - $prop_module::single_value::parse($context, i) - }) { - $var = Some(value); - continue; - } - } - }; -} - -<%helpers:shorthand name="transition" - engines="gecko servo" - extra_prefixes="moz:layout.css.prefixes.transitions webkit" - sub_properties="transition-property transition-duration - transition-timing-function - transition-delay" - spec="https://drafts.csswg.org/css-transitions/#propdef-transition"> - use crate::parser::Parse; - % for prop in "delay duration property timing_function".split(): - use crate::properties::longhands::transition_${prop}; - % endfor - use crate::values::specified::TransitionProperty; - - pub fn parse_value<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - struct SingleTransition { - % for prop in "duration timing_function delay".split(): - transition_${prop}: transition_${prop}::SingleSpecifiedValue, - % endfor - // Unlike other properties, transition-property uses an Option<> to - // represent 'none' as `None`. - transition_property: Option, - } - - fn parse_one_transition<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - % for prop in "property duration timing_function delay".split(): - let mut ${prop} = None; - % endfor - - let mut parsed = 0; - loop { - parsed += 1; - - try_parse_one!(context, input, duration, transition_duration); - try_parse_one!(context, input, timing_function, transition_timing_function); - try_parse_one!(context, input, delay, transition_delay); - // Must check 'transition-property' after 'transition-timing-function' since - // 'transition-property' accepts any keyword. - if property.is_none() { - if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) { - property = Some(Some(value)); - continue; - } - - if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { - // 'none' is not a valid value for , - // so it's not acceptable in the function above. - property = Some(None); - continue; - } - } - - parsed -= 1; - break - } - - if parsed != 0 { - Ok(SingleTransition { - % for prop in "duration timing_function delay".split(): - transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value - ::get_initial_specified_value), - % endfor - transition_property: property.unwrap_or( - Some(transition_property::single_value::get_initial_specified_value())), - }) - } else { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - } - - % for prop in "property duration timing_function delay".split(): - let mut ${prop}s = Vec::new(); - % endfor - - let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?; - let multiple_items = results.len() >= 2; - for result in results { - if let Some(value) = result.transition_property { - propertys.push(value); - } else if multiple_items { - // If there is more than one item, and any of transitions has 'none', - // then it's invalid. Othersize, leave propertys to be empty (which - // means "transition-property: none"); - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - % for prop in "duration timing_function delay".split(): - ${prop}s.push(result.transition_${prop}); - % endfor - } - - Ok(expanded! { - % for prop in "property duration timing_function delay".split(): - transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()), - % endfor - }) - } - - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { - let property_len = self.transition_property.0.len(); - - // There are two cases that we can do shorthand serialization: - // * when all value lists have the same length, or - // * when transition-property is none, and other value lists have exactly one item. - if property_len == 0 { - % for name in "duration delay timing_function".split(): - if self.transition_${name}.0.len() != 1 { - return Ok(()); - } - % endfor - } else { - % for name in "duration delay timing_function".split(): - if self.transition_${name}.0.len() != property_len { - return Ok(()); - } - % endfor - } - - // Representative length. - let len = self.transition_duration.0.len(); - - for i in 0..len { - if i != 0 { - dest.write_str(", ")?; - } - if property_len == 0 { - dest.write_str("none")?; - } else { - self.transition_property.0[i].to_css(dest)?; - } - % for name in "duration timing_function delay".split(): - dest.write_str(" ")?; - self.transition_${name}.0[i].to_css(dest)?; - % endfor - } - Ok(()) - } - } - - -<%helpers:shorthand name="animation" - engines="gecko servo" - extra_prefixes="moz:layout.css.prefixes.animations webkit" - sub_properties="animation-name animation-duration - animation-timing-function animation-delay - animation-iteration-count animation-direction - animation-fill-mode animation-play-state animation-timeline" - rule_types_allowed="Style" - spec="https://drafts.csswg.org/css-animations/#propdef-animation"> - <% - props = "name timeline duration timing_function delay iteration_count \ - direction fill_mode play_state".split() - %> - % for prop in props: - use crate::properties::longhands::animation_${prop}; - % endfor - - pub fn parse_value<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - struct SingleAnimation { - % for prop in props: - animation_${prop}: animation_${prop}::SingleSpecifiedValue, - % endfor - } - - fn scroll_linked_animations_enabled() -> bool { - #[cfg(feature = "gecko")] - return static_prefs::pref!("layout.css.scroll-linked-animations.enabled"); - #[cfg(feature = "servo")] - return false; - } - - fn parse_one_animation<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - % for prop in props: - let mut ${prop} = None; - % endfor - - let mut parsed = 0; - // NB: Name must be the last one here so that keywords valid for other - // longhands are not interpreted as names. - // - // Also, duration must be before delay, see - // https://drafts.csswg.org/css-animations/#typedef-single-animation - loop { - parsed += 1; - try_parse_one!(context, input, duration, animation_duration); - try_parse_one!(context, input, timing_function, animation_timing_function); - try_parse_one!(context, input, delay, animation_delay); - try_parse_one!(context, input, iteration_count, animation_iteration_count); - try_parse_one!(context, input, direction, animation_direction); - try_parse_one!(context, input, fill_mode, animation_fill_mode); - try_parse_one!(context, input, play_state, animation_play_state); - try_parse_one!(context, input, name, animation_name); - if scroll_linked_animations_enabled() { - try_parse_one!(context, input, timeline, animation_timeline); - } - - parsed -= 1; - break - } - - // If nothing is parsed, this is an invalid entry. - if parsed == 0 { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } else { - Ok(SingleAnimation { - % for prop in props: - animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value - ::get_initial_specified_value), - % endfor - }) - } - } - - % for prop in props: - let mut ${prop}s = vec![]; - % endfor - - let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?; - for result in results.into_iter() { - % for prop in props: - ${prop}s.push(result.animation_${prop}); - % endfor - } - - Ok(expanded! { - % for prop in props: - animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()), - % endfor - }) - } - - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { - let len = self.animation_name.0.len(); - // There should be at least one declared value - if len == 0 { - return Ok(()); - } - - // If any value list length is differs then we don't do a shorthand serialization - // either. - % for name in props[2:]: - if len != self.animation_${name}.0.len() { - return Ok(()) - } - % endfor - - // If the preference of animation-timeline is disabled, `self.animation_timeline` is - // None. - if self.animation_timeline.map_or(false, |v| len != v.0.len()) { - return Ok(()); - } - - for i in 0..len { - if i != 0 { - dest.write_str(", ")?; - } - - % for name in props[2:]: - self.animation_${name}.0[i].to_css(dest)?; - dest.write_str(" ")?; - % endfor - - self.animation_name.0[i].to_css(dest)?; - - // Based on the spec, the default values of other properties must be output in at - // least the cases necessary to distinguish an animation-name. The serialization - // order of animation-timeline is always later than animation-name, so it's fine - // to not serialize it if it is the default value. It's still possible to - // distinguish them (because we always serialize animation-name). - // https://drafts.csswg.org/css-animations-1/#animation - // https://drafts.csswg.org/css-animations-2/#typedef-single-animation - // - // Note: it's also fine to always serialize this. However, it seems Blink - // doesn't serialize default animation-timeline now, so we follow the same rule. - if let Some(ref timeline) = self.animation_timeline { - if !timeline.0[i].is_auto() { - dest.write_char(' ')?; - timeline.0[i].to_css(dest)?; - } - } - } - Ok(()) - } - } - - ${helpers.two_properties_shorthand( "overscroll-behavior", "overscroll-behavior-x", diff --git a/components/style/properties/shorthands/ui.mako.rs b/components/style/properties/shorthands/ui.mako.rs new file mode 100644 index 00000000000..178698edebf --- /dev/null +++ b/components/style/properties/shorthands/ui.mako.rs @@ -0,0 +1,310 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +macro_rules! try_parse_one { + ($context: expr, $input: expr, $var: ident, $prop_module: ident) => { + if $var.is_none() { + if let Ok(value) = $input.try_parse(|i| { + $prop_module::single_value::parse($context, i) + }) { + $var = Some(value); + continue; + } + } + }; +} + +<%helpers:shorthand name="transition" + engines="gecko servo-2013 servo-2020" + extra_prefixes="moz:layout.css.prefixes.transitions webkit" + sub_properties="transition-property transition-duration + transition-timing-function + transition-delay" + spec="https://drafts.csswg.org/css-transitions/#propdef-transition"> + use crate::parser::Parse; + % for prop in "delay duration property timing_function".split(): + use crate::properties::longhands::transition_${prop}; + % endfor + use crate::values::specified::TransitionProperty; + + pub fn parse_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + struct SingleTransition { + % for prop in "duration timing_function delay".split(): + transition_${prop}: transition_${prop}::SingleSpecifiedValue, + % endfor + // Unlike other properties, transition-property uses an Option<> to + // represent 'none' as `None`. + transition_property: Option, + } + + fn parse_one_transition<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + % for prop in "property duration timing_function delay".split(): + let mut ${prop} = None; + % endfor + + let mut parsed = 0; + loop { + parsed += 1; + + try_parse_one!(context, input, duration, transition_duration); + try_parse_one!(context, input, timing_function, transition_timing_function); + try_parse_one!(context, input, delay, transition_delay); + // Must check 'transition-property' after 'transition-timing-function' since + // 'transition-property' accepts any keyword. + if property.is_none() { + if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) { + property = Some(Some(value)); + continue; + } + + if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { + // 'none' is not a valid value for , + // so it's not acceptable in the function above. + property = Some(None); + continue; + } + } + + parsed -= 1; + break + } + + if parsed != 0 { + Ok(SingleTransition { + % for prop in "duration timing_function delay".split(): + transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value + ::get_initial_specified_value), + % endfor + transition_property: property.unwrap_or( + Some(transition_property::single_value::get_initial_specified_value())), + }) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + } + + % for prop in "property duration timing_function delay".split(): + let mut ${prop}s = Vec::new(); + % endfor + + let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?; + let multiple_items = results.len() >= 2; + for result in results { + if let Some(value) = result.transition_property { + propertys.push(value); + } else if multiple_items { + // If there is more than one item, and any of transitions has 'none', + // then it's invalid. Othersize, leave propertys to be empty (which + // means "transition-property: none"); + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + % for prop in "duration timing_function delay".split(): + ${prop}s.push(result.transition_${prop}); + % endfor + } + + Ok(expanded! { + % for prop in "property duration timing_function delay".split(): + transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()), + % endfor + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + let property_len = self.transition_property.0.len(); + + // There are two cases that we can do shorthand serialization: + // * when all value lists have the same length, or + // * when transition-property is none, and other value lists have exactly one item. + if property_len == 0 { + % for name in "duration delay timing_function".split(): + if self.transition_${name}.0.len() != 1 { + return Ok(()); + } + % endfor + } else { + % for name in "duration delay timing_function".split(): + if self.transition_${name}.0.len() != property_len { + return Ok(()); + } + % endfor + } + + // Representative length. + let len = self.transition_duration.0.len(); + + for i in 0..len { + if i != 0 { + dest.write_str(", ")?; + } + if property_len == 0 { + dest.write_str("none")?; + } else { + self.transition_property.0[i].to_css(dest)?; + } + % for name in "duration timing_function delay".split(): + dest.write_str(" ")?; + self.transition_${name}.0[i].to_css(dest)?; + % endfor + } + Ok(()) + } + } + + +<%helpers:shorthand name="animation" + engines="gecko servo-2013 servo-2020" + extra_prefixes="moz:layout.css.prefixes.animations webkit" + sub_properties="animation-name animation-duration + animation-timing-function animation-delay + animation-iteration-count animation-direction + animation-fill-mode animation-play-state animation-timeline" + rule_types_allowed="Style" + spec="https://drafts.csswg.org/css-animations/#propdef-animation"> + <% + props = "name timeline duration timing_function delay iteration_count \ + direction fill_mode play_state".split() + %> + % for prop in props: + use crate::properties::longhands::animation_${prop}; + % endfor + + pub fn parse_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + struct SingleAnimation { + % for prop in props: + animation_${prop}: animation_${prop}::SingleSpecifiedValue, + % endfor + } + + fn parse_one_animation<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + % for prop in props: + let mut ${prop} = None; + % endfor + + let mut parsed = 0; + // NB: Name must be the last one here so that keywords valid for other + // longhands are not interpreted as names. + // + // Also, duration must be before delay, see + // https://drafts.csswg.org/css-animations/#typedef-single-animation + loop { + parsed += 1; + try_parse_one!(context, input, duration, animation_duration); + try_parse_one!(context, input, timing_function, animation_timing_function); + try_parse_one!(context, input, delay, animation_delay); + try_parse_one!(context, input, iteration_count, animation_iteration_count); + try_parse_one!(context, input, direction, animation_direction); + try_parse_one!(context, input, fill_mode, animation_fill_mode); + try_parse_one!(context, input, play_state, animation_play_state); + try_parse_one!(context, input, name, animation_name); + if static_prefs::pref!("layout.css.scroll-linked-animations.enabled") { + try_parse_one!(context, input, timeline, animation_timeline); + } + + parsed -= 1; + break + } + + // If nothing is parsed, this is an invalid entry. + if parsed == 0 { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } else { + Ok(SingleAnimation { + % for prop in props: + animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value + ::get_initial_specified_value), + % endfor + }) + } + } + + % for prop in props: + let mut ${prop}s = vec![]; + % endfor + + let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?; + for result in results.into_iter() { + % for prop in props: + ${prop}s.push(result.animation_${prop}); + % endfor + } + + Ok(expanded! { + % for prop in props: + animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()), + % endfor + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + let len = self.animation_name.0.len(); + // There should be at least one declared value + if len == 0 { + return Ok(()); + } + + // If any value list length is differs then we don't do a shorthand serialization + // either. + % for name in props[2:]: + if len != self.animation_${name}.0.len() { + return Ok(()) + } + % endfor + + // If the preference of animation-timeline is disabled, `self.animation_timeline` is + // None. + if self.animation_timeline.map_or(false, |v| len != v.0.len()) { + return Ok(()); + } + + for i in 0..len { + if i != 0 { + dest.write_str(", ")?; + } + + % for name in props[2:]: + self.animation_${name}.0[i].to_css(dest)?; + dest.write_str(" ")?; + % endfor + + self.animation_name.0[i].to_css(dest)?; + + // Based on the spec, the default values of other properties must be output in at + // least the cases necessary to distinguish an animation-name. The serialization + // order of animation-timeline is always later than animation-name, so it's fine + // to not serialize it if it is the default value. It's still possible to + // distinguish them (because we always serialize animation-name). + // https://drafts.csswg.org/css-animations-1/#animation + // https://drafts.csswg.org/css-animations-2/#typedef-single-animation + // + // Note: it's also fine to always serialize this. However, it seems Blink + // doesn't serialize default animation-timeline now, so we follow the same rule. + if let Some(ref timeline) = self.animation_timeline { + if !timeline.0[i].is_auto() { + dest.write_char(' ')?; + timeline.0[i].to_css(dest)?; + } + } + } + Ok(()) + } + } + diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 749504972a7..94298a653d2 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -626,13 +626,13 @@ impl StyleSharingCache { // // These are things we don't check in the candidate match because they // are either uncommon or expensive. - let box_style = style.style().get_box(); - if box_style.specifies_transitions() { + let ui_style = style.style().get_ui(); + if ui_style.specifies_transitions() { debug!("Failing to insert to the cache: transitions"); return; } - if box_style.specifies_animations() { + if ui_style.specifies_animations() { debug!("Failing to insert to the cache: animations"); return; }