1use crate::values::computed::length::Length as ComputedLength;
8use crate::values::computed::length::LengthPercentage as ComputedLengthPercentage;
9use crate::values::specified::angle::Angle as SpecifiedAngle;
10use crate::values::specified::length::Length as SpecifiedLength;
11use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
12use crate::values::{computed, CSSFloat};
13use crate::{Zero, ZeroNoPercent};
14use euclid::default::{Rect, Transform3D};
15use std::fmt::{self, Write};
16use style_traits::{CssWriter, ToCss};
17
18#[allow(missing_docs)]
20#[derive(
21 Clone,
22 Copy,
23 Debug,
24 Deserialize,
25 MallocSizeOf,
26 PartialEq,
27 Serialize,
28 SpecifiedValueInfo,
29 ToAnimatedValue,
30 ToComputedValue,
31 ToCss,
32 ToResolvedValue,
33 ToShmem,
34)]
35#[css(comma, function = "matrix")]
36#[repr(C)]
37pub struct GenericMatrix<T> {
38 pub a: T,
39 pub b: T,
40 pub c: T,
41 pub d: T,
42 pub e: T,
43 pub f: T,
44}
45
46pub use self::GenericMatrix as Matrix;
47
48#[allow(missing_docs)]
49#[cfg_attr(rustfmt, rustfmt_skip)]
50#[derive(
51 Clone,
52 Copy,
53 Debug,
54 Deserialize,
55 MallocSizeOf,
56 PartialEq,
57 Serialize,
58 SpecifiedValueInfo,
59 ToAnimatedValue,
60 ToComputedValue,
61 ToCss,
62 ToResolvedValue,
63 ToShmem,
64)]
65#[css(comma, function = "matrix3d")]
66#[repr(C)]
67pub struct GenericMatrix3D<T> {
68 pub m11: T, pub m12: T, pub m13: T, pub m14: T,
69 pub m21: T, pub m22: T, pub m23: T, pub m24: T,
70 pub m31: T, pub m32: T, pub m33: T, pub m34: T,
71 pub m41: T, pub m42: T, pub m43: T, pub m44: T,
72}
73
74pub use self::GenericMatrix3D as Matrix3D;
75
76#[cfg_attr(rustfmt, rustfmt_skip)]
77impl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {
78 #[inline]
79 fn from(m: Matrix<T>) -> Self {
80 Transform3D::new(
81 m.a.into(), m.b.into(), 0.0, 0.0,
82 m.c.into(), m.d.into(), 0.0, 0.0,
83 0.0, 0.0, 1.0, 0.0,
84 m.e.into(), m.f.into(), 0.0, 1.0,
85 )
86 }
87}
88
89#[cfg_attr(rustfmt, rustfmt_skip)]
90impl<T: Into<f64>> From<Matrix3D<T>> for Transform3D<f64> {
91 #[inline]
92 fn from(m: Matrix3D<T>) -> Self {
93 Transform3D::new(
94 m.m11.into(), m.m12.into(), m.m13.into(), m.m14.into(),
95 m.m21.into(), m.m22.into(), m.m23.into(), m.m24.into(),
96 m.m31.into(), m.m32.into(), m.m33.into(), m.m34.into(),
97 m.m41.into(), m.m42.into(), m.m43.into(), m.m44.into(),
98 )
99 }
100}
101
102#[derive(
104 Animate,
105 Clone,
106 ComputeSquaredDistance,
107 Copy,
108 Debug,
109 MallocSizeOf,
110 PartialEq,
111 SpecifiedValueInfo,
112 ToAnimatedValue,
113 ToAnimatedZero,
114 ToComputedValue,
115 ToCss,
116 ToResolvedValue,
117 ToShmem,
118)]
119#[repr(C)]
120pub struct GenericTransformOrigin<H, V, Depth> {
121 pub horizontal: H,
123 pub vertical: V,
125 pub depth: Depth,
127}
128
129pub use self::GenericTransformOrigin as TransformOrigin;
130
131impl<H, V, D> TransformOrigin<H, V, D> {
132 pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
134 Self {
135 horizontal,
136 vertical,
137 depth,
138 }
139 }
140}
141
142fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
143 x == y
144}
145
146#[derive(
149 Clone,
150 Debug,
151 Deserialize,
152 MallocSizeOf,
153 PartialEq,
154 Serialize,
155 SpecifiedValueInfo,
156 ToAnimatedValue,
157 ToComputedValue,
158 ToCss,
159 ToResolvedValue,
160 ToShmem,
161)]
162#[repr(C, u8)]
163pub enum GenericPerspectiveFunction<L> {
164 None,
166 Length(L),
168}
169
170impl<L> GenericPerspectiveFunction<L> {
171 pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
173 match *self {
174 Self::None => f32::INFINITY,
175 Self::Length(ref l) => f(l),
176 }
177 }
178}
179
180pub use self::GenericPerspectiveFunction as PerspectiveFunction;
181
182#[derive(
183 Clone,
184 Debug,
185 Deserialize,
186 MallocSizeOf,
187 PartialEq,
188 Serialize,
189 SpecifiedValueInfo,
190 ToAnimatedValue,
191 ToComputedValue,
192 ToCss,
193 ToResolvedValue,
194 ToShmem,
195)]
196#[repr(C, u8)]
197pub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>
199where
200 Angle: Zero,
201 LengthPercentage: Zero + ZeroNoPercent,
202 Number: PartialEq,
203{
204 Matrix(GenericMatrix<Number>),
206 Matrix3D(GenericMatrix3D<Number>),
208 #[css(comma, function)]
214 Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
215 #[css(function = "skewX")]
217 SkewX(Angle),
218 #[css(function = "skewY")]
220 SkewY(Angle),
221 #[css(comma, function)]
223 Translate(
224 LengthPercentage,
225 #[css(skip_if = "ZeroNoPercent::is_zero_no_percent")] LengthPercentage,
226 ),
227 #[css(function = "translateX")]
229 TranslateX(LengthPercentage),
230 #[css(function = "translateY")]
232 TranslateY(LengthPercentage),
233 #[css(function = "translateZ")]
235 TranslateZ(Length),
236 #[css(comma, function = "translate3d")]
238 Translate3D(LengthPercentage, LengthPercentage, Length),
239 #[css(comma, function)]
243 Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
244 #[css(function = "scaleX")]
246 ScaleX(Number),
247 #[css(function = "scaleY")]
249 ScaleY(Number),
250 #[css(function = "scaleZ")]
252 ScaleZ(Number),
253 #[css(comma, function = "scale3d")]
255 Scale3D(Number, Number, Number),
256 #[css(function)]
260 Rotate(Angle),
261 #[css(function = "rotateX")]
263 RotateX(Angle),
264 #[css(function = "rotateY")]
266 RotateY(Angle),
267 #[css(function = "rotateZ")]
269 RotateZ(Angle),
270 #[css(comma, function = "rotate3d")]
274 Rotate3D(Number, Number, Number, Angle),
275 #[css(function)]
282 Perspective(GenericPerspectiveFunction<Length>),
283 #[allow(missing_docs)]
285 #[css(comma, function = "interpolatematrix")]
286 InterpolateMatrix {
287 from_list: GenericTransform<
288 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
289 >,
290 to_list: GenericTransform<
291 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
292 >,
293 progress: computed::Percentage,
294 },
295 #[allow(missing_docs)]
297 #[css(comma, function = "accumulatematrix")]
298 AccumulateMatrix {
299 from_list: GenericTransform<
300 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
301 >,
302 to_list: GenericTransform<
303 GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
304 >,
305 count: Integer,
306 },
307}
308
309pub use self::GenericTransformOperation as TransformOperation;
310
311#[derive(
312 Clone,
313 Debug,
314 Deserialize,
315 MallocSizeOf,
316 PartialEq,
317 Serialize,
318 SpecifiedValueInfo,
319 ToAnimatedValue,
320 ToComputedValue,
321 ToCss,
322 ToResolvedValue,
323 ToShmem,
324)]
325#[repr(C)]
326pub struct GenericTransform<T>(#[css(if_empty = "none", iterable)] pub crate::OwnedSlice<T>);
328
329pub use self::GenericTransform as Transform;
330
331impl<Angle, Number, Length, Integer, LengthPercentage>
332 TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
333where
334 Angle: Zero,
335 LengthPercentage: Zero + ZeroNoPercent,
336 Number: PartialEq,
337{
338 pub fn is_rotate(&self) -> bool {
340 use self::TransformOperation::*;
341 matches!(
342 *self,
343 Rotate(..) | Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..)
344 )
345 }
346
347 pub fn is_translate(&self) -> bool {
349 use self::TransformOperation::*;
350 match *self {
351 Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {
352 true
353 },
354 _ => false,
355 }
356 }
357
358 pub fn is_scale(&self) -> bool {
360 use self::TransformOperation::*;
361 match *self {
362 Scale(..) | Scale3D(..) | ScaleX(..) | ScaleY(..) | ScaleZ(..) => true,
363 _ => false,
364 }
365 }
366}
367
368pub trait ToAbsoluteLength {
370 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;
372}
373
374impl ToAbsoluteLength for SpecifiedLength {
375 #[inline]
379 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
380 match *self {
381 SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),
382 SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
383 }
384 }
385}
386
387impl ToAbsoluteLength for SpecifiedLengthPercentage {
388 #[inline]
392 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
393 use self::SpecifiedLengthPercentage::*;
394 match *self {
395 Length(len) => len.to_computed_pixel_length_without_context(),
396 Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
397 Percentage(..) => Err(()),
398 }
399 }
400}
401
402impl ToAbsoluteLength for ComputedLength {
403 #[inline]
404 fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
405 Ok(self.px())
406 }
407}
408
409impl ToAbsoluteLength for ComputedLengthPercentage {
410 #[inline]
411 fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
412 Ok(self
413 .maybe_percentage_relative_to(containing_len)
414 .ok_or(())?
415 .px())
416 }
417}
418
419pub trait ToMatrix {
421 fn is_3d(&self) -> bool;
423
424 fn to_3d_matrix(
426 &self,
427 reference_box: Option<&Rect<ComputedLength>>,
428 ) -> Result<Transform3D<f64>, ()>;
429}
430
431pub trait ToRadians {
433 fn radians64(&self) -> f64;
435}
436
437impl ToRadians for computed::angle::Angle {
438 #[inline]
439 fn radians64(&self) -> f64 {
440 computed::angle::Angle::radians64(self)
441 }
442}
443
444impl ToRadians for SpecifiedAngle {
445 #[inline]
446 fn radians64(&self) -> f64 {
447 computed::angle::Angle::from_degrees(self.degrees()).radians64()
448 }
449}
450
451impl<Angle, Number, Length, Integer, LoP> ToMatrix
452 for TransformOperation<Angle, Number, Length, Integer, LoP>
453where
454 Angle: Zero + ToRadians + Copy,
455 Number: PartialEq + Copy + Into<f32> + Into<f64>,
456 Length: ToAbsoluteLength,
457 LoP: Zero + ToAbsoluteLength + ZeroNoPercent,
458{
459 #[inline]
460 fn is_3d(&self) -> bool {
461 use self::TransformOperation::*;
462 match *self {
463 Translate3D(..) | TranslateZ(..) | Rotate3D(..) | RotateX(..) | RotateY(..) |
464 RotateZ(..) | Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,
465 _ => false,
466 }
467 }
468
469 #[inline]
474 fn to_3d_matrix(
475 &self,
476 reference_box: Option<&Rect<ComputedLength>>,
477 ) -> Result<Transform3D<f64>, ()> {
478 use self::TransformOperation::*;
479
480 let reference_width = reference_box.map(|v| v.size.width);
481 let reference_height = reference_box.map(|v| v.size.height);
482 let matrix = match *self {
483 Rotate3D(ax, ay, az, theta) => {
484 let theta = theta.radians64();
485 let (ax, ay, az, theta) =
486 get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
487 Transform3D::rotation(
488 ax as f64,
489 ay as f64,
490 az as f64,
491 euclid::Angle::radians(theta),
492 )
493 },
494 RotateX(theta) => {
495 let theta = euclid::Angle::radians(theta.radians64());
496 Transform3D::rotation(1., 0., 0., theta)
497 },
498 RotateY(theta) => {
499 let theta = euclid::Angle::radians(theta.radians64());
500 Transform3D::rotation(0., 1., 0., theta)
501 },
502 RotateZ(theta) | Rotate(theta) => {
503 let theta = euclid::Angle::radians(theta.radians64());
504 Transform3D::rotation(0., 0., 1., theta)
505 },
506 Perspective(ref p) => {
507 let px = match p {
508 PerspectiveFunction::None => f32::INFINITY,
509 PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
510 };
511 create_perspective_matrix(px).cast()
512 },
513 Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
514 Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
515 ScaleX(s) => Transform3D::scale(s.into(), 1., 1.),
516 ScaleY(s) => Transform3D::scale(1., s.into(), 1.),
517 ScaleZ(s) => Transform3D::scale(1., 1., s.into()),
518 Translate3D(ref tx, ref ty, ref tz) => {
519 let tx = tx.to_pixel_length(reference_width)? as f64;
520 let ty = ty.to_pixel_length(reference_height)? as f64;
521 Transform3D::translation(tx, ty, tz.to_pixel_length(None)? as f64)
522 },
523 Translate(ref tx, ref ty) => {
524 let tx = tx.to_pixel_length(reference_width)? as f64;
525 let ty = ty.to_pixel_length(reference_height)? as f64;
526 Transform3D::translation(tx, ty, 0.)
527 },
528 TranslateX(ref t) => {
529 let t = t.to_pixel_length(reference_width)? as f64;
530 Transform3D::translation(t, 0., 0.)
531 },
532 TranslateY(ref t) => {
533 let t = t.to_pixel_length(reference_height)? as f64;
534 Transform3D::translation(0., t, 0.)
535 },
536 TranslateZ(ref z) => Transform3D::translation(0., 0., z.to_pixel_length(None)? as f64),
537 Skew(theta_x, theta_y) => Transform3D::skew(
538 euclid::Angle::radians(theta_x.radians64()),
539 euclid::Angle::radians(theta_y.radians64()),
540 ),
541 SkewX(theta) => Transform3D::skew(
542 euclid::Angle::radians(theta.radians64()),
543 euclid::Angle::radians(0.),
544 ),
545 SkewY(theta) => Transform3D::skew(
546 euclid::Angle::radians(0.),
547 euclid::Angle::radians(theta.radians64()),
548 ),
549 Matrix3D(m) => m.into(),
550 Matrix(m) => m.into(),
551 InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
552 Transform3D::identity()
559 },
560 };
561 Ok(matrix)
562 }
563}
564
565impl<T> Transform<T> {
566 pub fn none() -> Self {
568 Transform(Default::default())
569 }
570}
571
572impl<T: ToMatrix> Transform<T> {
573 #[cfg_attr(rustfmt, rustfmt_skip)]
578 pub fn to_transform_3d_matrix(
579 &self,
580 reference_box: Option<&Rect<ComputedLength>>
581 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
582 Self::components_to_transform_3d_matrix(&self.0, reference_box)
583 }
584
585 #[cfg_attr(rustfmt, rustfmt_skip)]
587 pub fn components_to_transform_3d_matrix(
588 ops: &[T],
589 reference_box: Option<&Rect<ComputedLength>>,
590 ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
591 let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
592 use std::{f32, f64};
593 let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;
594 Transform3D::new(
595 cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
596 cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
597 cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
598 cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
599 )
600 };
601
602 let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;
603 Ok((cast_3d_transform(m), is_3d))
604 }
605
606 pub fn to_transform_3d_matrix_f64(
608 &self,
609 reference_box: Option<&Rect<ComputedLength>>
610 ) -> Result<(Transform3D<f64>, bool), ()> {
611 Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)
612 }
613
614 fn components_to_transform_3d_matrix_f64(
616 ops: &[T],
617 reference_box: Option<&Rect<ComputedLength>>,
618 ) -> Result<(Transform3D<f64>, bool), ()> {
619 let mut transform = Transform3D::<f64>::identity();
626 let mut contain_3d = false;
627
628 for operation in ops {
629 let matrix = operation.to_3d_matrix(reference_box)?;
630 contain_3d = contain_3d || operation.is_3d();
631 transform = matrix.then(&transform);
632 }
633
634 Ok((transform, contain_3d))
635 }
636}
637
638#[inline]
640pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
641 if d.is_finite() {
642 Transform3D::perspective(d.max(1.))
643 } else {
644 Transform3D::identity()
645 }
646}
647
648pub fn get_normalized_vector_and_angle<T: Zero>(
650 x: CSSFloat,
651 y: CSSFloat,
652 z: CSSFloat,
653 angle: T,
654) -> (CSSFloat, CSSFloat, CSSFloat, T) {
655 use crate::values::computed::transform::DirectionVector;
656 use euclid::approxeq::ApproxEq;
657 let vector = DirectionVector::new(x, y, z);
658 if vector.square_length().approx_eq(&f32::zero()) {
659 (0., 0., 1., T::zero())
663 } else {
664 let vector = vector.robust_normalize();
665 (vector.x, vector.y, vector.z, angle)
666 }
667}
668
669#[derive(
670 Clone,
671 Copy,
672 Debug,
673 Deserialize,
674 MallocSizeOf,
675 PartialEq,
676 Serialize,
677 SpecifiedValueInfo,
678 ToAnimatedValue,
679 ToAnimatedZero,
680 ToComputedValue,
681 ToResolvedValue,
682 ToShmem,
683)]
684#[repr(C, u8)]
685pub enum GenericRotate<Number, Angle> {
689 None,
691 Rotate(Angle),
693 Rotate3D(Number, Number, Number, Angle),
695}
696
697pub use self::GenericRotate as Rotate;
698
699pub trait IsParallelTo {
702 fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
704}
705
706impl<Number, Angle> ToCss for Rotate<Number, Angle>
707where
708 Number: Copy + ToCss + Zero,
709 Angle: ToCss,
710 (Number, Number, Number): IsParallelTo,
711{
712 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
713 where
714 W: fmt::Write,
715 {
716 use crate::values::computed::transform::DirectionVector;
717 match *self {
718 Rotate::None => dest.write_str("none"),
719 Rotate::Rotate(ref angle) => angle.to_css(dest),
720 Rotate::Rotate3D(x, y, z, ref angle) => {
721 let v = (x, y, z);
727 let axis = if x.is_zero() && y.is_zero() && z.is_zero() {
728 None
735 } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
736 Some("x ")
737 } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
738 Some("y ")
739 } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
740 return angle.to_css(dest);
742 } else {
743 None
744 };
745 match axis {
746 Some(a) => dest.write_str(a)?,
747 None => {
748 x.to_css(dest)?;
749 dest.write_char(' ')?;
750 y.to_css(dest)?;
751 dest.write_char(' ')?;
752 z.to_css(dest)?;
753 dest.write_char(' ')?;
754 },
755 }
756 angle.to_css(dest)
757 },
758 }
759 }
760}
761
762#[derive(
763 Clone,
764 Copy,
765 Debug,
766 Deserialize,
767 MallocSizeOf,
768 PartialEq,
769 Serialize,
770 SpecifiedValueInfo,
771 ToAnimatedValue,
772 ToAnimatedZero,
773 ToComputedValue,
774 ToResolvedValue,
775 ToShmem,
776)]
777#[repr(C, u8)]
778pub enum GenericScale<Number> {
782 None,
784 Scale(Number, Number, Number),
786}
787
788pub use self::GenericScale as Scale;
789
790impl<Number> ToCss for Scale<Number>
791where
792 Number: ToCss + PartialEq + Copy,
793 f32: From<Number>,
794{
795 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
796 where
797 W: fmt::Write,
798 f32: From<Number>,
799 {
800 match *self {
801 Scale::None => dest.write_str("none"),
802 Scale::Scale(ref x, ref y, ref z) => {
803 x.to_css(dest)?;
804
805 let is_3d = f32::from(*z) != 1.0;
806 if is_3d || x != y {
807 dest.write_char(' ')?;
808 y.to_css(dest)?;
809 }
810
811 if is_3d {
812 dest.write_char(' ')?;
813 z.to_css(dest)?;
814 }
815 Ok(())
816 },
817 }
818 }
819}
820
821#[inline]
822fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(
823 _: &LengthPercentage,
824 y: &LengthPercentage,
825 z: &Length,
826) -> bool {
827 y.is_zero_no_percent() && z.is_zero()
828}
829
830#[derive(
831 Clone,
832 Debug,
833 Deserialize,
834 MallocSizeOf,
835 PartialEq,
836 Serialize,
837 SpecifiedValueInfo,
838 ToAnimatedValue,
839 ToAnimatedZero,
840 ToComputedValue,
841 ToCss,
842 ToResolvedValue,
843 ToShmem,
844)]
845#[repr(C, u8)]
846pub enum GenericTranslate<LengthPercentage, Length>
860where
861 LengthPercentage: Zero + ZeroNoPercent,
862 Length: Zero,
863{
864 None,
866 Translate(
868 LengthPercentage,
869 #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
870 #[css(skip_if = "Zero::is_zero")] Length,
871 ),
872}
873
874pub use self::GenericTranslate as Translate;
875
876#[allow(missing_docs)]
877#[derive(
878 Clone,
879 Copy,
880 Debug,
881 MallocSizeOf,
882 Parse,
883 PartialEq,
884 SpecifiedValueInfo,
885 ToComputedValue,
886 ToCss,
887 ToResolvedValue,
888 ToShmem,
889)]
890#[repr(u8)]
891pub enum TransformStyle {
892 Flat,
893 #[css(keyword = "preserve-3d")]
894 Preserve3d,
895}