Bug 1357663 - Make font-stretch animatable. r?hiro draft
authorMantaroh Yoshinaga <mantaroh@gmail.com>
Thu, 18 May 2017 15:46:28 +0900
changeset 580145 c30cab256bbe0c0689b41530b7c7a7fd8525700d
parent 580040 baf05f61bc14fdf45511bc1165ce76daa08c5c0f
child 580146 84404aff23813779d56bcf065bd56eaeda906880
push id59453
push usermantaroh@gmail.com
push dateThu, 18 May 2017 06:47:04 +0000
reviewershiro
bugs1357663
milestone55.0a1
Bug 1357663 - Make font-stretch animatable. r?hiro MozReview-Commit-ID: 7XcOXbipbhP
servo/components/style/properties/data.py
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/font.mako.rs
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -122,16 +122,25 @@ class Keyword(object):
             return self.gecko_constant_prefix + "_" + suffix.upper()
 
     def needs_cast(self):
         return self.gecko_enum_prefix is None
 
     def maybe_cast(self, type_str):
         return "as " + type_str if self.needs_cast() else ""
 
+    def casted_constant_name(self, value, cast_type):
+        if cast_type is None:
+            raise TypeError("We should specify the cast_type.")
+
+        if self.gecko_enum_prefix is None:
+            return cast_type.upper() + "_" + self.gecko_constant(value)
+        else:
+            return cast_type.upper() + "_" + self.gecko_constant(value).upper().replace("::", "_")
+
 
 def arg_to_bool(arg):
     if isinstance(arg, bool):
         return arg
     assert arg in ["True", "False"]
     return arg == "True"
 
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -269,24 +269,34 @@ def set_gecko_property(ffi_name, expr):
         };
         ${set_gecko_property(gecko_ffi_name, "result")}
         % if on_set:
         self.${on_set}();
         % endif
     }
 </%def>
 
-<%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword)">
+<%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')">
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         use properties::longhands::${ident}::computed_value::T as Keyword;
         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
-        match ${get_gecko_property(gecko_ffi_name)} ${keyword.maybe_cast("u32")} {
+
+        // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).
+        // And they are convert to signed integer in Rust bindings. We need to cast then
+        // as signed type when we have both signed/unsigned integer in order to use them
+        // as match's arms.
+        // Also, to use same implementation here we use casted constant if we have only singed values.
+        % for value in keyword.values_for('gecko'):
+        const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} = structs::${keyword.gecko_constant(value)} as ${cast_type};
+        % endfor
+
+        match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
             % for value in keyword.values_for('gecko'):
-            structs::${keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)},
+            ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_rust_ident(value)},
             % endfor
             % if keyword.gecko_inexhaustive:
             x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x),
             % endif
         }
     }
 </%def>
 
@@ -329,21 +339,21 @@ fn color_to_nscolor_zero_currentcolor(co
         % if complex_color:
             ${get_gecko_property(gecko_ffi_name)}.into()
         % else:
             Color::RGBA(convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)}))
         % endif
     }
 </%def>
 
-<%def name="impl_keyword(ident, gecko_ffi_name, keyword, need_clone, **kwargs)">
-<%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, **kwargs)"></%call>
+<%def name="impl_keyword(ident, gecko_ffi_name, keyword, need_clone, cast_type='u8', **kwargs)">
+<%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
 %if need_clone:
-<%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword)"></%call>
+<%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
 % endif
 </%def>
 
 <%def name="impl_simple(ident, gecko_ffi_name, need_clone=False)">
 <%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
 % if need_clone:
     <%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
