Bug 1207734 - Part 7.a. (stylo) Implement rotate property styling. draft
authorcku <cku@mozilla.com>
Wed, 06 Dec 2017 20:59:40 +0800
changeset 720535 03d6593a4e8b5760d87e96254cbba86819b60b58
parent 720496 1d3f1ebcc205857e45c7b8cf86f95606c491b02f
child 720536 20ea79f10f21f9bab71eede6d08eb38ac62c2491
push id95571
push userbmo:cku@mozilla.com
push dateMon, 15 Jan 2018 18:35:41 +0000
bugs1207734
milestone59.0a1
Bug 1207734 - Part 7.a. (stylo) Implement rotate property styling. MozReview-Commit-ID: FjsXxkruIUB
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
@@ -3079,23 +3079,58 @@ fn static_assert() {
             % endfor
             _ => panic!("Found unexpected value for animation-${ident}"),
         }
     }
     ${impl_animation_count(ident, gecko_ffi_name)}
     ${impl_copy_animation_value(ident, gecko_ffi_name)}
 </%def>
 
+<%def name="impl_individual_transform(ident, type, gecko_ffi_name)">
+    pub fn set_${ident}(&mut self, other: values::computed::${type}) {
+        unsafe { self.gecko.${gecko_ffi_name}.clear() };
+
+        match other.to_transform_operation() {
+            None => {},
+            Some(operation) => {
+                convert_transform(&[operation], &mut self.gecko.${gecko_ffi_name})
+            }
+        }
+    }
+
+    pub fn copy_${ident}_from(&mut self, other: &Self) {
+        unsafe { self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); }
+    }
+
+    pub fn reset_${ident}(&mut self, other: &Self) {
+        self.copy_${ident}_from(other)
+    }
+
+    pub fn clone_${ident}(&self) -> values::computed::${type} {
+        use values::generics::transform::${type};
+
+        if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() {
+            return ${type}::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_eq!(transform.0.len(), 1);
+        ${type}::from_transform_operation(&transform.0.pop().unwrap())
+    }
+</%def>
+
 <% skip_box_longhands= """display overflow-y vertical-align
                           animation-name animation-delay animation-duration
                           animation-direction animation-fill-mode animation-play-state
                           animation-iteration-count animation-timing-function
                           transition-duration transition-delay
                           transition-timing-function transition-property
-                          page-break-before page-break-after
+                          page-break-before page-break-after rotate
                           scroll-snap-points-x scroll-snap-points-y
                           scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate
                           perspective-origin -moz-binding will-change
                           overscroll-behavior-x overscroll-behavior-y
                           overflow-clip-box-inline overflow-clip-box-block
                           perspective-origin -moz-binding will-change
                           shape-outside contain touch-action""" %>
 <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
@@ -3517,16 +3552,18 @@ fn static_assert() {
         T {
             horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mPerspectiveOrigin[0])
                 .expect("Expected length or percentage for horizontal value of perspective-origin"),
             vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mPerspectiveOrigin[1])
                 .expect("Expected length or percentage for vertical value of perspective-origin"),
         }
     }
 
+    ${impl_individual_transform('rotate', 'Rotate', 'mSpecifiedRotate')}
+
     pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) {
         use gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange};
         use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_OPACITY;
         use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_SCROLL;
         use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_TRANSFORM;
         use properties::PropertyId;
         use properties::longhands::will_change::computed_value::T;
 
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -387,16 +387,23 @@
 ${helpers.predefined_type("transform", "Transform",
                           "generics::transform::Transform::none()",
                           extra_prefixes="webkit",
                           animation_value_type="ComputedValue",
                           gecko_ffi_name="mSpecifiedTransform",
                           flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                           spec="https://drafts.csswg.org/css-transforms/#propdef-transform")}
 
+${helpers.predefined_type("rotate", "Rotate",
+                          "generics::transform::Rotate::None",
+                          animation_value_type="ComputedValue",
+                          flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
+                          gecko_pref="layout.css.individual-transform.enabled",
+                          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
@@ -60,17 +60,17 @@ pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::percentage::Percentage;
 pub use self::position::{Position, GridAutoFlow, GridTemplateAreas};
 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign, TextOverflow, WordSpacing};
 pub use self::time::Time;
-pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin};
+pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin, Rotate};
 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
