Bug 1357357 - Make the parser of transition-property match the spec.

1. We add a new arm to TransitionProperty, TransitionProperty::Unsupported,
   which contains an Atom, so it's better to remove the Copy trait from
   TransitionProperty.
2. TransitionProperty::Unsupported(Atom) represents any non-animatable, custom,
   or unrecognized property, and we use Atom to store the ident string for
   serialization.
This commit is contained in:
Boris Chiou 2017-04-24 13:22:25 +08:00
parent e5762cb695
commit 02fc1789e8
13 changed files with 139 additions and 56 deletions

View file

@ -272,9 +272,13 @@ impl PropertyAnimation {
let timing_function = box_style.transition_timing_function_mod(transition_index); let timing_function = box_style.transition_timing_function_mod(transition_index);
let duration = box_style.transition_duration_mod(transition_index); let duration = box_style.transition_duration_mod(transition_index);
if let TransitionProperty::Unsupported(_) = transition_property {
return result
}
if transition_property.is_shorthand() { if transition_property.is_shorthand() {
return transition_property.longhands().iter().filter_map(|transition_property| { return transition_property.longhands().iter().filter_map(|transition_property| {
PropertyAnimation::from_transition_property(*transition_property, PropertyAnimation::from_transition_property(transition_property,
timing_function, timing_function,
duration, duration,
old_style, old_style,
@ -284,7 +288,7 @@ impl PropertyAnimation {
if transition_property != TransitionProperty::All { if transition_property != TransitionProperty::All {
if let Some(property_animation) = if let Some(property_animation) =
PropertyAnimation::from_transition_property(transition_property, PropertyAnimation::from_transition_property(&transition_property,
timing_function, timing_function,
duration, duration,
old_style, old_style,
@ -296,7 +300,7 @@ impl PropertyAnimation {
TransitionProperty::each(|transition_property| { TransitionProperty::each(|transition_property| {
if let Some(property_animation) = if let Some(property_animation) =
PropertyAnimation::from_transition_property(transition_property, PropertyAnimation::from_transition_property(&transition_property,
timing_function, timing_function,
duration, duration,
old_style, old_style,
@ -308,15 +312,15 @@ impl PropertyAnimation {
result result
} }
fn from_transition_property(transition_property: TransitionProperty, fn from_transition_property(transition_property: &TransitionProperty,
timing_function: TransitionTimingFunction, timing_function: TransitionTimingFunction,
duration: Time, duration: Time,
old_style: &ComputedValues, old_style: &ComputedValues,
new_style: &ComputedValues) new_style: &ComputedValues)
-> Option<PropertyAnimation> { -> Option<PropertyAnimation> {
debug_assert!(!transition_property.is_shorthand() && debug_assert!(!transition_property.is_shorthand() &&
transition_property != TransitionProperty::All); transition_property != &TransitionProperty::All);
let animated_property = AnimatedProperty::from_transition_property(&transition_property, let animated_property = AnimatedProperty::from_transition_property(transition_property,
old_style, old_style,
new_style); new_style);
@ -702,7 +706,7 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
for transition_property in &animation.properties_changed { for transition_property in &animation.properties_changed {
debug!("update_style_for_animation: scanning prop {:?} for animation \"{}\"", debug!("update_style_for_animation: scanning prop {:?} for animation \"{}\"",
transition_property, name); transition_property, name);
match PropertyAnimation::from_transition_property(*transition_property, match PropertyAnimation::from_transition_property(transition_property,
timing_function, timing_function,
Time::from_seconds(relative_duration as f32), Time::from_seconds(relative_duration as f32),
&from_style, &from_style,

View file

@ -659,6 +659,7 @@ mod bindings {
"StyleBasicShape", "StyleBasicShape",
"StyleBasicShapeType", "StyleBasicShapeType",
"StyleShapeSource", "StyleShapeSource",
"StyleTransition",
"nsCSSFontFaceRule", "nsCSSFontFaceRule",
"nsCSSKeyword", "nsCSSKeyword",
"nsCSSPropertyID", "nsCSSPropertyID",

View file

@ -520,7 +520,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
/// Returns true if we need to update transitions for the specified property on this element. /// Returns true if we need to update transitions for the specified property on this element.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn needs_transitions_update_per_property(&self, fn needs_transitions_update_per_property(&self,
property: TransitionProperty, property: &TransitionProperty,
combined_duration: f32, combined_duration: f32,
before_change_style: &Arc<ComputedValues>, before_change_style: &Arc<ComputedValues>,
after_change_style: &Arc<ComputedValues>, after_change_style: &Arc<ComputedValues>,

View file

@ -811,7 +811,7 @@ impl<'le> TElement for GeckoElement<'le> {
continue; continue;
} }
let mut property_check_helper = |property: TransitionProperty| -> bool { let mut property_check_helper = |property: &TransitionProperty| -> bool {
if self.needs_transitions_update_per_property(property, if self.needs_transitions_update_per_property(property,
combined_duration, combined_duration,
before_change_style, before_change_style,
@ -821,7 +821,9 @@ impl<'le> TElement for GeckoElement<'le> {
} }
if let Some(set) = transitions_to_keep.as_mut() { if let Some(set) = transitions_to_keep.as_mut() {
set.insert(property); // The TransitionProperty here must be animatable, so cloning it is cheap
// because it is an integer-like enum.
set.insert(property.clone());
} }
false false
}; };
@ -835,12 +837,12 @@ impl<'le> TElement for GeckoElement<'le> {
}); });
if is_shorthand { if is_shorthand {
let shorthand: TransitionProperty = property.into(); let shorthand: TransitionProperty = property.into();
if shorthand.longhands().iter().any(|&p| property_check_helper(p)) { if shorthand.longhands().iter().any(|p| property_check_helper(p)) {
return true; return true;
} }
} else { } else {
if animated_properties::nscsspropertyid_is_animatable(property) && if animated_properties::nscsspropertyid_is_animatable(property) &&
property_check_helper(property.into()) { property_check_helper(&property.into()) {
return true; return true;
} }
} }
@ -855,7 +857,7 @@ impl<'le> TElement for GeckoElement<'le> {
} }
fn needs_transitions_update_per_property(&self, fn needs_transitions_update_per_property(&self,
property: TransitionProperty, property: &TransitionProperty,
combined_duration: f32, combined_duration: f32,
before_change_style: &Arc<ComputedValues>, before_change_style: &Arc<ComputedValues>,
after_change_style: &Arc<ComputedValues>, after_change_style: &Arc<ComputedValues>,
@ -869,17 +871,17 @@ impl<'le> TElement for GeckoElement<'le> {
return false; return false;
} }
if existing_transitions.contains_key(&property) { if existing_transitions.contains_key(property) {
// If there is an existing transition, update only if the end value differs. // If there is an existing transition, update only if the end value differs.
// If the end value has not changed, we should leave the currently running // If the end value has not changed, we should leave the currently running
// transition as-is since we don't want to interrupt its timing function. // transition as-is since we don't want to interrupt its timing function.
let after_value = let after_value =
Arc::new(AnimationValue::from_computed_values(&property, after_change_style)); Arc::new(AnimationValue::from_computed_values(property, after_change_style));
return existing_transitions.get(&property).unwrap() != &after_value; return existing_transitions.get(property).unwrap() != &after_value;
} }
combined_duration > 0.0f32 && combined_duration > 0.0f32 &&
AnimatedProperty::from_transition_property(&property, AnimatedProperty::from_transition_property(property,
before_change_style, before_change_style,
after_change_style).does_animate() after_change_style).does_animate()
} }

View file

@ -39,6 +39,7 @@ use gecko_bindings::structs::SheetParsingMode;
use gecko_bindings::structs::StyleBasicShape; use gecko_bindings::structs::StyleBasicShape;
use gecko_bindings::structs::StyleBasicShapeType; use gecko_bindings::structs::StyleBasicShapeType;
use gecko_bindings::structs::StyleShapeSource; use gecko_bindings::structs::StyleShapeSource;
use gecko_bindings::structs::StyleTransition;
use gecko_bindings::structs::nsCSSFontFaceRule; use gecko_bindings::structs::nsCSSFontFaceRule;
use gecko_bindings::structs::nsCSSKeyword; use gecko_bindings::structs::nsCSSKeyword;
use gecko_bindings::structs::nsCSSPropertyID; use gecko_bindings::structs::nsCSSPropertyID;
@ -692,6 +693,11 @@ extern "C" {
aProperty: nsCSSPropertyID) aProperty: nsCSSPropertyID)
-> RawServoAnimationValueBorrowedOrNull; -> RawServoAnimationValueBorrowedOrNull;
} }
extern "C" {
pub fn Gecko_StyleTransition_SetUnsupportedProperty(aTransition:
*mut StyleTransition,
aAtom: *mut nsIAtom);
}
extern "C" { extern "C" {
pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32) pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32)
-> *mut nsIAtom; -> *mut nsIAtom;

View file

@ -259,8 +259,8 @@ fn get_animated_properties(keyframes: &[Arc<Locked<Keyframe>>], guard: &SharedRw
if let Some(property) = TransitionProperty::from_declaration(declaration) { if let Some(property) = TransitionProperty::from_declaration(declaration) {
if !seen.has_transition_property_bit(&property) { if !seen.has_transition_property_bit(&property) {
ret.push(property);
seen.set_transition_property_bit(&property); seen.set_transition_property_bit(&property);
ret.push(property);
} }
} }
} }

View file

@ -33,6 +33,7 @@ use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed;
use gecko_bindings::bindings::Gecko_FontFamilyList_Clear; use gecko_bindings::bindings::Gecko_FontFamilyList_Clear;
use gecko_bindings::bindings::Gecko_SetCursorArrayLength; use gecko_bindings::bindings::Gecko_SetCursorArrayLength;
use gecko_bindings::bindings::Gecko_SetCursorImage; use gecko_bindings::bindings::Gecko_SetCursorImage;
use gecko_bindings::bindings::Gecko_StyleTransition_SetUnsupportedProperty;
use gecko_bindings::bindings::Gecko_NewCSSShadowArray; use gecko_bindings::bindings::Gecko_NewCSSShadowArray;
use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
@ -53,6 +54,7 @@ use gecko::values::convert_rgba_to_nscolor;
use gecko::values::GeckoStyleCoordConvertible; use gecko::values::GeckoStyleCoordConvertible;
use gecko::values::round_border_to_device_pixels; use gecko::values::round_border_to_device_pixels;
use logical_geometry::WritingMode; use logical_geometry::WritingMode;
use properties::animated_properties::TransitionProperty;
use properties::longhands; use properties::longhands;
use properties::{Importance, LonghandId}; use properties::{Importance, LonghandId};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId}; use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
@ -2149,7 +2151,12 @@ fn static_assert() {
unsafe { self.gecko.mTransitions.ensure_len(v.0.len()) }; unsafe { self.gecko.mTransitions.ensure_len(v.0.len()) };
self.gecko.mTransitionPropertyCount = v.0.len() as u32; self.gecko.mTransitionPropertyCount = v.0.len() as u32;
for (servo, gecko) in v.0.into_iter().zip(self.gecko.mTransitions.iter_mut()) { for (servo, gecko) in v.0.into_iter().zip(self.gecko.mTransitions.iter_mut()) {
gecko.mProperty = servo.into(); match servo {
TransitionProperty::Unsupported(ref atom) => unsafe {
Gecko_StyleTransition_SetUnsupportedProperty(gecko, atom.as_ptr())
},
_ => gecko.mProperty = (&servo).into(),
}
} }
} else { } else {
// In gecko |none| is represented by eCSSPropertyExtra_no_properties. // In gecko |none| is represented by eCSSPropertyExtra_no_properties.
@ -2172,7 +2179,22 @@ fn static_assert() {
pub fn transition_property_at(&self, index: usize) pub fn transition_property_at(&self, index: usize)
-> longhands::transition_property::computed_value::SingleComputedValue { -> longhands::transition_property::computed_value::SingleComputedValue {
self.gecko.mTransitions[index].mProperty.into() use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
let property = self.gecko.mTransitions[index].mProperty;
if property == eCSSProperty_UNKNOWN || property == eCSSPropertyExtra_variable {
let atom = self.gecko.mTransitions[index].mUnknownProperty.raw();
debug_assert!(!atom.is_null());
TransitionProperty::Unsupported(atom.into())
} 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.
TransitionProperty::Unsupported(atom!("none"))
} else {
property.into()
}
} }
pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID { pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID {
@ -2180,6 +2202,8 @@ fn static_assert() {
} }
pub fn copy_transition_property_from(&mut self, other: &Self) { pub fn copy_transition_property_from(&mut self, other: &Self) {
use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
unsafe { self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len()) }; unsafe { self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len()) };
let count = other.gecko.mTransitionPropertyCount; let count = other.gecko.mTransitionPropertyCount;
@ -2187,6 +2211,12 @@ fn static_assert() {
for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) { for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) {
transition.mProperty = other.gecko.mTransitions[index].mProperty; transition.mProperty = other.gecko.mTransitions[index].mProperty;
if transition.mProperty == eCSSProperty_UNKNOWN ||
transition.mProperty == eCSSPropertyExtra_variable {
let atom = other.gecko.mTransitions[index].mUnknownProperty.raw();
debug_assert!(!atom.is_null());
unsafe { Gecko_StyleTransition_SetUnsupportedProperty(transition, atom) };
}
} }
} }
${impl_transition_count('property', 'Property')} ${impl_transition_count('property', 'Property')}

View file

@ -7,9 +7,10 @@
<% from data import SYSTEM_FONT_LONGHANDS %> <% from data import SYSTEM_FONT_LONGHANDS %>
use app_units::Au; use app_units::Au;
use cssparser::{Color as CSSParserColor, Parser, RGBA}; use cssparser::{Color as CSSParserColor, Parser, RGBA, serialize_identifier};
use euclid::{Point2D, Size2D}; use euclid::{Point2D, Size2D};
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
use properties::{CSSWideKeyword, PropertyDeclaration}; use properties::{CSSWideKeyword, PropertyDeclaration};
use properties::longhands; use properties::longhands;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList; use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
@ -25,6 +26,7 @@ use properties::longhands::transform::computed_value::T as TransformList;
use properties::longhands::vertical_align::computed_value::T as VerticalAlign; use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
use properties::longhands::visibility::computed_value::T as Visibility; use properties::longhands::visibility::computed_value::T as Visibility;
#[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId}; #[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
#[cfg(feature = "servo")] use servo_atoms::Atom;
use std::cmp; use std::cmp;
#[cfg(feature = "gecko")] use std::collections::HashMap; #[cfg(feature = "gecko")] use std::collections::HashMap;
use std::fmt; use std::fmt;
@ -45,7 +47,7 @@ use values::generics::position as generic_position;
/// property. /// property.
// NB: This needs to be here because it needs all the longhands generated // NB: This needs to be here because it needs all the longhands generated
// beforehand. // beforehand.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum TransitionProperty { pub enum TransitionProperty {
/// All, any animatable property changing should generate a transition. /// All, any animatable property changing should generate a transition.
@ -62,24 +64,27 @@ pub enum TransitionProperty {
/// ${prop.name} /// ${prop.name}
${prop.camel_case}, ${prop.camel_case},
% endfor % endfor
/// Unrecognized property which could be any non-animatable, custom property, or
/// unknown property.
Unsupported(Atom)
} }
impl TransitionProperty { impl TransitionProperty {
/// Iterates over each longhand property. /// Iterates over each longhand property.
pub fn each<F: FnMut(TransitionProperty) -> ()>(mut cb: F) { pub fn each<F: FnMut(&TransitionProperty) -> ()>(mut cb: F) {
% for prop in data.longhands: % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
cb(TransitionProperty::${prop.camel_case}); cb(&TransitionProperty::${prop.camel_case});
% endif % endif
% endfor % endfor
} }
/// Iterates over every property that is not TransitionProperty::All, stopping and returning /// Iterates over every property that is not TransitionProperty::All, stopping and returning
/// true when the provided callback returns true for the first time. /// true when the provided callback returns true for the first time.
pub fn any<F: FnMut(TransitionProperty) -> bool>(mut cb: F) -> bool { pub fn any<F: FnMut(&TransitionProperty) -> bool>(mut cb: F) -> bool {
% for prop in data.longhands: % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
if cb(TransitionProperty::${prop.camel_case}) { if cb(&TransitionProperty::${prop.camel_case}) {
return true; return true;
} }
% endif % endif
@ -89,7 +94,8 @@ impl TransitionProperty {
/// Parse a transition-property value. /// Parse a transition-property value.
pub fn parse(input: &mut Parser) -> Result<Self, ()> { pub fn parse(input: &mut Parser) -> Result<Self, ()> {
match_ignore_ascii_case! { &try!(input.expect_ident()), let ident = try!(input.expect_ident());
match_ignore_ascii_case! { &ident,
"all" => Ok(TransitionProperty::All), "all" => Ok(TransitionProperty::All),
% for prop in data.longhands: % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
@ -99,7 +105,13 @@ impl TransitionProperty {
% for prop in data.shorthands_except_all(): % for prop in data.shorthands_except_all():
"${prop.name}" => Ok(TransitionProperty::${prop.camel_case}), "${prop.name}" => Ok(TransitionProperty::${prop.camel_case}),
% endfor % endfor
_ => Err(()) "none" => Err(()),
_ => {
match CSSWideKeyword::from_ident(&ident) {
Some(_) => Err(()),
None => Ok(TransitionProperty::Unsupported((&*ident).into()))
}
}
} }
} }
@ -199,6 +211,11 @@ impl ToCss for TransitionProperty {
% for prop in data.shorthands_except_all(): % for prop in data.shorthands_except_all():
TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"), TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"),
% endfor % endfor
#[cfg(feature = "gecko")]
TransitionProperty::Unsupported(ref atom) => serialize_identifier(&atom.to_string(),
dest),
#[cfg(feature = "servo")]
TransitionProperty::Unsupported(ref atom) => serialize_identifier(atom, dest),
} }
} }
} }
@ -206,9 +223,9 @@ impl ToCss for TransitionProperty {
/// Convert to nsCSSPropertyID. /// Convert to nsCSSPropertyID.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
impl From<TransitionProperty> for nsCSSPropertyID { impl<'a> From< &'a TransitionProperty> for nsCSSPropertyID {
fn from(transition_property: TransitionProperty) -> nsCSSPropertyID { fn from(transition_property: &'a TransitionProperty) -> nsCSSPropertyID {
match transition_property { match *transition_property {
% for prop in data.longhands: % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
TransitionProperty::${prop.camel_case} TransitionProperty::${prop.camel_case}
@ -220,6 +237,7 @@ impl From<TransitionProperty> for nsCSSPropertyID {
=> ${helpers.to_nscsspropertyid(prop.ident)}, => ${helpers.to_nscsspropertyid(prop.ident)},
% endfor % endfor
TransitionProperty::All => nsCSSPropertyID::eCSSPropertyExtra_all_properties, TransitionProperty::All => nsCSSPropertyID::eCSSPropertyExtra_all_properties,
_ => panic!("Unconvertable Servo transition property: {:?}", transition_property),
} }
} }
} }
@ -234,6 +252,9 @@ impl From<nsCSSPropertyID> for TransitionProperty {
% if prop.animatable: % if prop.animatable:
${helpers.to_nscsspropertyid(prop.ident)} ${helpers.to_nscsspropertyid(prop.ident)}
=> TransitionProperty::${prop.camel_case}, => TransitionProperty::${prop.camel_case},
% else:
${helpers.to_nscsspropertyid(prop.ident)}
=> TransitionProperty::Unsupported(Atom::from("${prop.ident}")),
% endif % endif
% endfor % endfor
% for prop in data.shorthands_except_all(): % for prop in data.shorthands_except_all():
@ -241,7 +262,7 @@ impl From<nsCSSPropertyID> for TransitionProperty {
=> TransitionProperty::${prop.camel_case}, => TransitionProperty::${prop.camel_case},
% endfor % endfor
nsCSSPropertyID::eCSSPropertyExtra_all_properties => TransitionProperty::All, nsCSSPropertyID::eCSSPropertyExtra_all_properties => TransitionProperty::All,
_ => panic!("Unsupported Servo transition property: {:?}", property), _ => panic!("Unconvertable nsCSSPropertyID: {:?}", property),
} }
} }
} }
@ -356,7 +377,7 @@ impl AnimatedProperty {
} }
% endif % endif
% endfor % endfor
other => panic!("Can't use TransitionProperty::{:?} here", other), ref other => panic!("Can't use TransitionProperty::{:?} here", other),
} }
} }
} }
@ -544,7 +565,7 @@ impl AnimationValue {
} }
% endif % endif
% endfor % endfor
other => panic!("Can't use TransitionProperty::{:?} here.", other), ref other => panic!("Can't use TransitionProperty::{:?} here.", other),
} }
} }
} }

