1use crate::parser::{Parse, ParserContext};
8use crate::values::computed::{Context, LengthPercentage as ComputedLengthPercentage};
9use crate::values::computed::{Percentage as ComputedPercentage, ToComputedValue};
10use crate::values::generics::transform as generic;
11use crate::values::generics::transform::{Matrix, Matrix3D};
12use crate::values::specified::position::{
13 HorizontalPositionKeyword, Side, VerticalPositionKeyword,
14};
15use crate::values::specified::{
16 self, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage,
17};
18use crate::Zero;
19use cssparser::Parser;
20use style_traits::{ParseError, StyleParseErrorKind};
21
22pub use crate::values::generics::transform::TransformStyle;
23
24pub type TransformOperation =
26 generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
27
28pub type Transform = generic::Transform<TransformOperation>;
30
31pub type TransformOrigin = generic::TransformOrigin<
33 OriginComponent<HorizontalPositionKeyword>,
34 OriginComponent<VerticalPositionKeyword>,
35 Length,
36>;
37
38#[cfg(feature = "gecko")]
39fn all_transform_boxes_are_enabled(_context: &ParserContext) -> bool {
40 true
41}
42
43#[cfg(feature = "servo")]
44fn all_transform_boxes_are_enabled(_context: &ParserContext) -> bool {
45 false
46}
47
48#[allow(missing_docs)]
52#[derive(
53 Animate,
54 Clone,
55 ComputeSquaredDistance,
56 Copy,
57 Debug,
58 Deserialize,
59 MallocSizeOf,
60 Parse,
61 PartialEq,
62 Serialize,
63 SpecifiedValueInfo,
64 ToAnimatedValue,
65 ToComputedValue,
66 ToCss,
67 ToResolvedValue,
68 ToShmem,
69 ToTyped,
70)]
71#[repr(u8)]
72pub enum TransformBox {
73 #[parse(condition = "all_transform_boxes_are_enabled")]
74 ContentBox,
75 BorderBox,
76 FillBox,
77 #[parse(condition = "all_transform_boxes_are_enabled")]
78 StrokeBox,
79 ViewBox,
80}
81
82impl TransformOrigin {
83 #[inline]
85 pub fn initial_value() -> Self {
86 Self::new(
87 OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))),
88 OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))),
89 Length::zero(),
90 )
91 }
92
93 pub fn zero_zero() -> Self {
95 Self::new(
96 OriginComponent::Length(LengthPercentage::zero()),
97 OriginComponent::Length(LengthPercentage::zero()),
98 Length::zero(),
99 )
100 }
101}
102
103impl Transform {
104 fn parse_internal<'i, 't>(
109 context: &ParserContext,
110 input: &mut Parser<'i, 't>,
111 ) -> Result<Self, ParseError<'i>> {
112 use style_traits::{Separator, Space};
113
114 if input
115 .try_parse(|input| input.expect_ident_matching("none"))
116 .is_ok()
117 {
118 return Ok(generic::Transform::none());
119 }
120
121 Ok(generic::Transform(
122 Space::parse(input, |input| {
123 let function = input.expect_function()?.clone();
124 input.parse_nested_block(|input| {
125 let location = input.current_source_location();
126 let result = match_ignore_ascii_case! { &function,
127 "matrix" => {
128 let a = Number::parse(context, input)?;
129 input.expect_comma()?;
130 let b = Number::parse(context, input)?;
131 input.expect_comma()?;
132 let c = Number::parse(context, input)?;
133 input.expect_comma()?;
134 let d = Number::parse(context, input)?;
135 input.expect_comma()?;
136 let e = Number::parse(context, input)?;
138 input.expect_comma()?;
139 let f = Number::parse(context, input)?;
140 Ok(generic::TransformOperation::Matrix(Matrix { a, b, c, d, e, f }))
141 },
142 "matrix3d" => {
143 let m11 = Number::parse(context, input)?;
144 input.expect_comma()?;
145 let m12 = Number::parse(context, input)?;
146 input.expect_comma()?;
147 let m13 = Number::parse(context, input)?;
148 input.expect_comma()?;
149 let m14 = Number::parse(context, input)?;
150 input.expect_comma()?;
151 let m21 = Number::parse(context, input)?;
152 input.expect_comma()?;
153 let m22 = Number::parse(context, input)?;
154 input.expect_comma()?;
155 let m23 = Number::parse(context, input)?;
156 input.expect_comma()?;
157 let m24 = Number::parse(context, input)?;
158 input.expect_comma()?;
159 let m31 = Number::parse(context, input)?;
160 input.expect_comma()?;
161 let m32 = Number::parse(context, input)?;
162 input.expect_comma()?;
163 let m33 = Number::parse(context, input)?;
164 input.expect_comma()?;
165 let m34 = Number::parse(context, input)?;
166 input.expect_comma()?;
167 let m41 = Number::parse(context, input)?;
169 input.expect_comma()?;
170 let m42 = Number::parse(context, input)?;
171 input.expect_comma()?;
172 let m43 = Number::parse(context, input)?;
173 input.expect_comma()?;
174 let m44 = Number::parse(context, input)?;
175 Ok(generic::TransformOperation::Matrix3D(Matrix3D {
176 m11, m12, m13, m14,
177 m21, m22, m23, m24,
178 m31, m32, m33, m34,
179 m41, m42, m43, m44,
180 }))
181 },
182 "translate" => {
183 let sx = specified::LengthPercentage::parse(context, input)?;
184 if input.try_parse(|input| input.expect_comma()).is_ok() {
185 let sy = specified::LengthPercentage::parse(context, input)?;
186 Ok(generic::TransformOperation::Translate(sx, sy))
187 } else {
188 Ok(generic::TransformOperation::Translate(sx, Zero::zero()))
189 }
190 },
191 "translatex" => {
192 let tx = specified::LengthPercentage::parse(context, input)?;
193 Ok(generic::TransformOperation::TranslateX(tx))
194 },
195 "translatey" => {
196 let ty = specified::LengthPercentage::parse(context, input)?;
197 Ok(generic::TransformOperation::TranslateY(ty))
198 },
199 "translatez" => {
200 let tz = specified::Length::parse(context, input)?;
201 Ok(generic::TransformOperation::TranslateZ(tz))
202 },
203 "translate3d" => {
204 let tx = specified::LengthPercentage::parse(context, input)?;
205 input.expect_comma()?;
206 let ty = specified::LengthPercentage::parse(context, input)?;
207 input.expect_comma()?;
208 let tz = specified::Length::parse(context, input)?;
209 Ok(generic::TransformOperation::Translate3D(tx, ty, tz))
210 },
211 "scale" => {
212 let sx = NumberOrPercentage::parse(context, input)?.to_number();
213 if input.try_parse(|input| input.expect_comma()).is_ok() {
214 let sy = NumberOrPercentage::parse(context, input)?.to_number();
215 Ok(generic::TransformOperation::Scale(sx, sy))
216 } else {
217 Ok(generic::TransformOperation::Scale(sx, sx))
218 }
219 },
220 "scalex" => {
221 let sx = NumberOrPercentage::parse(context, input)?.to_number();
222 Ok(generic::TransformOperation::ScaleX(sx))
223 },
224 "scaley" => {
225 let sy = NumberOrPercentage::parse(context, input)?.to_number();
226 Ok(generic::TransformOperation::ScaleY(sy))
227 },
228 "scalez" => {
229 let sz = NumberOrPercentage::parse(context, input)?.to_number();
230 Ok(generic::TransformOperation::ScaleZ(sz))
231 },
232 "scale3d" => {
233 let sx = NumberOrPercentage::parse(context, input)?.to_number();
234 input.expect_comma()?;
235 let sy = NumberOrPercentage::parse(context, input)?.to_number();
236 input.expect_comma()?;
237 let sz = NumberOrPercentage::parse(context, input)?.to_number();
238 Ok(generic::TransformOperation::Scale3D(sx, sy, sz))
239 },
240 "rotate" => {
241 let theta = specified::Angle::parse_with_unitless(context, input)?;
242 Ok(generic::TransformOperation::Rotate(theta))
243 },
244 "rotatex" => {
245 let theta = specified::Angle::parse_with_unitless(context, input)?;
246 Ok(generic::TransformOperation::RotateX(theta))
247 },
248 "rotatey" => {
249 let theta = specified::Angle::parse_with_unitless(context, input)?;
250 Ok(generic::TransformOperation::RotateY(theta))
251 },
252 "rotatez" => {
253 let theta = specified::Angle::parse_with_unitless(context, input)?;
254 Ok(generic::TransformOperation::RotateZ(theta))
255 },
256 "rotate3d" => {
257 let ax = Number::parse(context, input)?;
258 input.expect_comma()?;
259 let ay = Number::parse(context, input)?;
260 input.expect_comma()?;
261 let az = Number::parse(context, input)?;
262 input.expect_comma()?;
263 let theta = specified::Angle::parse_with_unitless(context, input)?;
264 Ok(generic::TransformOperation::Rotate3D(ax, ay, az, theta))
266 },
267 "skew" => {
268 let ax = specified::Angle::parse_with_unitless(context, input)?;
269 if input.try_parse(|input| input.expect_comma()).is_ok() {
270 let ay = specified::Angle::parse_with_unitless(context, input)?;
271 Ok(generic::TransformOperation::Skew(ax, ay))
272 } else {
273 Ok(generic::TransformOperation::Skew(ax, Zero::zero()))
274 }
275 },
276 "skewx" => {
277 let theta = specified::Angle::parse_with_unitless(context, input)?;
278 Ok(generic::TransformOperation::SkewX(theta))
279 },
280 "skewy" => {
281 let theta = specified::Angle::parse_with_unitless(context, input)?;
282 Ok(generic::TransformOperation::SkewY(theta))
283 },
284 "perspective" => {
285 let p = match input.try_parse(|input| specified::Length::parse_non_negative(context, input)) {
286 Ok(p) => generic::PerspectiveFunction::Length(p),
287 Err(..) => {
288 input.expect_ident_matching("none")?;
289 generic::PerspectiveFunction::None
290 }
291 };
292 Ok(generic::TransformOperation::Perspective(p))
293 },
294 _ => Err(()),
295 };
296 result.map_err(|()| {
297 location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(
298 function.clone(),
299 ))
300 })
301 })
302 })?
303 .into(),
304 ))
305 }
306}
307
308impl Parse for Transform {
309 fn parse<'i, 't>(
310 context: &ParserContext,
311 input: &mut Parser<'i, 't>,
312 ) -> Result<Self, ParseError<'i>> {
313 Transform::parse_internal(context, input)
314 }
315}
316
317#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
319pub enum OriginComponent<S> {
320 Center,
322 Length(LengthPercentage),
324 Side(S),
326}
327
328impl Parse for TransformOrigin {
329 fn parse<'i, 't>(
330 context: &ParserContext,
331 input: &mut Parser<'i, 't>,
332 ) -> Result<Self, ParseError<'i>> {
333 let parse_depth = |input: &mut Parser| {
334 input
335 .try_parse(|i| Length::parse(context, i))
336 .unwrap_or(Length::zero())
337 };
338 match input.try_parse(|i| OriginComponent::parse(context, i)) {
339 Ok(x_origin @ OriginComponent::Center) => {
340 if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {
341 let depth = parse_depth(input);
342 return Ok(Self::new(x_origin, y_origin, depth));
343 }
344 let y_origin = OriginComponent::Center;
345 if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {
346 let x_origin = OriginComponent::Side(x_keyword);
347 let depth = parse_depth(input);
348 return Ok(Self::new(x_origin, y_origin, depth));
349 }
350 let depth = Length::from_px(0.);
351 return Ok(Self::new(x_origin, y_origin, depth));
352 },
353 Ok(x_origin) => {
354 if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {
355 let depth = parse_depth(input);
356 return Ok(Self::new(x_origin, y_origin, depth));
357 }
358 let y_origin = OriginComponent::Center;
359 let depth = Length::from_px(0.);
360 return Ok(Self::new(x_origin, y_origin, depth));
361 },
362 Err(_) => {},
363 }
364 let y_keyword = VerticalPositionKeyword::parse(input)?;
365 let y_origin = OriginComponent::Side(y_keyword);
366 if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {
367 let x_origin = OriginComponent::Side(x_keyword);
368 let depth = parse_depth(input);
369 return Ok(Self::new(x_origin, y_origin, depth));
370 }
371 if input
372 .try_parse(|i| i.expect_ident_matching("center"))
373 .is_ok()
374 {
375 let x_origin = OriginComponent::Center;
376 let depth = parse_depth(input);
377 return Ok(Self::new(x_origin, y_origin, depth));
378 }
379 let x_origin = OriginComponent::Center;
380 let depth = Length::from_px(0.);
381 Ok(Self::new(x_origin, y_origin, depth))
382 }
383}
384
385impl<S> ToComputedValue for OriginComponent<S>
386where
387 S: Side,
388{
389 type ComputedValue = ComputedLengthPercentage;
390
391 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
392 match *self {
393 OriginComponent::Center => {
394 ComputedLengthPercentage::new_percent(ComputedPercentage(0.5))
395 },
396 OriginComponent::Length(ref length) => length.to_computed_value(context),
397 OriginComponent::Side(ref keyword) => {
398 let p = ComputedPercentage(if keyword.is_start() { 0. } else { 1. });
399 ComputedLengthPercentage::new_percent(p)
400 },
401 }
402 }
403
404 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
405 OriginComponent::Length(ToComputedValue::from_computed_value(computed))
406 }
407}
408
409impl<S> OriginComponent<S> {
410 pub fn zero() -> Self {
412 OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage::zero()))
413 }
414}
415
416pub type Rotate = generic::Rotate<Number, Angle>;
418
419impl Parse for Rotate {
420 fn parse<'i, 't>(
421 context: &ParserContext,
422 input: &mut Parser<'i, 't>,
423 ) -> Result<Self, ParseError<'i>> {
424 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
425 return Ok(generic::Rotate::None);
426 }
427
428 let angle = input
433 .try_parse(|i| specified::Angle::parse(context, i))
434 .ok();
435 let axis = input
436 .try_parse(|i| {
437 Ok(try_match_ident_ignore_ascii_case! { i,
438 "x" => (Number::new(1.), Number::new(0.), Number::new(0.)),
439 "y" => (Number::new(0.), Number::new(1.), Number::new(0.)),
440 "z" => (Number::new(0.), Number::new(0.), Number::new(1.)),
441 })
442 })
443 .or_else(|_: ParseError| -> Result<_, ParseError> {
444 input.try_parse(|i| {
445 Ok((
446 Number::parse(context, i)?,
447 Number::parse(context, i)?,
448 Number::parse(context, i)?,
449 ))
450 })
451 })
452 .ok();
453 let angle = match angle {
454 Some(a) => a,
455 None => specified::Angle::parse(context, input)?,
456 };
457
458 Ok(match axis {
459 Some((x, y, z)) => generic::Rotate::Rotate3D(x, y, z, angle),
460 None => generic::Rotate::Rotate(angle),
461 })
462 }
463}
464
465pub type Translate = generic::Translate<LengthPercentage, Length>;
467
468impl Parse for Translate {
469 fn parse<'i, 't>(
470 context: &ParserContext,
471 input: &mut Parser<'i, 't>,
472 ) -> Result<Self, ParseError<'i>> {
473 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
474 return Ok(generic::Translate::None);
475 }
476
477 let tx = specified::LengthPercentage::parse(context, input)?;
478 if let Ok(ty) = input.try_parse(|i| specified::LengthPercentage::parse(context, i)) {
479 if let Ok(tz) = input.try_parse(|i| specified::Length::parse(context, i)) {
480 return Ok(generic::Translate::Translate(tx, ty, tz));
482 }
483
484 return Ok(generic::Translate::Translate(
486 tx,
487 ty,
488 specified::Length::zero(),
489 ));
490 }
491
492 Ok(generic::Translate::Translate(
494 tx,
495 specified::LengthPercentage::zero(),
496 specified::Length::zero(),
497 ))
498 }
499}
500
501pub type Scale = generic::Scale<Number>;
503
504impl Parse for Scale {
505 fn parse<'i, 't>(
509 context: &ParserContext,
510 input: &mut Parser<'i, 't>,
511 ) -> Result<Self, ParseError<'i>> {
512 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
513 return Ok(generic::Scale::None);
514 }
515
516 let sx = NumberOrPercentage::parse(context, input)?.to_number();
517 if let Ok(sy) = input.try_parse(|i| NumberOrPercentage::parse(context, i)) {
518 let sy = sy.to_number();
519 if let Ok(sz) = input.try_parse(|i| NumberOrPercentage::parse(context, i)) {
520 return Ok(generic::Scale::Scale(sx, sy, sz.to_number()));
522 }
523
524 return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0)));
526 }
527
528 Ok(generic::Scale::Scale(sx, sx, Number::new(1.0)))
530 }
531}