Bug 1207734 - Part 6. (stylo) Implement scale property. draft
authorcku <cku@mozilla.com>
Wed, 06 Dec 2017 22:35:48 +0800
changeset 708805 c1868d20a81c6fc3e981a86e1821230a9b13f83b
parent 708804 2e558ac8403d56aebce922a22d8e3b869ac93dc0
child 708806 ade1ec586d4ce896418c56a36d5b624f0f1e1c54
push id92454
push userbmo:cku@mozilla.com
push dateThu, 07 Dec 2017 06:47:50 +0000
bugs1207734
milestone59.0a1
Bug 1207734 - Part 6. (stylo) Implement scale property. MozReview-Commit-ID: 6yIhT78Skh6
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/transform.rs
servo/components/style/values/generics/transform.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/transform.rs
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -666,16 +666,84 @@ def set_gecko_property(ffi_name, expr):
             },
             x => {
                 panic!("Found unexpected value in style struct for translate property: {:?}", x)
             }
         }
     }
 </%def>
 
+<%def name="impl_scale(ident, gecko_ffi_name)">
+    #[allow(non_snake_case)]
+    pub fn set_${ident}(&mut self, other: values::computed::Scale) {
+        use values::generics::transform::Scale;
+        use values::generics::transform::TransformOperation;
+
+        unsafe { self.gecko.${gecko_ffi_name}.clear() };
+
+        match other {
+            Scale::None => (),
+            Scale::Specified(sx, None, None) => {
+                let operation = vec![TransformOperation::ScaleX(sx)];
+                convert_transform(&operation, &mut self.gecko.${gecko_ffi_name});
+            },
+            Scale::Specified(sx, sy, None) => {
+                let operation = vec![TransformOperation::Scale(sx, sy)];
+                convert_transform(&operation, &mut self.gecko.${gecko_ffi_name});
+            },
+            Scale::Specified(sx, Some(sy), Some(sz)) => {
+                let operation = vec![TransformOperation::Scale3D(sx, sy, sz)];
+                convert_transform(&operation, &mut self.gecko.${gecko_ffi_name});
+            },
+            x => {
+                panic!("Found unexpected value in style struct for scale property: {:?}", x)
+            }
+        }
+    }
+
+    #[allow(non_snake_case)]
+    pub fn copy_${ident}_from(&mut self, other: &Self) {
+        unsafe { self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); }
+    }
+
+    #[allow(non_snake_case)]
+    pub fn reset_${ident}(&mut self, other: &Self) {
+        self.copy_${ident}_from(other)
+    }
+
+    #[allow(non_snake_case)]
+    pub fn clone_${ident}(&self) -> values::computed::Scale {
+        use values::generics::transform::Scale;
+        use values::generics::transform::TransformOperation;
+
+        if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() {
+            return Scale::None;
+        }
+
+        let list = unsafe { (*self.gecko.${gecko_ffi_name}.to_safe().get()).mHead.as_ref() };
+
+        let mut transform = clone_transform_from_list(list);
+        debug_assert!(transform.0.len() == 1);
+        match transform.0.pop() {
+            Some(TransformOperation::Scale3D(sx, sy, sz)) => {
+                Scale::Specified(sx, Some(sy), Some(sz))
+            },
+            Some(TransformOperation::Scale(sx, sy)) => {
+                Scale::Specified(sx, sy, None)
+            },
+            Some(TransformOperation::ScaleX(sx)) => {
+                Scale::Specified(sx, None, None)
+            },
+            x => {
+                panic!("Found unexpected value in style struct for scale property: {:?}", x)
+            }
+        }
+    }
+</%def>
+
 <%def name="impl_svg_length(ident, gecko_ffi_name)">
     // When context-value is used on an SVG length, the corresponding flag is
     // set on mContextFlags, and the length field is set to the initial value.
 
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber};
         use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
         let length = match v {
@@ -1654,16 +1722,17 @@ impl Clone for ${style_struct.gecko_stru
         "Rotate": impl_rotate,
         "SVGLength": impl_svg_length,
         "SVGOpacity": impl_svg_opacity,
         "SVGPaint": impl_svg_paint,
         "SVGWidth": impl_svg_length,
         "Transform": impl_transform,
         "TransformOrigin": impl_transform_origin,
         "Translate": impl_translate,
+        "Scale": impl_scale,
         "UrlOrNone": impl_css_url,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
 
         # get the method and pass additional keyword or type-specific arguments
         if longhand.logical:
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -590,16 +590,24 @@
 ${helpers.predefined_type("translate", "Translate",
                           "computed::Translate::none()",
                           extra_prefixes="webkit",
                           animation_value_type="ComputedValue",
                           gecko_ffi_name="mSpecifiedTranslate",
                           flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                           spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms")}
 
+${helpers.predefined_type("scale", "Scale",
+                          "computed::Scale::none()",
+                          extra_prefixes="webkit",
+                          animation_value_type="ComputedValue",
+                          gecko_ffi_name="mSpecifiedScale",
+                          flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
+                          spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms")}
+
 // CSSOM View Module
 // https://www.w3.org/TR/cssom-view-1/
 ${helpers.single_keyword("scroll-behavior",
                          "auto smooth",
                          gecko_pref="layout.css.scroll-behavior.property-enabled",
                          products="gecko",
                          spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior",
                          animation_value_type="discrete")}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -54,17 +54,17 @@ pub use self::length::{CalcLengthOrPerce
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{CSSPixelLength, NonNegativeLength, NonNegativeLengthOrPercentage};
 pub use self::percentage::Percentage;
 pub use self::position::{Position, GridAutoFlow, GridTemplateAreas};
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextOverflow, WordSpacing};
 pub use self::time::Time;
-pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin, Rotate, Translate};
+pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin, Rotate, Translate, Scale};
 pub use self::ui::MozForceBrokenImageIcon;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -7,17 +7,18 @@
 use euclid::{Transform3D, Vector3D};
 use num_traits::Zero;
 use super::{CSSFloat, Either};
 use values::animated::ToAnimatedZero;
 use values::computed::{Angle, Integer, Length, LengthOrPercentage, Number, Percentage};
 use values::computed::{LengthOrNumber, LengthOrPercentageOrNumber};
 use values::generics::transform::{self, Matrix as GenericMatrix, Matrix3D as GenericMatrix3D};
 use values::generics::transform::{Transform as GenericTransform, TransformOperation as GenericTransformOperation,
-    Rotate as GenericRotate, Translate as GenericTranslate};
+    Rotate as GenericRotate, Translate as GenericTranslate,
+    Scale as GenericScale};
 use values::generics::transform::TimingFunction as GenericTimingFunction;
 use values::generics::transform::TransformOrigin as GenericTransformOrigin;
 
 /// A single operation in a computed CSS `transform`
 pub type TransformOperation = GenericTransformOperation<
     Angle,
     Number,
     Length,
@@ -294,9 +295,12 @@ impl ToAnimatedZero for Transform {
             .collect::<Result<Vec<_>, _>>()?))
     }
 }
 
 /// A computed CSS `rotate`
 pub type Rotate = GenericRotate<Number, Angle>;
 
 /// A computed CSS `translate`
