aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/animation.rs873
-rw-r--r--components/style/keyframes.rs65
-rw-r--r--components/style/matching.rs5
-rw-r--r--components/style/properties/data.py53
-rw-r--r--components/style/properties/helpers/animated_properties.mako.rs728
-rw-r--r--components/style/properties/longhand/box.mako.rs259
-rw-r--r--components/style/properties/properties.mako.rs26
-rw-r--r--components/style/properties/shorthand/box.mako.rs2
-rw-r--r--components/style/selector_matching.rs17
-rw-r--r--components/style/values.rs24
-rw-r--r--components/style/viewport.rs1
11 files changed, 929 insertions, 1124 deletions
diff --git a/components/style/animation.rs b/components/style/animation.rs
index 3cd994af327..e6e8a3ac89e 100644
--- a/components/style/animation.rs
+++ b/components/style/animation.rs
@@ -6,36 +6,17 @@
use app_units::Au;
use bezier::Bezier;
-use cssparser::{Color, RGBA};
-use dom::{OpaqueNode, TRestyleDamage};
use euclid::point::Point2D;
-use properties::longhands::background_position::computed_value::T as BackgroundPosition;
-use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
-use properties::longhands::clip::computed_value::ClipRect;
-use properties::longhands::font_weight::computed_value::T as FontWeight;
-use properties::longhands::line_height::computed_value::T as LineHeight;
-use properties::longhands::text_shadow::computed_value::T as TextShadowList;
-use properties::longhands::text_shadow::computed_value::TextShadow;
-use properties::longhands::transform::computed_value::ComputedMatrix;
-use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
-use properties::longhands::transform::computed_value::T as TransformList;
-use properties::longhands::transition_property;
-use properties::longhands::transition_property::computed_value::TransitionProperty;
+use dom::{OpaqueNode, TRestyleDamage};
+use properties::animated_properties::{AnimatedProperty, TransitionProperty};
use properties::longhands::transition_timing_function::computed_value::StartEnd;
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
-use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
-use properties::longhands::visibility::computed_value::T as Visibility;
-use properties::longhands::z_index::computed_value::T as ZIndex;
use properties::style_struct_traits::Box;
use properties::{ComputedValues, ServoComputedValues};
-use std::cmp::Ordering;
-use std::iter::repeat;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use time;
-use values::CSSFloat;
-use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
-use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time};
+use values::computed::Time;
/// State relating to an animation.
#[derive(Clone)]
@@ -74,13 +55,18 @@ impl PropertyAnimation {
old_style: &ServoComputedValues,
new_style: &mut ServoComputedValues)
-> Vec<PropertyAnimation> {
- let mut result = Vec::new();
- let transition_property =
- new_style.as_servo().get_box().transition_property.0[transition_index];
+ let mut result = vec![];
+ let box_style = new_style.as_servo().get_box();
+ let transition_property = box_style.transition_property.0[transition_index];
+ let timing_function = *box_style.transition_timing_function.0.get_mod(transition_index);
+ let duration = *box_style.transition_duration.0.get_mod(transition_index);
+
+
if transition_property != TransitionProperty::All {
if let Some(property_animation) =
PropertyAnimation::from_transition_property(transition_property,
- transition_index,
+ timing_function,
+ duration,
old_style,
new_style) {
result.push(property_animation)
@@ -88,114 +74,40 @@ impl PropertyAnimation {
return result
}
- for transition_property in
- transition_property::computed_value::ALL_TRANSITION_PROPERTIES.iter() {
+ TransitionProperty::each(|transition_property| {
if let Some(property_animation) =
- PropertyAnimation::from_transition_property(*transition_property,
- transition_index,
+ PropertyAnimation::from_transition_property(transition_property,
+ timing_function,
+ duration,
old_style,
new_style) {
result.push(property_animation)
}
- }
+ });
result
}
fn from_transition_property(transition_property: TransitionProperty,
- transition_index: usize,
+ timing_function: TransitionTimingFunction,
+ duration: Time,
old_style: &ServoComputedValues,
- new_style: &mut ServoComputedValues)
+ new_style: &ServoComputedValues)
-> Option<PropertyAnimation> {
- let box_style = new_style.get_box();
- macro_rules! match_transition {
- ( $( [$name:ident; $structname:ident; $field:ident] ),* ) => {
- match transition_property {
- TransitionProperty::All => {
- panic!("Don't use `TransitionProperty::All` with \
- `PropertyAnimation::from_transition_property`!")
- }
- $(
- TransitionProperty::$name => {
- AnimatedProperty::$name(old_style.$structname().$field,
- new_style.$structname().$field)
- }
- )*
- TransitionProperty::Clip => {
- AnimatedProperty::Clip(old_style.get_effects().clip.0,
- new_style.get_effects().clip.0)
- }
- TransitionProperty::LetterSpacing => {
- AnimatedProperty::LetterSpacing(old_style.get_inheritedtext().letter_spacing.0,
- new_style.get_inheritedtext().letter_spacing.0)
- }
- TransitionProperty::TextShadow => {
- AnimatedProperty::TextShadow(old_style.get_inheritedtext().text_shadow.clone(),
- new_style.get_inheritedtext().text_shadow.clone())
- }
- TransitionProperty::Transform => {
- AnimatedProperty::Transform(old_style.get_effects().transform.clone(),
- new_style.get_effects().transform.clone())
- }
- TransitionProperty::WordSpacing => {
- AnimatedProperty::WordSpacing(old_style.get_inheritedtext().word_spacing.0,
- new_style.get_inheritedtext().word_spacing.0)
- }
- }
- }
- }
- let animated_property = match_transition!(
- [BackgroundColor; get_background; background_color],
- [BackgroundPosition; get_background; background_position],
- [BorderBottomColor; get_border; border_bottom_color],
- [BorderBottomWidth; get_border; border_bottom_width],
- [BorderLeftColor; get_border; border_left_color],
- [BorderLeftWidth; get_border; border_left_width],
- [BorderRightColor; get_border; border_right_color],
- [BorderRightWidth; get_border; border_right_width],
- [BorderSpacing; get_inheritedtable; border_spacing],
- [BorderTopColor; get_border; border_top_color],
- [BorderTopWidth; get_border; border_top_width],
- [Bottom; get_position; bottom],
- [Color; get_color; color],
- [FontSize; get_font; font_size],
- [FontWeight; get_font; font_weight],
- [Height; get_position; height],
- [Left; get_position; left],
- [LineHeight; get_inheritedtext; line_height],
- [MarginBottom; get_margin; margin_bottom],
- [MarginLeft; get_margin; margin_left],
- [MarginRight; get_margin; margin_right],
- [MarginTop; get_margin; margin_top],
- [MaxHeight; get_position; max_height],
- [MaxWidth; get_position; max_width],
- [MinHeight; get_position; min_height],
- [MinWidth; get_position; min_width],
- [Opacity; get_effects; opacity],
- [OutlineColor; get_outline; outline_color],
- [OutlineWidth; get_outline; outline_width],
- [PaddingBottom; get_padding; padding_bottom],
- [PaddingLeft; get_padding; padding_left],
- [PaddingRight; get_padding; padding_right],
- [PaddingTop; get_padding; padding_top],
- [Right; get_position; right],
- [TextIndent; get_inheritedtext; text_indent],
- [Top; get_position; top],
- [VerticalAlign; get_box; vertical_align],
- [Visibility; get_inheritedbox; visibility],
- [Width; get_position; width],
- [ZIndex; get_position; z_index]);
+ let animated_property = AnimatedProperty::from_transition_property(&transition_property,
+ old_style,
+ new_style);
let property_animation = PropertyAnimation {
property: animated_property,
- timing_function:
- *box_style.transition_timing_function.0.get_mod(transition_index),
- duration: *box_style.transition_duration.0.get_mod(transition_index),
+ timing_function: timing_function,
+ duration: duration,
};
- if property_animation.does_not_animate() {
- None
- } else {
+
+ if property_animation.does_animate() {
Some(property_animation)
+ } else {
+ None
}
}
@@ -215,725 +127,12 @@ impl PropertyAnimation {
}
};
- macro_rules! match_property(
- ( $( [$name:ident; $structname:ident; $field:ident] ),* ) => {
- match self.property {
- $(
- AnimatedProperty::$name(ref start, ref end) => {
- if let Some(value) = start.interpolate(end, progress) {
- style.$structname().$field = value
- }
- }
- )*
- AnimatedProperty::Clip(ref start, ref end) => {
- if let Some(value) = start.interpolate(end, progress) {
- style.mutate_effects().clip.0 = value
- }
- }
- AnimatedProperty::LetterSpacing(ref start, ref end) => {
- if let Some(value) = start.interpolate(end, progress) {
- style.mutate_inheritedtext().letter_spacing.0 = value
- }
- }
- AnimatedProperty::WordSpacing(ref start, ref end) => {
- if let Some(value) = start.interpolate(end, progress) {
- style.mutate_inheritedtext().word_spacing.0 = value
- }
- }
- }
- });
- match_property!(
- [BackgroundColor; mutate_background; background_color],
- [BackgroundPosition; mutate_background; background_position],
- [BorderBottomColor; mutate_border; border_bottom_color],
- [BorderBottomWidth; mutate_border; border_bottom_width],
- [BorderLeftColor; mutate_border; border_left_color],
- [BorderLeftWidth; mutate_border; border_left_width],
- [BorderRightColor; mutate_border; border_right_color],
- [BorderRightWidth; mutate_border; border_right_width],
- [BorderSpacing; mutate_inheritedtable; border_spacing],
- [BorderTopColor; mutate_border; border_top_color],
- [BorderTopWidth; mutate_border; border_top_width],
- [Bottom; mutate_position; bottom],
- [Color; mutate_color; color],
- [FontSize; mutate_font; font_size],
- [FontWeight; mutate_font; font_weight],
- [Height; mutate_position; height],
- [Left; mutate_position; left],
- [LineHeight; mutate_inheritedtext; line_height],
- [MarginBottom; mutate_margin; margin_bottom],
- [MarginLeft; mutate_margin; margin_left],
- [MarginRight; mutate_margin; margin_right],
- [MarginTop; mutate_margin; margin_top],
- [MaxHeight; mutate_position; max_height],
- [MaxWidth; mutate_position; max_width],
- [MinHeight; mutate_position; min_height],
- [MinWidth; mutate_position; min_width],
- [Opacity; mutate_effects; opacity],
- [OutlineColor; mutate_outline; outline_color],
- [OutlineWidth; mutate_outline; outline_width],
- [PaddingBottom; mutate_padding; padding_bottom],
- [PaddingLeft; mutate_padding; padding_left],
- [PaddingRight; mutate_padding; padding_right],
- [PaddingTop; mutate_padding; padding_top],
- [Right; mutate_position; right],
- [TextIndent; mutate_inheritedtext; text_indent],
- [TextShadow; mutate_inheritedtext; text_shadow],
- [Top; mutate_position; top],
- [Transform; mutate_effects; transform],
- [VerticalAlign; mutate_box; vertical_align],
- [Visibility; mutate_inheritedbox; visibility],
- [Width; mutate_position; width],
- [ZIndex; mutate_position; z_index]);
- }
-
- #[inline]
- fn does_not_animate(&self) -> bool {
- self.property.does_not_animate() || self.duration == Time(0.0)
- }
-}
-
-#[derive(Clone, Debug)]
-enum AnimatedProperty {
- BackgroundColor(Color, Color),
- BackgroundPosition(BackgroundPosition, BackgroundPosition),
- BorderBottomColor(Color, Color),
- BorderBottomWidth(Length, Length),
- BorderLeftColor(Color, Color),
- BorderLeftWidth(Length, Length),
- BorderRightColor(Color, Color),
- BorderRightWidth(Length, Length),
- BorderSpacing(BorderSpacing, BorderSpacing),
- BorderTopColor(Color, Color),
- BorderTopWidth(Length, Length),
- Bottom(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- Color(RGBA, RGBA),
- Clip(Option<ClipRect>, Option<ClipRect>),
- FontSize(Length, Length),
- FontWeight(FontWeight, FontWeight),
- Height(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- Left(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- LetterSpacing(Option<Au>, Option<Au>),
- LineHeight(LineHeight, LineHeight),
- MarginBottom(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- MarginLeft(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- MarginRight(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- MarginTop(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- MaxHeight(LengthOrPercentageOrNone, LengthOrPercentageOrNone),
- MaxWidth(LengthOrPercentageOrNone, LengthOrPercentageOrNone),
- MinHeight(LengthOrPercentage, LengthOrPercentage),
- MinWidth(LengthOrPercentage, LengthOrPercentage),
- Opacity(CSSFloat, CSSFloat),
- OutlineColor(Color, Color),
- OutlineWidth(Length, Length),
- PaddingBottom(LengthOrPercentage, LengthOrPercentage),
- PaddingLeft(LengthOrPercentage, LengthOrPercentage),
- PaddingRight(LengthOrPercentage, LengthOrPercentage),
- PaddingTop(LengthOrPercentage, LengthOrPercentage),
- Right(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- TextIndent(LengthOrPercentage, LengthOrPercentage),
- TextShadow(TextShadowList, TextShadowList),
- Top(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- Transform(TransformList, TransformList),
- VerticalAlign(VerticalAlign, VerticalAlign),
- Visibility(Visibility, Visibility),
- Width(LengthOrPercentageOrAuto, LengthOrPercentageOrAuto),
- WordSpacing(Option<Au>, Option<Au>),
- ZIndex(ZIndex, ZIndex),
-}
-
-impl AnimatedProperty {
- #[inline]
- fn does_not_animate(&self) -> bool {
- match *self {
- AnimatedProperty::Top(ref a, ref b) |
- AnimatedProperty::Right(ref a, ref b) |
- AnimatedProperty::Bottom(ref a, ref b) |
- AnimatedProperty::Left(ref a, ref b) |
- AnimatedProperty::MarginTop(ref a, ref b) |
- AnimatedProperty::MarginRight(ref a, ref b) |
- AnimatedProperty::MarginBottom(ref a, ref b) |
- AnimatedProperty::MarginLeft(ref a, ref b) |
- AnimatedProperty::Width(ref a, ref b) |
- AnimatedProperty::Height(ref a, ref b) => a == b,
- AnimatedProperty::MaxWidth(ref a, ref b) |
- AnimatedProperty::MaxHeight(ref a, ref b) => a == b,
- AnimatedProperty::MinWidth(ref a, ref b) |
- AnimatedProperty::MinHeight(ref a, ref b) |
- AnimatedProperty::TextIndent(ref a, ref b) => a == b,
- AnimatedProperty::FontSize(ref a, ref b) |
- AnimatedProperty::BorderTopWidth(ref a, ref b) |
- AnimatedProperty::BorderRightWidth(ref a, ref b) |
- AnimatedProperty::BorderBottomWidth(ref a, ref b) |
- AnimatedProperty::BorderLeftWidth(ref a, ref b) => a == b,
- AnimatedProperty::BorderTopColor(ref a, ref b) |
- AnimatedProperty::BorderRightColor(ref a, ref b) |
- AnimatedProperty::BorderBottomColor(ref a, ref b) |
- AnimatedProperty::BorderLeftColor(ref a, ref b) |
- AnimatedProperty::OutlineColor(ref a, ref b) |
- AnimatedProperty::BackgroundColor(ref a, ref b) => a == b,
- AnimatedProperty::PaddingTop(ref a, ref b) |
- AnimatedProperty::PaddingRight(ref a, ref b) |
- AnimatedProperty::PaddingBottom(ref a, ref b) |
- AnimatedProperty::PaddingLeft(ref a, ref b) => a == b,
- AnimatedProperty::LineHeight(ref a, ref b) => a == b,
- AnimatedProperty::LetterSpacing(ref a, ref b) => a == b,
- AnimatedProperty::BackgroundPosition(ref a, ref b) => a == b,
- AnimatedProperty::BorderSpacing(ref a, ref b) => a == b,
- AnimatedProperty::Clip(ref a, ref b) => a == b,
- AnimatedProperty::Color(ref a, ref b) => a == b,
- AnimatedProperty::FontWeight(ref a, ref b) => a == b,
- AnimatedProperty::Opacity(ref a, ref b) => a == b,
- AnimatedProperty::OutlineWidth(ref a, ref b) => a == b,
- AnimatedProperty::TextShadow(ref a, ref b) => a == b,
- AnimatedProperty::VerticalAlign(ref a, ref b) => a == b,
- AnimatedProperty::Visibility(ref a, ref b) => a == b,
- AnimatedProperty::WordSpacing(ref a, ref b) => a == b,
- AnimatedProperty::ZIndex(ref a, ref b) => a == b,
- AnimatedProperty::Transform(ref a, ref b) => a == b,
- }
- }
-}
-
-/// A trait used to implement [interpolation][interpolated-types].
-///
-/// [interpolated-types]: https://drafts.csswg.org/css-transitions/#interpolated-types
-trait Interpolate: Sized {
- fn interpolate(&self, other: &Self, time: f64) -> Option<Self>;
-}
-
-impl Interpolate for Au {
- #[inline]
- fn interpolate(&self, other: &Au, time: f64) -> Option<Au> {
- Some(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * time).round() as i32))
- }
-}
-
-impl <T> Interpolate for Option<T> where T: Interpolate {
- #[inline]
- fn interpolate(&self, other: &Option<T>, time: f64) -> Option<Option<T>> {
- match (self, other) {
- (&Some(ref this), &Some(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(Some(value))
- })
- }
- (_, _) => None
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Interpolate for f32 {
- #[inline]
- fn interpolate(&self, other: &f32, time: f64) -> Option<f32> {
- Some(((*self as f64) + ((*other as f64) - (*self as f64)) * time) as f32)
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Interpolate for f64 {
- #[inline]
- fn interpolate(&self, other: &f64, time: f64) -> Option<f64> {
- Some(*self + (*other - *self) * time)
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-integer
-impl Interpolate for i32 {
- #[inline]
- fn interpolate(&self, other: &i32, time: f64) -> Option<i32> {
- let a = *self as f64;
- let b = *other as f64;
- Some((a + (b - a) * time).round() as i32)
- }
-}
-
-impl Interpolate for Angle {
- #[inline]
- fn interpolate(&self, other: &Angle, time: f64) -> Option<Angle> {
- self.radians().interpolate(&other.radians(), time).map(Angle)
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-visibility
-impl Interpolate for Visibility {
- #[inline]
- fn interpolate(&self, other: &Visibility, time: f64)
- -> Option<Visibility> {
- match (*self, *other) {
- (Visibility::visible, _) | (_, Visibility::visible) => {
- if time >= 0.0 && time <= 1.0 {
- Some(Visibility::visible)
- } else if time < 0.0 {
- Some(*self)
- } else {
- Some(*other)
- }
- }
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-integer
-impl Interpolate for ZIndex {
- #[inline]
- fn interpolate(&self, other: &ZIndex, time: f64)
- -> Option<ZIndex> {
- match (*self, *other) {
- (ZIndex::Number(ref this),
- ZIndex::Number(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(ZIndex::Number(value))
- })
- }
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-length
-impl Interpolate for VerticalAlign {
- #[inline]
- fn interpolate(&self, other: &VerticalAlign, time: f64)
- -> Option<VerticalAlign> {
- match (*self, *other) {
- (VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)),
- VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => {
- this.interpolate(other, time).and_then(|value| {
- Some(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)))
- })
- }
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
-impl Interpolate for BorderSpacing {
- #[inline]
- fn interpolate(&self, other: &BorderSpacing, time: f64)
- -> Option<BorderSpacing> {
- self.horizontal.interpolate(&other.horizontal, time).and_then(|horizontal| {
- self.vertical.interpolate(&other.vertical, time).and_then(|vertical| {
- Some(BorderSpacing { horizontal: horizontal, vertical: vertical })
- })
- })
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-color
-impl Interpolate for RGBA {
- #[inline]
- fn interpolate(&self, other: &RGBA, time: f64) -> Option<RGBA> {
- match (self.red.interpolate(&other.red, time),
- self.green.interpolate(&other.green, time),
- self.blue.interpolate(&other.blue, time),
- self.alpha.interpolate(&other.alpha, time)) {
- (Some(red), Some(green), Some(blue), Some(alpha)) => {
- Some(RGBA { red: red, green: green, blue: blue, alpha: alpha })
- }
- (_, _, _, _) => None
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-color
-impl Interpolate for Color {
- #[inline]
- fn interpolate(&self, other: &Color, time: f64) -> Option<Color> {
- match (*self, *other) {
- (Color::RGBA(ref this), Color::RGBA(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(Color::RGBA(value))
- })
- }
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Interpolate for CalcLengthOrPercentage {
- #[inline]
- fn interpolate(&self, other: &CalcLengthOrPercentage, time: f64)
- -> Option<CalcLengthOrPercentage> {
- Some(CalcLengthOrPercentage {
- length: self.length().interpolate(&other.length(), time),
- percentage: self.percentage().interpolate(&other.percentage(), time),
- })
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Interpolate for LengthOrPercentage {
- #[inline]
- fn interpolate(&self, other: &LengthOrPercentage, time: f64)
- -> Option<LengthOrPercentage> {
- match (*self, *other) {
- (LengthOrPercentage::Length(ref this),
- LengthOrPercentage::Length(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LengthOrPercentage::Length(value))
- })
- }
- (LengthOrPercentage::Percentage(ref this),
- LengthOrPercentage::Percentage(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LengthOrPercentage::Percentage(value))
- })
- }
- (this, other) => {
- let this: CalcLengthOrPercentage = From::from(this);
- let other: CalcLengthOrPercentage = From::from(other);
- this.interpolate(&other, time).and_then(|value| {
- Some(LengthOrPercentage::Calc(value))
- })
- }
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Interpolate for LengthOrPercentageOrAuto {
- #[inline]
- fn interpolate(&self, other: &LengthOrPercentageOrAuto, time: f64)
- -> Option<LengthOrPercentageOrAuto> {
- match (*self, *other) {
- (LengthOrPercentageOrAuto::Length(ref this),
- LengthOrPercentageOrAuto::Length(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LengthOrPercentageOrAuto::Length(value))
- })
- }
- (LengthOrPercentageOrAuto::Percentage(ref this),
- LengthOrPercentageOrAuto::Percentage(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LengthOrPercentageOrAuto::Percentage(value))
- })
- }
- (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
- Some(LengthOrPercentageOrAuto::Auto)
- }
- (this, other) => {
- let this: Option<CalcLengthOrPercentage> = From::from(this);
- let other: Option<CalcLengthOrPercentage> = From::from(other);
- this.interpolate(&other, time).unwrap_or(None).and_then(|value| {
- Some(LengthOrPercentageOrAuto::Calc(value))
- })
- }
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Interpolate for LengthOrPercentageOrNone {
- #[inline]
- fn interpolate(&self, other: &LengthOrPercentageOrNone, time: f64)
- -> Option<LengthOrPercentageOrNone> {
- match (*self, *other) {
- (LengthOrPercentageOrNone::Length(ref this),
- LengthOrPercentageOrNone::Length(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LengthOrPercentageOrNone::Length(value))
- })
- }
- (LengthOrPercentageOrNone::Percentage(ref this),
- LengthOrPercentageOrNone::Percentage(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LengthOrPercentageOrNone::Percentage(value))
- })
- }
- (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
- Some(LengthOrPercentageOrNone::None)
- }
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-/// https://drafts.csswg.org/css-transitions/#animtype-length
-impl Interpolate for LineHeight {
- #[inline]
- fn interpolate(&self, other: &LineHeight, time: f64)
- -> Option<LineHeight> {
- match (*self, *other) {
- (LineHeight::Length(ref this),
- LineHeight::Length(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LineHeight::Length(value))
- })
- }
- (LineHeight::Number(ref this),
- LineHeight::Number(ref other)) => {
- this.interpolate(other, time).and_then(|value| {
- Some(LineHeight::Number(value))
- })
- }
- (LineHeight::Normal, LineHeight::Normal) => {
- Some(LineHeight::Normal)
- }
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-font-weight
-impl Interpolate for FontWeight {
- #[inline]
- fn interpolate(&self, other: &FontWeight, time: f64)
- -> Option<FontWeight> {
- let a = (*self as u32) as f64;
- let b = (*other as u32) as f64;
- let weight = a + (b - a) * time;
- Some(if weight < 150. {
- FontWeight::Weight100
- } else if weight < 250. {
- FontWeight::Weight200
- } else if weight < 350. {
- FontWeight::Weight300
- } else if weight < 450. {
- FontWeight::Weight400
- } else if weight < 550. {
- FontWeight::Weight500
- } else if weight < 650. {
- FontWeight::Weight600
- } else if weight < 750. {
- FontWeight::Weight700
- } else if weight < 850. {
- FontWeight::Weight800
- } else {
- FontWeight::Weight900
- })
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-rect
-impl Interpolate for ClipRect {
- #[inline]
- fn interpolate(&self, other: &ClipRect, time: f64)
- -> Option<ClipRect> {
- match (self.top.interpolate(&other.top, time),
- self.right.interpolate(&other.right, time),
- self.bottom.interpolate(&other.bottom, time),
- self.left.interpolate(&other.left, time)) {
- (Some(top), Some(right), Some(bottom), Some(left)) => {
- Some(ClipRect { top: top, right: right, bottom: bottom, left: left })
- },
- (_, _, _, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
-impl Interpolate for BackgroundPosition {
- #[inline]
- fn interpolate(&self, other: &BackgroundPosition, time: f64)
- -> Option<BackgroundPosition> {
- match (self.horizontal.interpolate(&other.horizontal, time),
- self.vertical.interpolate(&other.vertical, time)) {
- (Some(horizontal), Some(vertical)) => {
- Some(BackgroundPosition { horizontal: horizontal, vertical: vertical })
- },
- (_, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
-impl Interpolate for TextShadow {
- #[inline]
- fn interpolate(&self, other: &TextShadow, time: f64)
- -> Option<TextShadow> {
- match (self.offset_x.interpolate(&other.offset_x, time),
- self.offset_y.interpolate(&other.offset_y, time),
- self.blur_radius.interpolate(&other.blur_radius, time),
- self.color.interpolate(&other.color, time)) {
- (Some(offset_x), Some(offset_y), Some(blur_radius), Some(color)) => {
- Some(TextShadow { offset_x: offset_x, offset_y: offset_y, blur_radius: blur_radius, color: color })
- },
- (_, _, _, _) => None,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
-impl Interpolate for TextShadowList {
- #[inline]
- fn interpolate(&self, other: &TextShadowList, time: f64)
- -> Option<TextShadowList> {
- let zero = TextShadow {
- offset_x: Au(0),
- offset_y: Au(0),
- blur_radius: Au(0),
- color: Color::RGBA(RGBA {
- red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0
- })
- };
-
- let interpolate_each = |(a, b): (&TextShadow, &TextShadow)| {
- a.interpolate(b, time).unwrap()
- };
-
- Some(TextShadowList(match self.0.len().cmp(&other.0.len()) {
- Ordering::Less => other.0.iter().chain(repeat(&zero)).zip(other.0.iter()).map(interpolate_each).collect(),
- _ => self.0.iter().zip(other.0.iter().chain(repeat(&zero))).map(interpolate_each).collect(),
- }))
- }
-}
-
-/// Check if it's possible to do a direct numerical interpolation
-/// between these two transform lists.
-/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
-fn can_interpolate_list(from_list: &[TransformOperation],
- to_list: &[TransformOperation]) -> bool {
- // Lists must be equal length
- if from_list.len() != to_list.len() {
- return false;
- }
-
- // Each transform operation must match primitive type in other list
- for (from, to) in from_list.iter().zip(to_list) {
- match (from, to) {
- (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
- (&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
- (&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
- (&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
- (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
- (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
- _ => {
- return false;
- }
- }
- }
-
- true
-}
-
-/// Interpolate two transform lists.
-/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
-fn interpolate_transform_list(from_list: &[TransformOperation],
- to_list: &[TransformOperation],
- time: f64) -> TransformList {
- let mut result = vec![];
-
- if can_interpolate_list(from_list, to_list) {
- for (from, to) in from_list.iter().zip(to_list) {
- match (from, to) {
- (&TransformOperation::Matrix(from),
- &TransformOperation::Matrix(_to)) => {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.push(TransformOperation::Matrix(from));
- }
- (&TransformOperation::Skew(fx, fy),
- &TransformOperation::Skew(tx, ty)) => {
- let ix = fx.interpolate(&tx, time).unwrap();
- let iy = fy.interpolate(&ty, time).unwrap();
- result.push(TransformOperation::Skew(ix, iy));
- }
- (&TransformOperation::Translate(fx, fy, fz),
- &TransformOperation::Translate(tx, ty, tz)) => {
- let ix = fx.interpolate(&tx, time).unwrap();
- let iy = fy.interpolate(&ty, time).unwrap();
- let iz = fz.interpolate(&tz, time).unwrap();
- result.push(TransformOperation::Translate(ix, iy, iz));
- }
- (&TransformOperation::Scale(fx, fy, fz),
- &TransformOperation::Scale(tx, ty, tz)) => {
- let ix = fx.interpolate(&tx, time).unwrap();
- let iy = fy.interpolate(&ty, time).unwrap();
- let iz = fz.interpolate(&tz, time).unwrap();
- result.push(TransformOperation::Scale(ix, iy, iz));
- }
- (&TransformOperation::Rotate(fx, fy, fz, fa),
- &TransformOperation::Rotate(_tx, _ty, _tz, _ta)) => {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.push(TransformOperation::Rotate(fx, fy, fz, fa));
- }
- (&TransformOperation::Perspective(fd),
- &TransformOperation::Perspective(_td)) => {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.push(TransformOperation::Perspective(fd));
- }
- _ => {
- // This should be unreachable due to the can_interpolate_list() call.
- unreachable!();
- }
- }
- }
- } else {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.extend_from_slice(from_list);
+ self.property.update(style, progress);
}
- TransformList(Some(result))
-}
-
-/// Build an equivalent 'identity transform function list' based
-/// on an existing transform list.
-/// https://drafts.csswg.org/css-transforms/#none-transform-animation
-fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> {
- let mut result = vec!();
-
- for operation in list {
- match *operation {
- TransformOperation::Matrix(..) => {
- let identity = ComputedMatrix::identity();
- result.push(TransformOperation::Matrix(identity));
- }
- TransformOperation::Skew(..) => {
- result.push(TransformOperation::Skew(Angle(0.0), Angle(0.0)));
- }
- TransformOperation::Translate(..) => {
- result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
- LengthOrPercentage::zero(),
- Au(0)));
- }
- TransformOperation::Scale(..) => {
- result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
- }
- TransformOperation::Rotate(..) => {
- result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0)));
- }
- TransformOperation::Perspective(..) => {
- // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
- let identity = ComputedMatrix::identity();
- result.push(TransformOperation::Matrix(identity));
- }
- }
- }
-
- result
-}
-
-/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
-impl Interpolate for TransformList {
#[inline]
- fn interpolate(&self, other: &TransformList, time: f64) -> Option<TransformList> {
- let result = match (&self.0, &other.0) {
- (&Some(ref from_list), &Some(ref to_list)) => {
- // https://drafts.csswg.org/css-transforms/#transform-transform-animation
- interpolate_transform_list(from_list, &to_list, time)
- }
- (&Some(ref from_list), &None) => {
- // https://drafts.csswg.org/css-transforms/#none-transform-animation
- let to_list = build_identity_transform_list(from_list);
- interpolate_transform_list(from_list, &to_list, time)
- }
- (&None, &Some(ref to_list)) => {
- // https://drafts.csswg.org/css-transforms/#none-transform-animation
- let from_list = build_identity_transform_list(to_list);
- interpolate_transform_list(&from_list, to_list, time)
- }
- _ => {
- // https://drafts.csswg.org/css-transforms/#none-none-animation
- TransformList(None)
- }
- };
-
- Some(result)
+ fn does_animate(&self) -> bool {
+ self.property.does_animate() && self.duration != Time(0.0)
}
}
@@ -948,6 +147,7 @@ pub trait GetMod {
impl<T> GetMod for Vec<T> {
type Item = T;
+ #[inline]
fn get_mod(&self, i: usize) -> &T {
&(*self)[i % self.len()]
}
@@ -991,10 +191,9 @@ pub fn start_transitions_if_applicable<C: ComputedValues>(new_animations_sender:
/// Updates a single animation and associated style based on the current time. If `damage` is
/// provided, inserts the appropriate restyle damage.
-pub fn update_style_for_animation<C: ComputedValues,
- Damage: TRestyleDamage<ConcreteComputedValues=C>>(animation: &Animation,
- style: &mut Arc<C>,
- damage: Option<&mut Damage>) {
+pub fn update_style_for_animation<Damage: TRestyleDamage>(animation: &Animation,
+ style: &mut Arc<Damage::ConcreteComputedValues>,
+ damage: Option<&mut Damage>) {
let now = time::precise_time_s();
let mut progress = (now - animation.start_time) / animation.duration();
if progress > 1.0 {
diff --git a/components/style/keyframes.rs b/components/style/keyframes.rs
index 5f0cf6858be..a558020a748 100644
--- a/components/style/keyframes.rs
+++ b/components/style/keyframes.rs
@@ -4,7 +4,8 @@
use cssparser::{Parser, Delimiter};
use parser::ParserContext;
-use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
+use properties::{ComputedValues, PropertyDeclarationBlock, parse_property_declaration_list};
+use properties::animated_properties::AnimatedProperty;
/// Parses a keyframes list, like:
/// 0%, 50% {
@@ -19,6 +20,11 @@ pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Resul
while !input.is_exhausted() {
keyframes.push(try!(Keyframe::parse(context, input)));
}
+
+ if keyframes.len() < 2 {
+ return Err(())
+ }
+
Ok(keyframes)
}
@@ -89,3 +95,60 @@ impl Keyframe {
})
}
}
+
+/// A single step from a keyframe animation.
+#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
+pub struct ComputedKeyframesStep<C: ComputedValues> {
+ /// The percentage of the animation duration that should be taken for this
+ /// step.
+ duration_percentage: KeyframePercentage,
+ // XXX: Can we optimise this? Probably not such a show-stopper... Probably
+ // storing declared values could work/should be the thing to do?
+ /// The computed values at the beginning of the step.
+ begin: C,
+ /// The computed values at the end of the step.
+ end: C,
+}
+
+/// This structure represents a list of animation steps computed from the list
+/// of keyframes, in order.
+///
+/// It only takes into account animable properties.
+#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
+pub struct ComputedKeyframesAnimation<C: ComputedValues> {
+ steps: Vec<ComputedKeyframesStep<C>>,
+ /// The properties that change in this animation.
+ properties_changed: Vec<AnimatedProperty>,
+}
+
+fn get_animated_properties(keyframe: &Keyframe) -> Vec<AnimatedProperty> {
+ // TODO
+ vec![]
+}
+
+impl<C: ComputedValues> ComputedKeyframesAnimation<C> {
+ pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<ComputedKeyframesAnimation<C>> {
+ debug_assert!(keyframes.len() > 1);
+ let mut steps = vec![];
+
+ // NB: we do two passes, first storing the steps in the order of
+ // appeareance, then sorting them, then updating with the real
+ // "duration_percentage".
+ let mut animated_properties = get_animated_properties(&keyframes[0]);
+
+ if animated_properties.is_empty() {
+ return None;
+ }
+
+ for keyframe in keyframes {
+ for step in keyframe.selector.0.iter() {
+
+ }
+ }
+
+ Some(ComputedKeyframesAnimation {
+ steps: steps,
+ properties_changed: animated_properties,
+ })
+ }
+}
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 2e4b7922f7e..e17af2a2fcb 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -400,7 +400,6 @@ trait PrivateMatchMethods: TNode
shareable,
Some(&***parent_style),
cached_computed_values,
- animations,
context.error_reporter.clone());
cacheable = cacheable && is_cacheable;
this_style = the_style
@@ -411,7 +410,6 @@ trait PrivateMatchMethods: TNode
shareable,
None,
None,
- animations,
context.error_reporter.clone());
cacheable = cacheable && is_cacheable;
this_style = the_style
@@ -482,8 +480,7 @@ trait PrivateMatchMethods: TNode
if had_running_animations {
let mut all_running_animations = context.running_animations.write().unwrap();
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
- animation::update_style_for_animation::<Self::ConcreteComputedValues,
- Self::ConcreteRestyleDamage>(running_animation, style, None);
+ animation::update_style_for_animation::<Self::ConcreteRestyleDamage>(running_animation, style, None);
}
all_running_animations.remove(&this_opaque);
}
diff --git a/components/style/properties/data.py b/components/style/properties/data.py
index 4b2cc717407..eb16f986319 100644
--- a/components/style/properties/data.py
+++ b/components/style/properties/data.py
@@ -16,6 +16,53 @@ def to_camel_case(ident):
return re.sub("(^|_|-)([a-z])", lambda m: m.group(2).upper(), ident.strip("_").strip("-"))
+# https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties
+def is_known_animatable_property(name):
+ return name in [
+ "-moz-outline-radius", "-moz-outline-radius-bottomleft",
+ "-moz-outline-radius-bottomright", "-moz-outline-radius-topleft",
+ "-moz-outline-radius-topright", "-webkit-text-fill-color",
+ "-webkit-text-stroke", "-webkit-text-stroke-color",
+ "-webkit-touch-callout", "all", "backdrop-filter", "background",
+ "background-color", "background-position", "background-size", "border",
+ "border-bottom", "border-bottom-color", "border-bottom-left-radius",
+ "border-bottom-right-radius", "border-bottom-width", "border-color",
+ "border-left", "border-left-color", "border-left-width", "border-radius",
+ "border-right", "border-right-color", "border-right-width", "border-top",
+ "border-top-color", "border-top-left-radius", "border-top-right-radius",
+ "border-top-width", "border-width", "bottom", "box-shadow", "clip",
+ "clip-path", "color", "column-count", "column-gap", "column-rule",
+ "column-rule-color", "column-rule-width", "column-width", "columns",
+ "filter", "flex", "flex-basis", "flex-grow", "flex-shrink", "font",
+ "font-size", "font-size-adjust", "font-stretch", "font-weight",
+ "grid-column-gap", "grid-gap", "grid-row-gap", "height", "left",
+ "letter-spacing", "line-height", "margin", "margin-bottom",
+ "margin-left", "margin-right", "margin-top", "mask", "mask-position",
+ "mask-size", "max-height", "max-width", "min-height", "min-width",
+ "motion-offset", "motion-rotation", "object-position", "opacity",
+ "order", "outline", "outline-color", "outline-offset", "outline-width",
+ "padding", "padding-bottom", "padding-left", "padding-right",
+ "padding-top", "perspective", "perspective-origin", "right",
+ "scroll-snap-coordinate", "scroll-snap-destination",
+ "shape-image-threshold", "shape-margin", "shape-outside",
+ "text-decoration", "text-decoration-color", "text-emphasis",
+ "text-emphasis-color", "text-indent", "text-shadow", "top", "transform",
+ "transform-origin", "vertical-align", "visibility", "width",
+ "word-spacing", "z-index"
+ ]
+
+
+# FIXME: Servo doesn't support some animatable properties yet,those are in the
+# following list, and can be implemented removing it from the list and
+# implementing the Interpolate trait in helpers/animated_properties.mako.rs
+def is_not_supported_animatable_property(name):
+ return name in [
+ "flex-basis", "column-width", "column-height", "column-count",
+ "column-gap", "box-shadow", "clip", "filter", "transform-origin",
+ "perspective-origin", "font-stretch", "letter-spacing", "word-spacing",
+ "text-decoration" ]
+
+
class Keyword(object):
def __init__(self, name, values, gecko_constant_prefix=None,
extra_gecko_values=None, extra_servo_values=None):
@@ -47,7 +94,7 @@ class Keyword(object):
class Longhand(object):
def __init__(self, style_struct, name, derived_from=None, keyword=None,
predefined_type=None, custom_cascade=False, experimental=False, internal=False,
- need_clone=False, gecko_ffi_name=None):
+ need_clone=False, gecko_ffi_name=None, animatable=None):
self.name = name
self.keyword = keyword
self.predefined_type = predefined_type
@@ -60,6 +107,10 @@ class Longhand(object):
self.need_clone = need_clone
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
self.derived_from = (derived_from or "").split()
+ if animatable is not None:
+ self.animatable = animatable
+ else:
+ self.animatable = is_known_animatable_property(name) and not is_not_supported_animatable_property(name)
class Shorthand(object):
diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs
new file mode 100644
index 00000000000..6a637948972
--- /dev/null
+++ b/components/style/properties/helpers/animated_properties.mako.rs
@@ -0,0 +1,728 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use app_units::Au;
+use bezier::Bezier;
+use cssparser::{Color as CSSParserColor, Parser, RGBA, ToCss};
+use dom::{OpaqueNode, TRestyleDamage};
+use euclid::{Point2D, Size2D};
+use properties::longhands;
+use properties::longhands::background_position::computed_value::T as BackgroundPosition;
+use properties::longhands::background_size::computed_value::T as BackgroundSize;
+use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
+use properties::longhands::clip::computed_value::ClipRect;
+use properties::longhands::font_weight::computed_value::T as FontWeight;
+use properties::longhands::line_height::computed_value::T as LineHeight;
+use properties::longhands::text_shadow::computed_value::T as TextShadowList;
+use properties::longhands::text_shadow::computed_value::TextShadow;
+use properties::longhands::transform::computed_value::ComputedMatrix;
+use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
+use properties::longhands::transform::computed_value::T as TransformList;
+use properties::longhands::transition_property;
+use properties::longhands::transition_timing_function::computed_value::StartEnd;
+use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
+use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
+use properties::longhands::visibility::computed_value::T as Visibility;
+use properties::longhands::z_index::computed_value::T as ZIndex;
+use properties::style_struct_traits::*;
+use std::cmp::Ordering;
+use std::fmt;
+use std::iter::repeat;
+use super::ComputedValues;
+use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
+use values::computed::{BorderRadiusSize, LengthOrNone};
+use values::computed::{CalcLengthOrPercentage, Length, LengthOrPercentage, Time};
+
+// NB: This needs to be here because it needs all the longhands generated
+// beforehand.
+#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
+pub enum TransitionProperty {
+ All,
+ % for prop in data.longhands:
+ % if prop.animatable:
+ ${prop.camel_case},
+ % endif
+ % endfor
+}
+
+impl TransitionProperty {
+ /// Iterates over each property that is not `All`.
+ pub fn each<F: FnMut(TransitionProperty) -> ()>(mut cb: F) {
+ % for prop in data.longhands:
+ % if prop.animatable:
+ cb(TransitionProperty::${prop.camel_case});
+ % endif
+ % endfor
+ }
+
+ pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+ match_ignore_ascii_case! { try!(input.expect_ident()),
+ "all" => Ok(TransitionProperty::All),
+ % for prop in data.longhands:
+ % if prop.animatable:
+ "${prop.name}" => Ok(TransitionProperty::${prop.camel_case}),
+ % endif
+ % endfor
+ _ => Err(())
+ }
+ }
+
+}
+
+impl ToCss for TransitionProperty {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *self {
+ TransitionProperty::All => dest.write_str("all"),
+ % for prop in data.longhands:
+ % if prop.animatable:
+ TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"),
+ % endif
+ % endfor
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
+pub enum AnimatedProperty {
+ % for prop in data.longhands:
+ % if prop.animatable:
+ ${prop.camel_case}(longhands::${prop.ident}::computed_value::T,
+ longhands::${prop.ident}::computed_value::T),
+ % endif
+ % endfor
+}
+
+impl AnimatedProperty {
+ pub fn does_animate(&self) -> bool {
+ match *self {
+ % for prop in data.longhands:
+ % if prop.animatable:
+ AnimatedProperty::${prop.camel_case}(ref from, ref to) => from != to,
+ % endif
+ % endfor
+ }
+ }
+
+ pub fn update<C: ComputedValues>(&self, style: &mut C, progress: f64) {
+ match *self {
+ % for prop in data.longhands:
+ % if prop.animatable:
+ AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
+ if let Some(value) = from.interpolate(to, progress) {
+ style.mutate_${prop.style_struct.ident.strip("_")}().set_${prop.ident}(value);
+ }
+ }
+ % endif
+ % endfor
+ }
+ }
+
+ // NB: Transition properties need clone
+ pub fn from_transition_property<C: ComputedValues>(transition_property: &TransitionProperty,
+ old_style: &C,
+ new_style: &C) -> AnimatedProperty {
+ // TODO: Generalise this for GeckoLib, adding clone_xxx to the
+ // appropiate longhands.
+ let old_style = old_style.as_servo();
+ let new_style = new_style.as_servo();
+ match *transition_property {
+ TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
+ % for prop in data.longhands:
+ % if prop.animatable:
+ TransitionProperty::${prop.camel_case} => {
+ AnimatedProperty::${prop.camel_case}(old_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone(),
+ new_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone())
+ }
+ % endif
+ % endfor
+ }
+ }
+}
+
+pub trait Interpolate: Sized {
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self>;
+}
+
+impl Interpolate for Au {
+ #[inline]
+ fn interpolate(&self, other: &Au, time: f64) -> Option<Au> {
+ Some(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * time).round() as i32))
+ }
+}
+
+impl <T> Interpolate for Option<T> where T: Interpolate {
+ #[inline]
+ fn interpolate(&self, other: &Option<T>, time: f64) -> Option<Option<T>> {
+ match (self, other) {
+ (&Some(ref this), &Some(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(Some(value))
+ })
+ }
+ (_, _) => None
+ }
+ }
+}
+
+impl Interpolate for f32 {
+ #[inline]
+ fn interpolate(&self, other: &f32, time: f64) -> Option<f32> {
+ Some(((*self as f64) + ((*other as f64) - (*self as f64)) * time) as f32)
+ }
+}
+
+impl Interpolate for f64 {
+ #[inline]
+ fn interpolate(&self, other: &f64, time: f64) -> Option<f64> {
+ Some(*self + (*other - *self) * time)
+ }
+}
+
+impl Interpolate for i32 {
+ #[inline]
+ fn interpolate(&self, other: &i32, time: f64) -> Option<i32> {
+ let a = *self as f64;
+ let b = *other as f64;
+ Some((a + (b - a) * time).round() as i32)
+ }
+}
+
+impl Interpolate for Angle {
+ #[inline]
+ fn interpolate(&self, other: &Angle, time: f64) -> Option<Angle> {
+ self.radians().interpolate(&other.radians(), time).map(Angle)
+ }
+}
+
+impl Interpolate for Visibility {
+ #[inline]
+ fn interpolate(&self, other: &Visibility, time: f64)
+ -> Option<Visibility> {
+ match (*self, *other) {
+ (Visibility::visible, _) | (_, Visibility::visible) => {
+ if time >= 0.0 && time <= 1.0 {
+ Some(Visibility::visible)
+ } else if time < 0.0 {
+ Some(*self)
+ } else {
+ Some(*other)
+ }
+ }
+ (_, _) => None,
+ }
+ }
+}
+
+impl Interpolate for ZIndex {
+ #[inline]
+ fn interpolate(&self, other: &ZIndex, time: f64)
+ -> Option<ZIndex> {
+ match (*self, *other) {
+ (ZIndex::Number(ref this),
+ ZIndex::Number(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(ZIndex::Number(value))
+ })
+ }
+ (_, _) => None,
+ }
+ }
+}
+
+impl<T: Interpolate + Clone> Interpolate for Size2D<T> {
+ #[inline]
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
+ let width = match self.width.interpolate(&other.width, time) {
+ Some(width) => width,
+ None => return None,
+ };
+
+ let height = match self.height.interpolate(&other.height, time) {
+ Some(height) => height,
+ None => return None,
+ };
+ Some(Size2D::new(width, height))
+ }
+}
+
+impl<T: Interpolate + Clone> Interpolate for Point2D<T> {
+ #[inline]
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
+ let x = match self.x.interpolate(&other.x, time) {
+ Some(x) => x,
+ None => return None,
+ };
+
+ let y = match self.y.interpolate(&other.y, time) {
+ Some(y) => y,
+ None => return None,
+ };
+
+ Some(Point2D::new(x, y))
+ }
+}
+
+impl Interpolate for BorderRadiusSize {
+ #[inline]
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
+ self.0.interpolate(&other.0, time).map(BorderRadiusSize)
+ }
+}
+
+impl Interpolate for VerticalAlign {
+ #[inline]
+ fn interpolate(&self, other: &VerticalAlign, time: f64)
+ -> Option<VerticalAlign> {
+ match (*self, *other) {
+ (VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)),
+ VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)))
+ })
+ }
+ (_, _) => None,
+ }
+ }
+}
+
+impl Interpolate for BorderSpacing {
+ #[inline]
+ fn interpolate(&self, other: &BorderSpacing, time: f64)
+ -> Option<BorderSpacing> {
+ self.horizontal.interpolate(&other.horizontal, time).and_then(|horizontal| {
+ self.vertical.interpolate(&other.vertical, time).and_then(|vertical| {
+ Some(BorderSpacing { horizontal: horizontal, vertical: vertical })
+ })
+ })
+ }
+}
+
+impl Interpolate for RGBA {
+ #[inline]
+ fn interpolate(&self, other: &RGBA, time: f64) -> Option<RGBA> {
+ match (self.red.interpolate(&other.red, time),
+ self.green.interpolate(&other.green, time),
+ self.blue.interpolate(&other.blue, time),
+ self.alpha.interpolate(&other.alpha, time)) {
+ (Some(red), Some(green), Some(blue), Some(alpha)) => {
+ Some(RGBA { red: red, green: green, blue: blue, alpha: alpha })
+ }
+ (_, _, _, _) => None
+ }
+ }
+}
+
+impl Interpolate for CSSParserColor {
+ #[inline]
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
+ match (*self, *other) {
+ (CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(CSSParserColor::RGBA(value))
+ })
+ }
+ (_, _) => None,
+ }
+ }
+}
+
+impl Interpolate for CalcLengthOrPercentage {
+ #[inline]
+ fn interpolate(&self, other: &CalcLengthOrPercentage, time: f64)
+ -> Option<CalcLengthOrPercentage> {
+ Some(CalcLengthOrPercentage {
+ length: self.length().interpolate(&other.length(), time),
+ percentage: self.percentage().interpolate(&other.percentage(), time),
+ })
+ }
+}
+
+impl Interpolate for LengthOrPercentage {
+ #[inline]
+ fn interpolate(&self, other: &LengthOrPercentage, time: f64)
+ -> Option<LengthOrPercentage> {
+ match (*self, *other) {
+ (LengthOrPercentage::Length(ref this),
+ LengthOrPercentage::Length(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LengthOrPercentage::Length(value))
+ })
+ }
+ (LengthOrPercentage::Percentage(ref this),
+ LengthOrPercentage::Percentage(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LengthOrPercentage::Percentage(value))
+ })
+ }
+ (this, other) => {
+ let this: CalcLengthOrPercentage = From::from(this);
+ let other: CalcLengthOrPercentage = From::from(other);
+ this.interpolate(&other, time).and_then(|value| {
+ Some(LengthOrPercentage::Calc(value))
+ })
+ }
+ }
+ }
+}
+
+impl Interpolate for LengthOrPercentageOrAuto {
+ #[inline]
+ fn interpolate(&self, other: &LengthOrPercentageOrAuto, time: f64)
+ -> Option<LengthOrPercentageOrAuto> {
+ match (*self, *other) {
+ (LengthOrPercentageOrAuto::Length(ref this),
+ LengthOrPercentageOrAuto::Length(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LengthOrPercentageOrAuto::Length(value))
+ })
+ }
+ (LengthOrPercentageOrAuto::Percentage(ref this),
+ LengthOrPercentageOrAuto::Percentage(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LengthOrPercentageOrAuto::Percentage(value))
+ })
+ }
+ (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
+ Some(LengthOrPercentageOrAuto::Auto)
+ }
+ (this, other) => {
+ let this: Option<CalcLengthOrPercentage> = From::from(this);
+ let other: Option<CalcLengthOrPercentage> = From::from(other);
+ this.interpolate(&other, time).unwrap_or(None).and_then(|value| {
+ Some(LengthOrPercentageOrAuto::Calc(value))
+ })
+ }
+ }
+ }
+}
+
+impl Interpolate for LengthOrPercentageOrNone {
+ #[inline]
+ fn interpolate(&self, other: &LengthOrPercentageOrNone, time: f64)
+ -> Option<LengthOrPercentageOrNone> {
+ match (*self, *other) {
+ (LengthOrPercentageOrNone::Length(ref this),
+ LengthOrPercentageOrNone::Length(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LengthOrPercentageOrNone::Length(value))
+ })
+ }
+ (LengthOrPercentageOrNone::Percentage(ref this),
+ LengthOrPercentageOrNone::Percentage(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LengthOrPercentageOrNone::Percentage(value))
+ })
+ }
+ (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
+ Some(LengthOrPercentageOrNone::None)
+ }
+ (_, _) => None,
+ }
+ }
+}
+
+impl Interpolate for LineHeight {
+ #[inline]
+ fn interpolate(&self, other: &LineHeight, time: f64)
+ -> Option<LineHeight> {
+ match (*self, *other) {
+ (LineHeight::Length(ref this),
+ LineHeight::Length(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LineHeight::Length(value))
+ })
+ }
+ (LineHeight::Number(ref this),
+ LineHeight::Number(ref other)) => {
+ this.interpolate(other, time).and_then(|value| {
+ Some(LineHeight::Number(value))
+ })
+ }
+ (LineHeight::Normal, LineHeight::Normal) => {
+ Some(LineHeight::Normal)
+ }
+ (_, _) => None,
+ }
+ }
+}
+
+/// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
+impl Interpolate for FontWeight {
+ #[inline]
+ fn interpolate(&self, other: &FontWeight, time: f64)
+ -> Option<FontWeight> {
+ let a = (*self as u32) as f64;
+ let b = (*other as u32) as f64;
+ let weight = a + (b - a) * time;
+ Some(if weight < 150. {
+ FontWeight::Weight100
+ } else if weight < 250. {
+ FontWeight::Weight200
+ } else if weight < 350. {
+ FontWeight::Weight300
+ } else if weight < 450. {
+ FontWeight::Weight400
+ } else if weight < 550. {
+ FontWeight::Weight500
+ } else if weight < 650. {
+ FontWeight::Weight600
+ } else if weight < 750. {
+ FontWeight::Weight700
+ } else if weight < 850. {
+ FontWeight::Weight800
+ } else {
+ FontWeight::Weight900
+ })
+ }
+}
+
+impl Interpolate for ClipRect {
+ #[inline]
+ fn interpolate(&self, other: &ClipRect, time: f64)
+ -> Option<ClipRect> {
+ match (self.top.interpolate(&other.top, time),
+ self.right.interpolate(&other.right, time),
+ self.bottom.interpolate(&other.bottom, time),
+ self.left.interpolate(&other.left, time)) {
+ (Some(top), Some(right), Some(bottom), Some(left)) => {
+ Some(ClipRect { top: top, right: right, bottom: bottom, left: left })
+ },
+ (_, _, _, _) => None,
+ }
+ }
+}
+
+impl Interpolate for BackgroundPosition {
+ #[inline]
+ fn interpolate(&self, other: &BackgroundPosition, time: f64)
+ -> Option<BackgroundPosition> {
+ match (self.horizontal.interpolate(&other.horizontal, time),
+ self.vertical.interpolate(&other.vertical, time)) {
+ (Some(horizontal), Some(vertical)) => {
+ Some(BackgroundPosition { horizontal: horizontal, vertical: vertical })
+ },
+ (_, _) => None,
+ }
+ }
+}
+
+impl Interpolate for BackgroundSize {
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
+ use properties::longhands::background_size::computed_value::ExplicitSize;
+ match (self, other) {
+ (&BackgroundSize::Explicit(ref me), &BackgroundSize::Explicit(ref other))
+ => match (me.width.interpolate(&other.width, time),
+ me.height.interpolate(&other.height, time)) {
+ (Some(width), Some(height))
+ => Some(BackgroundSize::Explicit(
+ ExplicitSize { width: width, height: height })),
+ _ => None,
+ },
+ _ => None
+ }
+ }
+}
+
+impl Interpolate for TextShadow {
+ #[inline]
+ fn interpolate(&self, other: &TextShadow, time: f64)
+ -> Option<TextShadow> {
+ match (self.offset_x.interpolate(&other.offset_x, time),
+ self.offset_y.interpolate(&other.offset_y, time),
+ self.blur_radius.interpolate(&other.blur_radius, time),
+ self.color.interpolate(&other.color, time)) {
+ (Some(offset_x), Some(offset_y), Some(blur_radius), Some(color)) => {
+ Some(TextShadow { offset_x: offset_x, offset_y: offset_y, blur_radius: blur_radius, color: color })
+ },
+ (_, _, _, _) => None,
+ }
+ }
+}
+
+impl Interpolate for TextShadowList {
+ #[inline]
+ fn interpolate(&self, other: &TextShadowList, time: f64)
+ -> Option<TextShadowList> {
+ let zero = TextShadow {
+ offset_x: Au(0),
+ offset_y: Au(0),
+ blur_radius: Au(0),
+ color: CSSParserColor::RGBA(RGBA {
+ red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0
+ })
+ };
+
+ let interpolate_each = |(a, b): (&TextShadow, &TextShadow)| {
+ a.interpolate(b, time).unwrap()
+ };
+
+ Some(TextShadowList(match self.0.len().cmp(&other.0.len()) {
+ Ordering::Less => other.0.iter().chain(repeat(&zero)).zip(other.0.iter()).map(interpolate_each).collect(),
+ _ => self.0.iter().zip(other.0.iter().chain(repeat(&zero))).map(interpolate_each).collect(),
+ }))
+ }
+}
+
+/// Check if it's possible to do a direct numerical interpolation
+/// between these two transform lists.
+/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
+fn can_interpolate_list(from_list: &[TransformOperation],
+ to_list: &[TransformOperation]) -> bool {
+ // Lists must be equal length
+ if from_list.len() != to_list.len() {
+ return false;
+ }
+
+ // Each transform operation must match primitive type in other list
+ for (from, to) in from_list.iter().zip(to_list) {
+ match (from, to) {
+ (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
+ (&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
+ (&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
+ (&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
+ (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
+ (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
+ _ => {
+ return false;
+ }
+ }
+ }
+
+ true
+}
+
+/// Interpolate two transform lists.
+/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
+fn interpolate_transform_list(from_list: &[TransformOperation],
+ to_list: &[TransformOperation],
+ time: f64) -> TransformList {
+ let mut result = vec![];
+
+ if can_interpolate_list(from_list, to_list) {
+ for (from, to) in from_list.iter().zip(to_list) {
+ match (from, to) {
+ (&TransformOperation::Matrix(from),
+ &TransformOperation::Matrix(_to)) => {
+ // TODO(gw): Implement matrix decomposition and interpolation
+ result.push(TransformOperation::Matrix(from));
+ }
+ (&TransformOperation::Skew(fx, fy),
+ &TransformOperation::Skew(tx, ty)) => {
+ let ix = fx.interpolate(&tx, time).unwrap();
+ let iy = fy.interpolate(&ty, time).unwrap();
+ result.push(TransformOperation::Skew(ix, iy));
+ }
+ (&TransformOperation::Translate(fx, fy, fz),
+ &TransformOperation::Translate(tx, ty, tz)) => {
+ let ix = fx.interpolate(&tx, time).unwrap();
+ let iy = fy.interpolate(&ty, time).unwrap();
+ let iz = fz.interpolate(&tz, time).unwrap();
+ result.push(TransformOperation::Translate(ix, iy, iz));
+ }
+ (&TransformOperation::Scale(fx, fy, fz),
+ &TransformOperation::Scale(tx, ty, tz)) => {
+ let ix = fx.interpolate(&tx, time).unwrap();
+ let iy = fy.interpolate(&ty, time).unwrap();
+ let iz = fz.interpolate(&tz, time).unwrap();
+ result.push(TransformOperation::Scale(ix, iy, iz));
+ }
+ (&TransformOperation::Rotate(fx, fy, fz, fa),
+ &TransformOperation::Rotate(_tx, _ty, _tz, _ta)) => {
+ // TODO(gw): Implement matrix decomposition and interpolation
+ result.push(TransformOperation::Rotate(fx, fy, fz, fa));
+ }
+ (&TransformOperation::Perspective(fd),
+ &TransformOperation::Perspective(_td)) => {
+ // TODO(gw): Implement matrix decomposition and interpolation
+ result.push(TransformOperation::Perspective(fd));
+ }
+ _ => {
+ // This should be unreachable due to the can_interpolate_list() call.
+ unreachable!();
+ }
+ }
+ }
+ } else {
+ // TODO(gw): Implement matrix decomposition and interpolation
+ result.extend_from_slice(from_list);
+ }
+
+ TransformList(Some(result))
+}
+
+/// Build an equivalent 'identity transform function list' based
+/// on an existing transform list.
+/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
+fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> {
+ let mut result = vec!();
+
+ for operation in list {
+ match *operation {
+ TransformOperation::Matrix(..) => {
+ let identity = ComputedMatrix::identity();
+ result.push(TransformOperation::Matrix(identity));
+ }
+ TransformOperation::Skew(..) => {
+ result.push(TransformOperation::Skew(Angle(0.0), Angle(0.0)));
+ }
+ TransformOperation::Translate(..) => {
+ result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
+ LengthOrPercentage::zero(),
+ Au(0)));
+ }
+ TransformOperation::Scale(..) => {
+ result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
+ }
+ TransformOperation::Rotate(..) => {
+ result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0)));
+ }
+ TransformOperation::Perspective(..) => {
+ // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
+ let identity = ComputedMatrix::identity();
+ result.push(TransformOperation::Matrix(identity));
+ }
+ }
+ }
+
+ result
+}
+
+impl Interpolate for LengthOrNone {
+ fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
+ match (*self, *other) {
+ (LengthOrNone::Length(ref len), LengthOrNone::Length(ref other)) =>
+ len.interpolate(&other, time).map(LengthOrNone::Length),
+ _ => None,
+ }
+ }
+}
+
+impl Interpolate for TransformList {
+ #[inline]
+ fn interpolate(&self, other: &TransformList, time: f64) -> Option<TransformList> {
+ // http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
+ let result = match (&self.0, &other.0) {
+ (&Some(ref from_list), &Some(ref to_list)) => {
+ // Two lists of transforms
+ interpolate_transform_list(from_list, &to_list, time)
+ }
+ (&Some(ref from_list), &None) => {
+ // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
+ let to_list = build_identity_transform_list(from_list);
+ interpolate_transform_list(from_list, &to_list, time)
+ }
+ (&None, &Some(ref to_list)) => {
+ // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
+ let from_list = build_identity_transform_list(to_list);
+ interpolate_transform_list(&from_list, to_list, time)
+ }
+ _ => {
+ // http://dev.w3.org/csswg/css-transforms/#none-none-animation
+ TransformList(None)
+ }
+ };
+
+ Some(result)
+ }
+}
diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs
index 26014ee77b1..b2eed2758cb 100644
--- a/components/style/properties/longhand/box.mako.rs
+++ b/components/style/properties/longhand/box.mako.rs
@@ -525,170 +525,17 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
}
</%helpers:longhand>
-// TODO(pcwalton): Lots more properties.
<%helpers:longhand name="transition-property">
- use self::computed_value::TransitionProperty;
-
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
-
- pub use self::TransitionProperty as SingleComputedValue;
-
- #[derive(Copy, Clone, Debug, PartialEq)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum TransitionProperty {
- All,
- BackgroundColor,
- BackgroundPosition,
- BorderBottomColor,
- BorderBottomWidth,
- BorderLeftColor,
- BorderLeftWidth,
- BorderRightColor,
- BorderRightWidth,
- BorderSpacing,
- BorderTopColor,
- BorderTopWidth,
- Bottom,
- Color,
- Clip,
- FontSize,
- FontWeight,
- Height,
- Left,
- LetterSpacing,
- LineHeight,
- MarginBottom,
- MarginLeft,
- MarginRight,
- MarginTop,
- MaxHeight,
- MaxWidth,
- MinHeight,
- MinWidth,
- Opacity,
- OutlineColor,
- OutlineWidth,
- PaddingBottom,
- PaddingLeft,
- PaddingRight,
- PaddingTop,
- Right,
- TextIndent,
- TextShadow,
- Top,
- Transform,
- VerticalAlign,
- Visibility,
- Width,
- WordSpacing,
- ZIndex,
- }
-
- pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 45] = [
- TransitionProperty::BackgroundColor,
- TransitionProperty::BackgroundPosition,
- TransitionProperty::BorderBottomColor,
- TransitionProperty::BorderBottomWidth,
- TransitionProperty::BorderLeftColor,
- TransitionProperty::BorderLeftWidth,
- TransitionProperty::BorderRightColor,
- TransitionProperty::BorderRightWidth,
- TransitionProperty::BorderSpacing,
- TransitionProperty::BorderTopColor,
- TransitionProperty::BorderTopWidth,
- TransitionProperty::Bottom,
- TransitionProperty::Color,
- TransitionProperty::Clip,
- TransitionProperty::FontSize,
- TransitionProperty::FontWeight,
- TransitionProperty::Height,
- TransitionProperty::Left,
- TransitionProperty::LetterSpacing,
- TransitionProperty::LineHeight,
- TransitionProperty::MarginBottom,
- TransitionProperty::MarginLeft,
- TransitionProperty::MarginRight,
- TransitionProperty::MarginTop,
- TransitionProperty::MaxHeight,
- TransitionProperty::MaxWidth,
- TransitionProperty::MinHeight,
- TransitionProperty::MinWidth,
- TransitionProperty::Opacity,
- TransitionProperty::OutlineColor,
- TransitionProperty::OutlineWidth,
- TransitionProperty::PaddingBottom,
- TransitionProperty::PaddingLeft,
- TransitionProperty::PaddingRight,
- TransitionProperty::PaddingTop,
- TransitionProperty::Right,
- TransitionProperty::TextIndent,
- TransitionProperty::TextShadow,
- TransitionProperty::Top,
- TransitionProperty::Transform,
- TransitionProperty::VerticalAlign,
- TransitionProperty::Visibility,
- TransitionProperty::Width,
- TransitionProperty::WordSpacing,
- TransitionProperty::ZIndex,
- ];
-
- impl ToCss for TransitionProperty {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match *self {
- TransitionProperty::All => dest.write_str("all"),
- TransitionProperty::BackgroundColor => dest.write_str("background-color"),
- TransitionProperty::BackgroundPosition => dest.write_str("background-position"),
- TransitionProperty::BorderBottomColor => dest.write_str("border-bottom-color"),
- TransitionProperty::BorderBottomWidth => dest.write_str("border-bottom-width"),
- TransitionProperty::BorderLeftColor => dest.write_str("border-left-color"),
- TransitionProperty::BorderLeftWidth => dest.write_str("border-left-width"),
- TransitionProperty::BorderRightColor => dest.write_str("border-right-color"),
- TransitionProperty::BorderRightWidth => dest.write_str("border-right-width"),
- TransitionProperty::BorderSpacing => dest.write_str("border-spacing"),
- TransitionProperty::BorderTopColor => dest.write_str("border-top-color"),
- TransitionProperty::BorderTopWidth => dest.write_str("border-top-width"),
- TransitionProperty::Bottom => dest.write_str("bottom"),
- TransitionProperty::Color => dest.write_str("color"),
- TransitionProperty::Clip => dest.write_str("clip"),
- TransitionProperty::FontSize => dest.write_str("font-size"),
- TransitionProperty::FontWeight => dest.write_str("font-weight"),
- TransitionProperty::Height => dest.write_str("height"),
- TransitionProperty::Left => dest.write_str("left"),
- TransitionProperty::LetterSpacing => dest.write_str("letter-spacing"),
- TransitionProperty::LineHeight => dest.write_str("line-height"),
- TransitionProperty::MarginBottom => dest.write_str("margin-bottom"),
- TransitionProperty::MarginLeft => dest.write_str("margin-left"),
- TransitionProperty::MarginRight => dest.write_str("margin-right"),
- TransitionProperty::MarginTop => dest.write_str("margin-top"),
- TransitionProperty::MaxHeight => dest.write_str("max-height"),
- TransitionProperty::MaxWidth => dest.write_str("max-width"),
- TransitionProperty::MinHeight => dest.write_str("min-height"),
- TransitionProperty::MinWidth => dest.write_str("min-width"),
- TransitionProperty::Opacity => dest.write_str("opacity"),
- TransitionProperty::OutlineColor => dest.write_str("outline-color"),
- TransitionProperty::OutlineWidth => dest.write_str("outline-width"),
- TransitionProperty::PaddingBottom => dest.write_str("padding-bottom"),
- TransitionProperty::PaddingLeft => dest.write_str("padding-left"),
- TransitionProperty::PaddingRight => dest.write_str("padding-right"),
- TransitionProperty::PaddingTop => dest.write_str("padding-top"),
- TransitionProperty::Right => dest.write_str("right"),
- TransitionProperty::TextIndent => dest.write_str("text-indent"),
- TransitionProperty::TextShadow => dest.write_str("text-shadow"),
- TransitionProperty::Top => dest.write_str("top"),
- TransitionProperty::Transform => dest.write_str("transform"),
- TransitionProperty::VerticalAlign => dest.write_str("vertical-align"),
- TransitionProperty::Visibility => dest.write_str("visibility"),
- TransitionProperty::Width => dest.write_str("width"),
- TransitionProperty::WordSpacing => dest.write_str("word-spacing"),
- TransitionProperty::ZIndex => dest.write_str("z-index"),
- }
- }
- }
+ // NB: Can't generate the type here because it needs all the longhands
+ // generated beforehand.
+ pub use properties::animated_properties::TransitionProperty;
+ pub use properties::animated_properties::TransitionProperty as SingleComputedValue;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -715,61 +562,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
computed_value::T(Vec::new())
}
- pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
- match_ignore_ascii_case! {
- try!(input.expect_ident()),
- "all" => Ok(TransitionProperty::All),
- "background-color" => Ok(TransitionProperty::BackgroundColor),
- "background-position" => Ok(TransitionProperty::BackgroundPosition),
- "border-bottom-color" => Ok(TransitionProperty::BorderBottomColor),
- "border-bottom-width" => Ok(TransitionProperty::BorderBottomWidth),
- "border-left-color" => Ok(TransitionProperty::BorderLeftColor),
- "border-left-width" => Ok(TransitionProperty::BorderLeftWidth),
- "border-right-color" => Ok(TransitionProperty::BorderRightColor),
- "border-right-width" => Ok(TransitionProperty::BorderRightWidth),
- "border-spacing" => Ok(TransitionProperty::BorderSpacing),
- "border-top-color" => Ok(TransitionProperty::BorderTopColor),
- "border-top-width" => Ok(TransitionProperty::BorderTopWidth),
- "bottom" => Ok(TransitionProperty::Bottom),
- "color" => Ok(TransitionProperty::Color),
- "clip" => Ok(TransitionProperty::Clip),
- "font-size" => Ok(TransitionProperty::FontSize),
- "font-weight" => Ok(TransitionProperty::FontWeight),
- "height" => Ok(TransitionProperty::Height),
- "left" => Ok(TransitionProperty::Left),
- "letter-spacing" => Ok(TransitionProperty::LetterSpacing),
- "line-height" => Ok(TransitionProperty::LineHeight),
- "margin-bottom" => Ok(TransitionProperty::MarginBottom),
- "margin-left" => Ok(TransitionProperty::MarginLeft),
- "margin-right" => Ok(TransitionProperty::MarginRight),
- "margin-top" => Ok(TransitionProperty::MarginTop),
- "max-height" => Ok(TransitionProperty::MaxHeight),
- "max-width" => Ok(TransitionProperty::MaxWidth),
- "min-height" => Ok(TransitionProperty::MinHeight),
- "min-width" => Ok(TransitionProperty::MinWidth),
- "opacity" => Ok(TransitionProperty::Opacity),
- "outline-color" => Ok(TransitionProperty::OutlineColor),
- "outline-width" => Ok(TransitionProperty::OutlineWidth),
- "padding-bottom" => Ok(TransitionProperty::PaddingBottom),
- "padding-left" => Ok(TransitionProperty::PaddingLeft),
- "padding-right" => Ok(TransitionProperty::PaddingRight),
- "padding-top" => Ok(TransitionProperty::PaddingTop),
- "right" => Ok(TransitionProperty::Right),
- "text-indent" => Ok(TransitionProperty::TextIndent),
- "text-shadow" => Ok(TransitionProperty::TextShadow),
- "top" => Ok(TransitionProperty::Top),
- "transform" => Ok(TransitionProperty::Transform),
- "vertical-align" => Ok(TransitionProperty::VerticalAlign),
- "visibility" => Ok(TransitionProperty::Visibility),
- "width" => Ok(TransitionProperty::Width),
- "word-spacing" => Ok(TransitionProperty::WordSpacing),
- "z-index" => Ok(TransitionProperty::ZIndex),
- _ => Err(())
- }
- }
-
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
- Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
+ Ok(SpecifiedValue(try!(input.parse_comma_separated(SingleSpecifiedValue::parse))))
}
impl ToComputedValue for SpecifiedValue {
@@ -790,9 +584,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
</%helpers:longhand>
<%helpers:longhand name="animation-name" experimental="True">
- use cssparser::ToCss;
- use std::borrow::Cow;
- use std::fmt;
+ use values::computed::ComputedValueAsSpecified;
pub mod computed_value {
use cssparser::ToCss;
@@ -818,26 +610,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
}
}
- // TODO: Use Cows? Probably more codegen work would be needed, and this
- // could not be that worth it (animations arent *that* used).
- #[derive(Debug, Clone, PartialEq, HeapSizeOf)]
- pub struct SpecifiedValue(Vec<String>);
-
- impl ToCss for SpecifiedValue {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- if self.0.is_empty() {
- return dest.write_str("none")
- }
-
- for (i, name) in self.0.iter().enumerate() {
- if i != 0 {
- try!(dest.write_str(", "));
- }
- try!(dest.write_str(&name));
- }
- Ok(())
- }
- }
+ pub use self::computed_value::T as SpecifiedValue;
#[inline]
pub fn get_initial_value() -> computed_value::T {
@@ -845,27 +618,13 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+ use std::borrow::Cow;
Ok(SpecifiedValue(try!(input.parse_comma_separated(|input| {
input.expect_ident().map(Cow::into_owned)
}))))
}
- impl ToComputedValue for SpecifiedValue {
- type ComputedValue = computed_value::T;
-
- #[inline]
- fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
- let mut ret = vec![];
- if let Some(animations) = context.animations() {
- for name in self.0.iter() {
- if animations.contains_key(&**name) {
- ret.push(name.clone());
- }
- }
- }
- computed_value::T(ret)
- }
- }
+ impl ComputedValueAsSpecified for SpecifiedValue {}
</%helpers:longhand>
<%helpers:longhand name="animation-duration" experimental="True">
diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs
index 3df108e0e6a..26105138bcb 100644
--- a/components/style/properties/properties.mako.rs
+++ b/components/style/properties/properties.mako.rs
@@ -133,6 +133,10 @@ pub mod shorthands {
<%include file="/shorthand/text.mako.rs" />
}
+pub mod animated_properties {
+ <%include file="/helpers/animated_properties.mako.rs" />
+}
+
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
// Maybe submit for inclusion in libstd?
@@ -1045,6 +1049,23 @@ impl PropertyDeclaration {
PropertyDeclaration::Custom(_, _) => &[]
}
}
+
+ /// Returns true if this property is one of the animable properties, false
+ /// otherwise.
+ pub fn is_animatable(&self) -> bool {
+ match *self {
+ % for property in data.longhands:
+ PropertyDeclaration::${property.camel_case}(_) => {
+ % if property.animatable:
+ true
+ % else:
+ false
+ % endif
+ }
+ % endfor
+ PropertyDeclaration::Custom(..) => false,
+ }
+ }
}
pub mod style_struct_traits {
@@ -1595,14 +1616,12 @@ fn cascade_with_cached_declarations<C: ComputedValues>(
parent_style: &C,
cached_style: &C,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
- animations: Option<<&HashMap<String, Vec<Keyframe>>>,
mut error_reporter: StdBox<ParseErrorReporter + Send>)
-> C {
let mut context = computed::Context {
is_root_element: false,
viewport_size: viewport_size,
inherited_style: parent_style,
- animations: animations,
style: C::new(
custom_properties,
shareable,
@@ -1742,7 +1761,6 @@ pub fn cascade<C: ComputedValues>(
shareable: bool,
parent_style: Option<<&C>,
cached_style: Option<<&C>,
- animations: Option<<&HashMap<String, Vec<Keyframe>>>,
mut error_reporter: StdBox<ParseErrorReporter + Send>)
-> (C, bool) {
use properties::style_struct_traits::{Border, Box, Font, Outline};
@@ -1778,7 +1796,6 @@ pub fn cascade<C: ComputedValues>(
parent_style,
cached_style,
custom_properties,
- animations,
error_reporter);
return (style, false)
}
@@ -1787,7 +1804,6 @@ pub fn cascade<C: ComputedValues>(
is_root_element: is_root_element,
viewport_size: viewport_size,
inherited_style: inherited_style,
- animations: animations,
style: C::new(
custom_properties,
shareable,
diff --git a/components/style/properties/shorthand/box.mako.rs b/components/style/properties/shorthand/box.mako.rs
index 48b49ca4811..27e923ba1aa 100644
--- a/components/style/properties/shorthand/box.mako.rs
+++ b/components/style/properties/shorthand/box.mako.rs
@@ -32,7 +32,7 @@
let (mut timing_function, mut delay) = (None, None);
loop {
if property.is_none() {
- if let Ok(value) = input.try(|input| transition_property::parse_one(input)) {
+ if let Ok(value) = input.try(transition_property::SingleSpecifiedValue::parse) {
property = Some(value);
continue
}
diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs
index a3fe67b1b75..5b2901d70ef 100644
--- a/components/style/selector_matching.rs
+++ b/components/style/selector_matching.rs
@@ -8,6 +8,7 @@ use dom::PresentationalHintsSynthetizer;
use element_state::*;
use error_reporting::StdoutErrorReporter;
use keyframes::Keyframe;
+use keyframes::ComputedKeyframesAnimation;
use media_queries::{Device, MediaType};
use parser::ParserContextExtraData;
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
@@ -128,7 +129,7 @@ pub struct Stylist<Impl: SelectorImplExt> {
BuildHasherDefault<::fnv::FnvHasher>>,
/// A map with all the animations indexed by name.
- animations: HashMap<String, Vec<Keyframe>>,
+ animations: HashMap<String, ComputedKeyframesAnimation<Impl::ComputedValues>>,
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
/// These are eagerly computed once, and then used to resolve the new
@@ -252,10 +253,10 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
self.rules_source_order = rules_source_order;
}
CSSRule::Keyframes(ref keyframes_rule) => {
- // TODO: This *might* be optimised converting the
- // Vec<Keyframe> into something like Arc<[Keyframe]>.
- self.animations.insert(keyframes_rule.name.clone(),
- keyframes_rule.keyframes.clone());
+ if let Some(computed) = ComputedKeyframesAnimation::from_keyframes(&keyframes_rule.keyframes) {
+ self.animations.insert(keyframes_rule.name.clone(),
+ computed);
+ }
}
// We don't care about any other rule.
_ => {}
@@ -289,7 +290,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
properties::cascade(self.device.au_viewport_size(),
&declarations, false,
parent.map(|p| &**p),
- None, None,
+ None,
Box::new(StdoutErrorReporter));
Some(Arc::new(computed))
} else {
@@ -322,7 +323,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
let (computed, _) =
properties::cascade(self.device.au_viewport_size(),
&declarations, false,
- Some(&**parent), None, None,
+ Some(&**parent), None,
Box::new(StdoutErrorReporter));
Some(Arc::new(computed))
}
@@ -458,7 +459,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
}
#[inline]
- pub fn animations(&self) -> &HashMap<String, Vec<Keyframe>> {
+ pub fn animations(&self) -> &HashMap<String, ComputedKeyframesAnimation<Impl::ComputedValues>> {
&self.animations
}
}
diff --git a/components/style/values.rs b/components/style/values.rs
index 0a62485a185..5dea6f3cb7a 100644
--- a/components/style/values.rs
+++ b/components/style/values.rs
@@ -1391,16 +1391,13 @@ pub mod specified {
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
input.try(BorderRadiusSize::parse).or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
- "thin" =>
- Ok(BorderRadiusSize::circle(
- LengthOrPercentage::Length(Length::from_px(1.)))),
- "medium" =>
- Ok(BorderRadiusSize::circle(
- LengthOrPercentage::Length(Length::from_px(3.)))),
- "thick" =>
- Ok(BorderRadiusSize::circle(
- LengthOrPercentage::Length(Length::from_px(5.)))),
- _ => Err(())
+ "thin" => Ok(BorderRadiusSize::circle(
+ LengthOrPercentage::Length(Length::from_px(1.)))),
+ "medium" => Ok(BorderRadiusSize::circle(
+ LengthOrPercentage::Length(Length::from_px(3.)))),
+ "thick" => Ok(BorderRadiusSize::circle(
+ LengthOrPercentage::Length(Length::from_px(5.)))),
+ _ => Err(())
}
})
}
@@ -1561,10 +1558,8 @@ pub mod specified {
pub mod computed {
use app_units::Au;
use euclid::size::Size2D;
- use keyframes::Keyframe;
use properties::ComputedValues;
use properties::style_struct_traits::Font;
- use std::collections::HashMap;
use std::fmt;
use super::LocalToCss;
use super::specified::AngleOrCorner;
@@ -1580,7 +1575,6 @@ pub mod computed {
fn inherited_style(&self) -> &Self::ConcreteComputedValues;
fn style(&self) -> &Self::ConcreteComputedValues;
fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues;
- fn animations(&self) -> Option<&HashMap<String, Vec<Keyframe>>>;
}
pub struct Context<'a, C: ComputedValues> {
@@ -1588,7 +1582,6 @@ pub mod computed {
pub viewport_size: Size2D<Au>,
pub inherited_style: &'a C,
- pub animations: Option<&'a HashMap<String, Vec<Keyframe>>>,
/// Values access through this need to be in the properties "computed early":
/// color, text-decoration, font-size, display, position, float, border-*-style, outline-style
pub style: C,
@@ -1601,7 +1594,6 @@ pub mod computed {
fn inherited_style(&self) -> &C { &self.inherited_style }
fn style(&self) -> &C { &self.style }
fn mutate_style(&mut self) -> &mut C { &mut self.style }
- fn animations(&self) -> Option<&HashMap<String, Vec<Keyframe>>> { self.animations }
}
pub trait ToComputedValue {
@@ -1757,7 +1749,7 @@ pub mod computed {
}
- #[derive(PartialEq, Clone, Copy)]
+ #[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
diff --git a/components/style/viewport.rs b/components/style/viewport.rs
index 1707721a362..06411425922 100644
--- a/components/style/viewport.rs
+++ b/components/style/viewport.rs
@@ -648,7 +648,6 @@ impl MaybeNew for ViewportConstraints {
viewport_size: initial_viewport,
inherited_style: ServoComputedValues::initial_values(),
style: ServoComputedValues::initial_values().clone(),
- animations: None,
};
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'