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