View file

@ -258,7 +258,7 @@ impl LonghandIdSet {
TransitionProperty::${prop.camel_case} => self.insert(LonghandId::${prop.camel_case}), TransitionProperty::${prop.camel_case} => self.insert(LonghandId::${prop.camel_case}),
% endif % endif
% endfor % endfor
other => unreachable!("Tried to set TransitionProperty::{:?} in a PropertyBitfield", other), ref other => unreachable!("Tried to set TransitionProperty::{:?} in a PropertyBitfield", other),
} }
} }
@ -271,7 +271,7 @@ impl LonghandIdSet {
TransitionProperty::${prop.camel_case} => self.contains(LonghandId::${prop.camel_case}), TransitionProperty::${prop.camel_case} => self.contains(LonghandId::${prop.camel_case}),
% endif % endif
% endfor % endfor
other => unreachable!("Tried to get TransitionProperty::{:?} in a PropertyBitfield", other), ref other => unreachable!("Tried to get TransitionProperty::{:?} in a PropertyBitfield", other),
} }
} }
} }

View file

@ -98,10 +98,12 @@ macro_rules! try_parse_one {
loop { loop {
parsed += 1; parsed += 1;
try_parse_one!(input, property, transition_property);
try_parse_one!(context, input, duration, transition_duration); try_parse_one!(context, input, duration, transition_duration);
try_parse_one!(context, input, timing_function, transition_timing_function); try_parse_one!(context, input, timing_function, transition_timing_function);
try_parse_one!(context, input, delay, transition_delay); try_parse_one!(context, input, delay, transition_delay);
// Must check 'transition-property' after 'transition-timing-function' since
// 'transition-property' accepts any keyword.
try_parse_one!(input, property, transition_property);
parsed -= 1; parsed -= 1;
break break

View file

@ -1985,7 +1985,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
// This is safe since we immediately write to the uninitialized values. // This is safe since we immediately write to the uninitialized values.
unsafe { animation_values.set_len((i + 1) as u32) }; unsafe { animation_values.set_len((i + 1) as u32) };
seen.set_transition_property_bit(&anim.0); seen.set_transition_property_bit(&anim.0);
animation_values[i].mProperty = anim.0.into(); animation_values[i].mProperty = (&anim.0).into();
// We only make sure we have enough space for this variable, // We only make sure we have enough space for this variable,
// but didn't construct a default value for StyleAnimationValue, // but didn't construct a default value for StyleAnimationValue,
// so we should zero it to avoid getting undefined behaviors. // so we should zero it to avoid getting undefined behaviors.
@ -2056,7 +2056,7 @@ pub extern "C" fn Servo_StyleSet_FillKeyframesForName(raw_data: RawServoStyleSet
let block = style.to_declaration_block(property.clone().into()); let block = style.to_declaration_block(property.clone().into());
unsafe { unsafe {
(*keyframe).mPropertyValues.set_len((index + 1) as u32); (*keyframe).mPropertyValues.set_len((index + 1) as u32);
(*keyframe).mPropertyValues[index].mProperty = property.clone().into(); (*keyframe).mPropertyValues[index].mProperty = property.into();
// FIXME. Do not set computed values once we handles missing keyframes // FIXME. Do not set computed values once we handles missing keyframes
// with additive composition. // with additive composition.
(*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky( (*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky(
@ -2087,7 +2087,7 @@ pub extern "C" fn Servo_StyleSet_FillKeyframesForName(raw_data: RawServoStyleSet
unsafe { unsafe {
let property = TransitionProperty::from_declaration(declaration).unwrap(); let property = TransitionProperty::from_declaration(declaration).unwrap();
(*keyframe).mPropertyValues.set_len((index + 1) as u32); (*keyframe).mPropertyValues.set_len((index + 1) as u32);
(*keyframe).mPropertyValues[index].mProperty = property.into(); (*keyframe).mPropertyValues[index].mProperty = (&property).into();
(*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky( (*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky(
Arc::new(global_style_data.shared_lock.wrap( Arc::new(global_style_data.shared_lock.wrap(
PropertyDeclarationBlock::with_one( PropertyDeclarationBlock::with_one(

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use parsing::parse; use parsing::parse;
use servo_atoms::Atom;
use style::properties::animated_properties::TransitionProperty; use style::properties::animated_properties::TransitionProperty;
use style::properties::longhands::transition_property; use style::properties::longhands::transition_property;
use style::properties::shorthands::transition; use style::properties::shorthands::transition;
@ -13,16 +14,21 @@ fn test_longhand_properties() {
assert_roundtrip_with_context!(transition_property::parse, "margin-left"); assert_roundtrip_with_context!(transition_property::parse, "margin-left");
assert_roundtrip_with_context!(transition_property::parse, "background-color"); assert_roundtrip_with_context!(transition_property::parse, "background-color");
assert_roundtrip_with_context!(transition_property::parse, "width"); assert_roundtrip_with_context!(transition_property::parse, "width");
assert_roundtrip_with_context!(transition_property::parse, "transition-duration");
assert_roundtrip_with_context!(transition_property::parse, "unsupported-property");
assert_roundtrip_with_context!(transition_property::parse, "-other-unsupported-property");
assert_roundtrip_with_context!(transition_property::parse, "--var");
assert_eq!(parse_longhand!(transition_property, "margin-left, width"), assert_eq!(parse_longhand!(transition_property, "margin-left, transition-delay, width, --var"),
transition_property::SpecifiedValue( transition_property::SpecifiedValue(
vec![TransitionProperty::MarginLeft, vec![TransitionProperty::MarginLeft,
TransitionProperty::Width])); TransitionProperty::Unsupported(Atom::from("transition-delay")),
TransitionProperty::Width,
TransitionProperty::Unsupported(Atom::from("--var"))]));
// TODO: If one of the identifiers listed is not a recognized property name or is not an assert!(parse(transition_property::parse, ".width").is_err());
// animatable property, the implementation must still start transitions on the animatable assert!(parse(transition_property::parse, "1width").is_err());
// properties. Therefore, the parser shouldn't return Err for non-animatable property. assert!(parse(transition_property::parse, "- ").is_err());
assert!(parse(transition_property::parse, "transition-duration").is_err());
} }
#[test] #[test]
@ -44,11 +50,22 @@ fn test_keywords() {
assert_eq!(parse_longhand!(transition_property, "all"), assert_eq!(parse_longhand!(transition_property, "all"),
transition_property::SpecifiedValue(vec![TransitionProperty::All])); transition_property::SpecifiedValue(vec![TransitionProperty::All]));
assert_eq!(parse_longhand!(transition_property, "width, all"),
transition_property::SpecifiedValue(vec![TransitionProperty::Width,
TransitionProperty::All]));
// Using CSS Wide Keyword or none in the list will get the syntax invalid.
// Note: Only "none" alone is valid.
assert!(parse(transition_property::parse, "none").is_ok());
assert_eq!(parse_longhand!(transition_property, "none"), assert_eq!(parse_longhand!(transition_property, "none"),
transition_property::SpecifiedValue(vec![])); transition_property::SpecifiedValue(vec![]));
assert!(parse(transition_property::parse, "inherit").is_err()); assert!(parse(transition_property::parse, "inherit").is_err());
assert!(parse(transition_property::parse, "initial").is_err()); assert!(parse(transition_property::parse, "initial").is_err());
assert!(parse(transition_property::parse, "unset").is_err());
assert!(parse(transition_property::parse, "width, none").is_err());
assert!(parse(transition_property::parse, "width, initial").is_err());
assert!(parse(transition_property::parse, "width, inherit").is_err());
assert!(parse(transition_property::parse, "width, unset").is_err());
} }
#[test] #[test]
@ -61,7 +78,16 @@ fn test_transition_shorthand() {
assert_eq!(result.transition_property, assert_eq!(result.transition_property,
parse_longhand!(transition_property, "margin, all")); parse_longhand!(transition_property, "margin, all"));
let result = parse(transition::parse_value, "2s width, 3s --var, 4s background").unwrap();
assert_eq!(result.transition_property,
parse_longhand!(transition_property, "width, --var, background"));
let result = parse(transition::parse_value, "none").unwrap(); let result = parse(transition::parse_value, "none").unwrap();
assert_eq!(result.transition_property, assert_eq!(result.transition_property,
parse_longhand!(transition_property, "none")); parse_longhand!(transition_property, "none"));
assert!(parse(transition::parse_value, "2s width, none").is_err());
assert!(parse(transition::parse_value, "2s width, 2s initial").is_err());
assert!(parse(transition::parse_value, "2s width, 3s unset").is_err());
assert!(parse(transition::parse_value, "2s width, 4s inherit").is_err());
} }

View file

@ -6,15 +6,6 @@
[parse 'all, none'] [parse 'all, none']
expected: FAIL expected: FAIL
[parse 'foobar']
expected: FAIL
[parse 'all, foobar']
expected: FAIL
[parse 'foobar, all']
expected: FAIL
[parse 'initial'] [parse 'initial']
expected: FAIL expected: FAIL