--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -538,16 +538,74 @@ def set_gecko_property(ffi_name, expr):
}
<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
#[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)})
}
</%def>
+<%def name="impl_rotate(ident, gecko_ffi_name)">
+ #[allow(non_snake_case)]
+ pub fn set_${ident}(&mut self, other: values::computed::Rotate) {
+ use values::generics::transform::Rotate;
+ use values::generics::transform::TransformOperation;
+
+ unsafe { self.gecko.mSpecifiedRotate.clear() };
+
+ match other {
+ Rotate::None => {},
+ Rotate::Specified(Some((rx, ry, rz)), angle) => {
+ let operation = [TransformOperation::Rotate3D(rx, ry, rz, angle)];
+ convert_transform(&operation, &mut self.gecko.mSpecifiedRotate);
+ },
+ Rotate::Specified(None, angle) => {
+ let operation = [TransformOperation::Rotate(angle)];
+ convert_transform(&operation, &mut self.gecko.mSpecifiedRotate);
+ },
+ }
+ }
+
+ #[allow(non_snake_case)]
+ pub fn copy_${ident}_from(&mut self, other: &Self) {
+ unsafe { self.gecko.mSpecifiedRotate.set(&other.gecko.mSpecifiedRotate); }
+ }
+
+ #[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::Rotate {
+ use values::generics::transform::Rotate;
+ use values::generics::transform::TransformOperation;
+
+ if self.gecko.mSpecifiedRotate.mRawPtr.is_null() {
+ return Rotate::None;
+ }
+
+ let list = unsafe { (*self.gecko.mSpecifiedRotate.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::Rotate3D(rx, ry, rz, angle)) => {
+ Rotate::Specified(Some((rx, ry, rz)), angle)
+ },
+ Some(TransformOperation::Rotate(angle)) => {
+ Rotate::Specified(None, angle)
+ },
+ x => {
+ panic!("Found unexpected value in style struct for rotate 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 {
@@ -1518,16 +1576,17 @@ impl Clone for ${style_struct.gecko_stru
"MozScriptSizeMultiplier": impl_simple,
"NonNegativeLengthOrPercentage": impl_style_coord,
"NonNegativeNumber": impl_simple,
"Number": impl_simple,
"Integer": impl_simple,
"Opacity": impl_simple,
"Color": impl_color,
"RGBAColor": impl_rgba_color,
+ "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,
"UrlOrNone": impl_css_url,
}
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -601,16 +601,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/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -438,8 +438,35 @@ where
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
Some(ref value) => Ok(Some(value.to_animated_zero()?)),
None => Ok(None),
}
}
}
+
+impl<T, U, V> ToAnimatedZero for (T, U, V)
+where
+ T: ToAnimatedZero,
+ U: ToAnimatedZero,
+ V: ToAnimatedZero,
+{
+ #[inline]
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ Ok((self.0.to_animated_zero()?, self.1.to_animated_zero()?,
+ self.2.to_animated_zero()?))
+ }
+}
+
+impl<T, U, V> Animate for (T, U, V)
+where
+ T: Animate,
+ U: Animate,
+ V: Animate,
+{
+ #[inline]
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ Ok((self.0.animate(&other.0, procedure)?,
+ self.1.animate(&other.1, procedure)?,
+ self.2.animate(&other.2, procedure)?,))
+ }
+}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -57,17 +57,17 @@ pub use self::length::{CSSPixelLength, N
pub use self::list::{ListStyleImage, Quotes};
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, 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;
@@ -298,16 +298,39 @@ impl<A, B> ToComputedValue for (A, B)
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
(A::from_computed_value(&computed.0), B::from_computed_value(&computed.1))
}
}
+impl<A, B, C> ToComputedValue for (A, B, C)
+ where A: ToComputedValue, B: ToComputedValue, C: ToComputedValue,
+{
+ type ComputedValue = (
+ <A as ToComputedValue>::ComputedValue,
+ <B as ToComputedValue>::ComputedValue,
+ <C as ToComputedValue>::ComputedValue,
+ );
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ (self.0.to_computed_value(context), self.1.to_computed_value(context),
+ self.2.to_computed_value(context))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ (A::from_computed_value(&computed.0),
+ B::from_computed_value(&computed.1),
+ C::from_computed_value(&computed.2))
+ }
+}
+
impl<T> ToComputedValue for Option<T>
where T: ToComputedValue
{
type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
self.as_ref().map(|item| item.to_computed_value(context))
--- 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,11 @@ 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>;
\ No newline at end of file
--- a/servo/components/style/values/distance.rs
+++ b/servo/components/style/values/distance.rs
@@ -128,8 +128,22 @@ impl Sum for SquaredDistance {
{
let first = match iter.next() {
Some(first) => first,
None => return SquaredDistance::Value(0.),
};
iter.fold(first, Add::add)
}
}
+
+impl<T, U, V> ComputeSquaredDistance for (T, U, V)
+where
+ T: ComputeSquaredDistance,
+ U: ComputeSquaredDistance,
+ V: ComputeSquaredDistance,
+{
+ #[inline]
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ Ok(self.0.compute_squared_distance(&other.0)? +
+ self.1.compute_squared_distance(&other.1)? +
+ self.2.compute_squared_distance(&other.2)?)
+ }
+}
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -750,8 +750,20 @@ 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,
+ /// '<number>{3}? <angle>'
+ Specified(Option<(Number, Number, Number)>, Angle)
+}
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -54,17 +54,17 @@ pub use self::outline::OutlineStyle;
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};
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
@@ -6,17 +6,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};
+use values::generics::transform::{Matrix3D, Transform as GenericTransform,
+ 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,34 @@ 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() {
+ // <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::Specified(Some((rx, ry, rz)), angle));
+ } else {
+ // <angle>
+ let angle = specified::Angle::parse_with_unitless(context, input)?;
+ return Ok(GenericRotate::Specified(None, angle));
+ }
+ }
+}
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -72,16 +72,31 @@ where
T: ToCss,
{
#[inline]
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
}
}
+impl<T, U, V> ToCss for (T, U, V)
+where
+ T: ToCss,
+ U: ToCss,
+ V: ToCss,
+{
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ self.0.to_css(dest)?;
+ dest.write_str(" ")?;
+ self.1.to_css(dest)?;
+ dest.write_str(" ")?;
+ self.2.to_css(dest)
+ }
+}
+
#[macro_export]
macro_rules! serialize_function {
($dest: expr, $name: ident($( $arg: expr, )+)) => {
serialize_function!($dest, $name($($arg),+))
};
($dest: expr, $name: ident($first_arg: expr $( , $arg: expr )*)) => {
{
$dest.write_str(concat!(stringify!($name), "("))?;