-pub type Translate = GenericTranslate<LengthOrPercentage, Length>;
\ No newline at end of file
+pub type Translate = GenericTranslate<LengthOrPercentage, Length>;
+
+/// A computed CSS `scale`
+pub type Scale = GenericScale<Number>;
\ No newline at end of file
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -837,8 +837,52 @@ impl<LengthOrPercentage: ToCss  + fmt::D
 }
 
 impl<LengthOrPercentage, Length> Translate<LengthOrPercentage, Length> {
     /// `none`
     pub fn none() -> Self {
         Translate::None
     }
 }
+
+#[derive(ToComputedValue, Animate, ComputeSquaredDistance, ToAnimatedZero)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+/// A value of the `Scale` property
+pub enum Scale<Number> {
+    /// 'none'
+    None,
+    /// '<length-percentage> [ <length-percentage> <length>? ]?'
+    Specified(Number, Option<Number>, Option<Number>)
+}
+
+impl<Number: ToCss + Copy + fmt::Debug> ToCss for Scale<Number> {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+    where
+        W: fmt::Write,
+    {
+        match *self {
+            Scale::None => dest.write_str("none"),
+            Scale::Specified(sx, None, None) => sx.to_css(dest),
+            Scale::Specified(sx, Some(sy), None) => {
+                sx.to_css(dest)?;
+                dest.write_str(" ")?;
+                sy.to_css(dest)
+            },
+            Scale::Specified(sx, Some(sy), Some(sz)) => {
+                sx.to_css(dest)?;
+                dest.write_str(" ")?;
+                sy.to_css(dest)?;
+                dest.write_str(" ")?;
+                sz.to_css(dest)
+            },
+            ref x => {
+                panic!("Found unexpected value for scale property: {:?}", x)
+            },
+        }
+    }
+}
+
+impl<Number> Scale<Number> {
+    /// `none`
+    pub fn none() -> Self {
+        Scale::None
+    }
+}
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -50,17 +50,17 @@ pub use self::length::{NoCalcLength, Vie
 pub use self::length::NonNegativeLengthOrPercentage;
 pub use self::rect::LengthOrNumberRect;
 pub use self::percentage::Percentage;
 pub use self::position::{Position, PositionComponent, GridAutoFlow, GridTemplateAreas};
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextDecorationLine, TextOverflow, WordSpacing};
 pub use self::time::Time;