@@ -9,16 +9,17 @@ 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};
 use values::generics::transform::TimingFunction as GenericTimingFunction;
 use values::generics::transform::TransformOrigin as GenericTransformOrigin;
+use values::generics::transform::Rotate as GenericRotate;
 
 /// A single operation in a computed CSS `transform`
 pub type TransformOperation = GenericTransformOperation<
     Angle,
     Number,
     Length,
     Integer,
     LengthOrNumber,
@@ -288,8 +289,32 @@ impl ToAnimatedZero for Transform {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Ok(GenericTransform(self.0
             .iter()
             .map(|op| op.to_animated_zero())
             .collect::<Result<Vec<_>, _>>()?))
     }
 }
+
+/// A computed CSS `rotate`
+pub type Rotate = GenericRotate<Number, Angle>;
+
+impl Rotate {
+    /// Convert TransformOperation to Rotate.
+    pub fn to_transform_operation(&self) -> Option<TransformOperation> {
+        match *self {
+            GenericRotate::None => None,
+            GenericRotate::Rotate(angle) => Some(GenericTransformOperation::Rotate(angle)),
+            GenericRotate::Rotate3D(rx, ry, rz, angle) => Some(GenericTransformOperation::Rotate3D(rx, ry, rz, angle)),
+        }
+    }
+
+    /// Convert Rotate to TransformOperation.
+    pub fn from_transform_operation(operation: &TransformOperation) -> Rotate {
+        match *operation {
+            GenericTransformOperation::Rotate(angle) => GenericRotate::Rotate(angle),
+            GenericTransformOperation::Rotate3D(rx, ry, rz, angle) =>
+                GenericRotate::Rotate3D(rx, ry, rz, angle),
+            ref x => unreachable!("Found unexpected value for rotate property: {:?}", x),
+        }
+    }
+}
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -750,8 +750,22 @@ pub fn get_normalized_vector_and_angle<T
         // A direction vector that cannot be normalized, such as [0, 0, 0], will cause the
         // rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).
         (0., 0., 1., T::zero())
     } else {
         let vector = vector.normalize();
         (vector.x, vector.y, vector.z, angle)
     }
 }
+
+#[derive(ToComputedValue, Animate, ComputeSquaredDistance, ToAnimatedZero)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
+/// A value of the `Rotate` property
+///
+/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
+pub enum Rotate<Number, Angle> {
+    /// 'none'
+    None,
+    /// '<angle>'
+    Rotate(Angle),
+    /// '<number>{3} <angle>'
+    Rotate3D(Number, Number, Number, Angle),
+}
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -57,17 +57,17 @@ 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};
 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextDecorationLine};
 pub use self::text::{TextAlign, TextAlignKeyword, TextOverflow, WordSpacing};
 pub use self::time::Time;
-pub use self::transform::{TimingFunction, Transform, TransformOrigin};
+pub use self::transform::{TimingFunction, Transform, TransformOrigin, Rotate};
 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,16 +7,17 @@
 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};
+use values::generics::transform::Rotate as GenericRotate;
 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`
@@ -503,8 +504,35 @@ impl ToComputedValue for TimingFunction 
                 GenericTimingFunction::Steps(Integer::from_computed_value(&(steps as i32)), position)
             },
             GenericTimingFunction::Frames(frames) => {
                 GenericTimingFunction::Frames(Integer::from_computed_value(&(frames as i32)))
             },
         }
     }
 }
+
+/// A specified CSS `rotate`
+pub type Rotate = GenericRotate<Number, Angle>;
+
+impl Parse for Rotate {
+    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(GenericRotate::None);
+        }
+
+        if let Some(rx) = input.try(|i| Number::parse(context, i)).ok() {
+            // 'rotate: <number>{3} <angle>'
+            let ry = Number::parse(context, input)?;
+            let rz = Number::parse(context, input)?;
+            let angle = specified::Angle::parse(context, input)?;
+            return Ok(GenericRotate::Rotate3D(rx, ry, rz, angle));
+        }
+
+        // 'rotate: <angle>'
+        let angle = specified::Angle::parse(context, input)?;
+        Ok(GenericRotate::Rotate(angle))
+    }
+}
+