@@ -1875,16 +1885,17 @@ fn static_assert() {
                                             "table-header-group table-footer-group table-row table-column-group " +
                                             "table-column table-cell table-caption list-item flex none " +
                                             "inline-flex grid inline-grid ruby ruby-base ruby-base-container " +
                                             "ruby-text ruby-text-container contents flow-root -webkit-box " +
                                             "-webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid " +
                                             "-moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck " +
                                             "-moz-popup -moz-groupbox",
                                             gecko_enum_prefix="StyleDisplay",
+                                            gecko_inexhaustive="True",
                                             gecko_strip_moz_prefix=False) %>
 
     pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
         use properties::longhands::display::computed_value::T as Keyword;
         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
         let result = match v {
             % for value in display_keyword.values_for('gecko'):
                 Keyword::${to_rust_ident(value)} =>
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -12,16 +12,17 @@ use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
 #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
+use properties::longhands::font_stretch::computed_value::T as FontStretch;
 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::box_shadow::computed_value::T as BoxShadowList;
 use properties::longhands::box_shadow::single_value::computed_value::T as BoxShadow;
 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;
@@ -1357,16 +1358,64 @@ impl Animatable for FontWeight {
     #[inline]
     fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
         let a = (*self as u32) as f64;
         let b = (*other as u32) as f64;
         a.compute_distance(&b)
     }
 }
 
+/// https://drafts.csswg.org/css-fonts/#font-stretch-prop
+impl Animatable for FontStretch {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        let from = f64::from(*self);
+        let to   = f64::from(*other);
+        let interpolated_mapped_index = ((from * self_portion + to * other_portion) + 0.5).floor();
+        Ok(interpolated_mapped_index.into())
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        let from = f64::from(*self);
+        let to   = f64::from(*other);
+        from.compute_distance(&to)
+    }
+}
+
+/// We should treat font stretch as real number in order to interpolate this property.
+/// https://drafts.csswg.org/css-fonts-3/#font-stretch-animation
+impl From<FontStretch> for f64 {
+    fn from(stretch: FontStretch) -> f64 {
+        use self::FontStretch::*;
+        match stretch {
+            ultra_condensed => 1.0,
+            extra_condensed => 2.0,
+            condensed       => 3.0,
+            semi_condensed  => 4.0,
+            normal          => 5.0,
+            semi_expanded   => 6.0,
+            expanded        => 7.0,
+            extra_expanded  => 8.0,
+            ultra_expanded  => 9.0,
+        }
+    }
+}
+
+impl Into<FontStretch> for f64 {
+    fn into(self) -> FontStretch {
+        use properties::longhands::font_stretch::computed_value::T::*;
+        debug_assert!(self >= 1.0 && self <= 9.0);
+        static FONT_STRETCH_ENUM_MAP: [FontStretch; 9] =
+            [ ultra_condensed, extra_condensed, condensed, semi_condensed, normal,
+              semi_expanded, expanded, extra_expanded, ultra_expanded ];
+        FONT_STRETCH_ENUM_MAP[(self - 1.0) as usize]
+    }
+}
+
 /// https://drafts.csswg.org/css-transitions/#animtype-simple-list
 impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, V> {
     #[inline]
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
         Ok(generic_position::Position {
             horizontal: try!(self.horizontal.add_weighted(&other.horizontal,
                                                           self_portion, other_portion)),
             vertical: try!(self.vertical.add_weighted(&other.vertical,
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -1174,33 +1174,32 @@
                 }
                 Ok(result)
             },
             _ => Err(())
         }
     }
 </%helpers:longhand>
 
-// FIXME: This prop should be animatable
 ${helpers.single_keyword_system("font-stretch",
                                 "normal ultra-condensed extra-condensed condensed \
                                  semi-condensed semi-expanded expanded extra-expanded \
                                  ultra-expanded",
                                 gecko_ffi_name="mFont.stretch",
                                 gecko_constant_prefix="NS_FONT_STRETCH",
                                 cast_type='i16',
                                 spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
-                                animation_value_type="none")}
+                                animation_value_type="ComputedValue")}
 
 ${helpers.single_keyword_system("font-kerning",
                                 "auto none normal",
                                 products="gecko",
                                 gecko_ffi_name="mFont.kerning",
                                 gecko_constant_prefix="NS_FONT_KERNING",
-                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
+                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
                                 animation_value_type="none")}
 
 /// FIXME: Implement proper handling of each values.
 /// https://github.com/servo/servo/issues/15957
 <%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;