-pub use self::transform::{TimingFunction, Transform, TransformOrigin, Rotate, Translate};
+pub use self::transform::{TimingFunction, Transform, TransformOrigin, Rotate, Translate, Scale};
 pub use self::ui::MozForceBrokenImageIcon;
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -7,17 +7,18 @@
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 use style_traits::{ParseError, StyleParseErrorKind};
 use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage};
 use values::computed::{Percentage as ComputedPercentage, ToComputedValue};
 use values::computed::transform::TimingFunction as ComputedTimingFunction;
 use values::generics::transform::{Matrix3D, Transform as GenericTransform,
-    Rotate as GenericRotate, Translate as GenericTranslate};
+    Rotate as GenericRotate, Translate as GenericTranslate,
+    Scale as GenericScale};
 use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction, Matrix};
 use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin};
 use values::generics::transform::TransformOperation as GenericTransformOperation;
 use values::specified::{self, Angle, Number, Length, Integer};
 use values::specified::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber};
 use values::specified::position::{Side, X, Y};
 
 /// A single operation in a specified CSS `transform`
@@ -552,8 +553,33 @@ impl Parse for Translate {
             } else {
                 return Ok(GenericTranslate::Specified(tx, Some(ty), None));
             }
         }
 
         Ok(GenericTranslate::Specified(tx, None, None))
     }
 }
+
+/// A specified CSS `scale`
+pub type Scale = GenericScale<Number>;
+
+impl Parse for Scale {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>
+    ) -> Result<Self, ParseError<'i>> {
+        if input.try(|i| i.expect_ident_matching("none")).is_ok() {
+            return Ok(GenericScale::None);
+        }
+
+        let sx = Number::parse(context, input)?;
+        if let Some(sy) = input.try(|i| Number::parse(context, i)).ok() {
+            if let Some(sz) = input.try(|i| Number::parse(context, i)).ok() {
+                return Ok(GenericScale::Specified(sx, Some(sy), Some(sz)));
+            } else {
+                return Ok(GenericScale::Specified(sx, Some(sy), None));
+            }
+        }
+
+        Ok(GenericScale::Specified(sx, None, None))
+    }
+}
\ No newline at end of file