mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
style: Move transitions and animations to nsStyleUIReset
This mostly just moves code around, to minimize potential behavior changes. There are some cleanups that we should try to do long term (this "have an array with n different counts" is pretty weird). But for now this should unblock people. The destination struct (nsStyleUIReset) was chosen mainly because it's small and non-inherited, and it doesn't seem like a worse place than nsStyleDisplay. Differential Revision: https://phabricator.services.mozilla.com/D144183
This commit is contained in:
parent
fdff95b9c8
commit
76847f7b45
11 changed files with 781 additions and 783 deletions
|
@ -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<ComputedValues>) -> 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<ComputedValues>,
|
||||
) {
|
||||
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<E>(
|
|||
) 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<E>(
|
|||
// 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<E>(
|
|||
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<E>(
|
|||
&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<E>(
|
|||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -254,8 +254,8 @@ trait PrivateMatchMethods: TElement {
|
|||
new_style: &ComputedValues,
|
||||
pseudo_element: Option<PseudoElement>,
|
||||
) -> 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
|
||||
|
|
|
@ -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() {
|
|||
</%def>
|
||||
|
||||
<% 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<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.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<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.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<I>(&mut self, v: I)
|
||||
where
|
||||
I: IntoIterator<Item = values::computed::AnimationIterationCount>,
|
||||
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<I>(&mut self, v: I)
|
||||
where
|
||||
I: IntoIterator<Item = longhands::animation_timeline::computed_value::single_value::T>,
|
||||
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>
|
||||
|
||||
<%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<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.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<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.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<I>(&mut self, v: I)
|
||||
where
|
||||
I: IntoIterator<Item = values::computed::AnimationIterationCount>,
|
||||
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<I>(&mut self, v: I)
|
||||
where
|
||||
I: IntoIterator<Item = longhands::animation_timeline::computed_value::single_value::T>,
|
||||
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>
|
||||
|
||||
<%self:impl_trait style_struct_name="XUL">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
)}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<Longhands, ParseError<'i>> {
|
||||
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<TransitionProperty>,
|
||||
}
|
||||
|
||||
fn parse_one_transition<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<SingleTransition,ParseError<'i>> {
|
||||
% 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 <single-transition-property>,
|
||||
// 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<W>(&self, dest: &mut CssWriter<W>) -> 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>
|
||||
|
||||
<%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<Longhands, ParseError<'i>> {
|
||||
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<SingleAnimation, ParseError<'i>> {
|
||||
% 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<W>(&self, dest: &mut CssWriter<W>) -> 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:shorthand>
|
||||
|
||||
${helpers.two_properties_shorthand(
|
||||
"overscroll-behavior",
|
||||
"overscroll-behavior-x",
|
||||
|
|
310
components/style/properties/shorthands/ui.mako.rs
Normal file
310
components/style/properties/shorthands/ui.mako.rs
Normal file
|
@ -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<Longhands, ParseError<'i>> {
|
||||
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<TransitionProperty>,
|
||||
}
|
||||
|
||||
fn parse_one_transition<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<SingleTransition,ParseError<'i>> {
|
||||
% 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 <single-transition-property>,
|
||||
// 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<W>(&self, dest: &mut CssWriter<W>) -> 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>
|
||||
|
||||
<%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<Longhands, ParseError<'i>> {
|
||||
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<SingleAnimation, ParseError<'i>> {
|
||||
% 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<W>(&self, dest: &mut CssWriter<W>) -> 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:shorthand>
|
|
@ -626,13 +626,13 @@ impl<E: TElement> StyleSharingCache<E> {
|
|||
//
|
||||
// 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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue