Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/servo/components/style/values/specified/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 25 kB image not shown  

Quelle  transform.rs

  Sprache: Rust
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */


//! Specified types for CSS values that are related to transformations.

use crate::derives::*;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::{Context, LengthPercentage as ComputedLengthPercentage};
use crate::values::computed::{Percentage as ComputedPercentage, ToComputedValue};
use crate::values::generics::transform as generic;
use crate::values::generics::transform::{Matrix, Matrix3D, ToFloat};
use crate::values::specified::percentage::NoCalcPercentage;
use crate::values::specified::position::{
    HorizontalPositionKeyword, Side, VerticalPositionKeyword,
};
use crate::values::specified::{
    self, AllowQuirks, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage,
};
use crate::Zero;
use cssparser::{match_ignore_ascii_case, Parser};
use style_traits::{ParseError, StyleParseErrorKind};

pub use crate::values::generics::transform::TransformStyle;

/// A single operation in a specified CSS `transform`
pub type TransformOperation =
    generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;

/// A specified CSS `transform`
pub type Transform = generic::Transform<TransformOperation>;

/// The specified value of a CSS `<transform-origin>`
pub type TransformOrigin = generic::TransformOrigin<
    OriginComponent<HorizontalPositionKeyword>,
    OriginComponent<VerticalPositionKeyword>,
    Length,
>;

#[cfg(feature = "gecko")]
fn all_transform_boxes_are_enabled(_context: &ParserContext) -> bool {
    true
}

#[cfg(feature = "servo")]
fn all_transform_boxes_are_enabled(_context: &ParserContext) -> bool {
    false
}

/// The specified value of `transform-box`.
/// https://drafts.csswg.org/css-transforms-1/#transform-box
// Note: Once we ship everything, we can drop this and just use single_keyword for tranform-box.
#[allow(missing_docs)]
#[derive(
    Animate,
    Clone,
    ComputeSquaredDistance,
    Copy,
    Debug,
    Deserialize,
    MallocSizeOf,
    Parse,
    PartialEq,
    Serialize,
    SpecifiedValueInfo,
    ToAnimatedValue,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
    ToTyped,
)]
#[repr(u8)]
pub enum TransformBox {
    #[parse(condition = "all_transform_boxes_are_enabled")]
    ContentBox,
    BorderBox,
    FillBox,
    #[parse(condition = "all_transform_boxes_are_enabled")]
    StrokeBox,
    ViewBox,
}

impl TransformOrigin {
    /// Returns the initial specified value for `transform-origin`.
    #[inline]
    pub fn initial_value() -> Self {
        Self::new(
            OriginComponent::Length(LengthPercentage::Percentage(NoCalcPercentage::new(0.5))),
            OriginComponent::Length(LengthPercentage::Percentage(NoCalcPercentage::new(0.5))),
            Length::zero(),
        )
    }

    /// Returns the `0 0` value.
    pub fn zero_zero() -> Self {
        Self::new(
            OriginComponent::Length(LengthPercentage::zero()),
            OriginComponent::Length(LengthPercentage::zero()),
            Length::zero(),
        )
    }
}

/// Whether to allow unitless values for perspective in prefixed transform properties.
///
/// See: https://github.com/whatwg/compat/issues/100
#[allow(missing_docs)]
pub enum AllowUnitlessPerspective {
    No,
    Yes,
}

impl Transform {
    /// Parse the transform property value, allowing unitless perspective values.
    ///
    /// This is used for `-webkit-transform` which allows unitless values for perspective.
    #[inline]
    pub(cratefn parse_legacy<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Self::parse_internal(context, input, AllowUnitlessPerspective::Yes)
    }
    /// Internal parse function for deciding if we wish to accept prefixed values or not
    ///
    /// `transform` allows unitless zero angles as an exception, see:
    /// https://github.com/w3c/csswg-drafts/issues/1162
    fn parse_internal<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        allow_unitless_perspective: AllowUnitlessPerspective,
    ) -> Result<Self, ParseError<'i>> {
        use style_traits::{Separator, Space};

        if input
            .try_parse(|input| input.expect_ident_matching("none"))
            .is_ok()
        {
            return Ok(generic::Transform::none());
        }

        Ok(generic::Transform(
            Space::parse(input, |input| {
                let function = input.expect_function()?.clone();
                input.parse_nested_block(|input| {
                    let location = input.current_source_location();
                    let result = match_ignore_ascii_case! { &function,
                        "matrix" => {
                            let a = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let b = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let c = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let d = Number::parse(context, input)?;
                            input.expect_comma()?;
                            // Standard matrix parsing.
                            let e = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let f = Number::parse(context, input)?;
                            Ok(generic::TransformOperation::Matrix(Matrix { a, b, c, d, e, f }))
                        },
                        "matrix3d" => {
                            let m11 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m12 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m13 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m14 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m21 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m22 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m23 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m24 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m31 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m32 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m33 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m34 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            // Standard matrix3d parsing.
                            let m41 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m42 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m43 = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let m44 = Number::parse(context, input)?;
                            Ok(generic::TransformOperation::Matrix3D(Matrix3D {
                                m11, m12, m13, m14,
                                m21, m22, m23, m24,
                                m31, m32, m33, m34,
                                m41, m42, m43, m44,
                            }))
                        },
                        "translate" => {
                            let sx = specified::LengthPercentage::parse(context, input)?;
                            if input.try_parse(|input| input.expect_comma()).is_ok() {
                                let sy = specified::LengthPercentage::parse(context, input)?;
                                Ok(generic::TransformOperation::Translate(sx, sy))
                            } else {
                                Ok(generic::TransformOperation::Translate(sx, Zero::zero()))
                            }
                        },
                        "translatex" => {
                            let tx = specified::LengthPercentage::parse(context, input)?;
                            Ok(generic::TransformOperation::TranslateX(tx))
                        },
                        "translatey" => {
                            let ty = specified::LengthPercentage::parse(context, input)?;
                            Ok(generic::TransformOperation::TranslateY(ty))
                        },
                        "translatez" => {
                            let tz = specified::Length::parse(context, input)?;
                            Ok(generic::TransformOperation::TranslateZ(tz))
                        },
                        "translate3d" => {
                            let tx = specified::LengthPercentage::parse(context, input)?;
                            input.expect_comma()?;
                            let ty = specified::LengthPercentage::parse(context, input)?;
                            input.expect_comma()?;
                            let tz = specified::Length::parse(context, input)?;
                            Ok(generic::TransformOperation::Translate3D(tx, ty, tz))
                        },
                        // TODO(Bug 2038213) - Properly handle serialization of percentages in calcs in
                        // scale values by not eagerly converting into numbers here at parse time.
                        "scale" => {
                            let Some(sx) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            if input.try_parse(|input| input.expect_comma()).is_ok() {
                                let Some(sy) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                                };
                                Ok(generic::TransformOperation::Scale(sx, sy))
                            } else {
                                Ok(generic::TransformOperation::Scale(sx.clone(), sx))
                            }
                        },
                        "scalex" => {
                            let Some(sx) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            Ok(generic::TransformOperation::ScaleX(sx))
                        },
                        "scaley" => {
                            let Some(sy) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            Ok(generic::TransformOperation::ScaleY(sy))
                        },
                        "scalez" => {
                            let Some(sz) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            Ok(generic::TransformOperation::ScaleZ(sz))
                        },
                        "scale3d" => {
                            let Some(sx) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            input.expect_comma()?;
                            let Some(sy) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            input.expect_comma()?;
                            let Some(sz) = NumberOrPercentage::parse(context, input)?.to_number() else {
                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                            };
                            Ok(generic::TransformOperation::Scale3D(sx, sy, sz))
                        },
                        "rotate" => {
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            Ok(generic::TransformOperation::Rotate(theta))
                        },
                        "rotatex" => {
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            Ok(generic::TransformOperation::RotateX(theta))
                        },
                        "rotatey" => {
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            Ok(generic::TransformOperation::RotateY(theta))
                        },
                        "rotatez" => {
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            Ok(generic::TransformOperation::RotateZ(theta))
                        },
                        "rotate3d" => {
                            let ax = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let ay = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let az = Number::parse(context, input)?;
                            input.expect_comma()?;
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            // TODO(gw): Check that the axis can be normalized.
                            Ok(generic::TransformOperation::Rotate3D(ax, ay, az, theta))
                        },
                        "skew" => {
                            let ax = specified::Angle::parse_with_unitless(context, input)?;
                            if input.try_parse(|input| input.expect_comma()).is_ok() {
                                let ay = specified::Angle::parse_with_unitless(context, input)?;
                                Ok(generic::TransformOperation::Skew(ax, ay))
                            } else {
                                Ok(generic::TransformOperation::Skew(ax, Zero::zero()))
                            }
                        },
                        "skewx" => {
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            Ok(generic::TransformOperation::SkewX(theta))
                        },
                        "skewy" => {
                            let theta = specified::Angle::parse_with_unitless(context, input)?;
                            Ok(generic::TransformOperation::SkewY(theta))
                        },
                        "perspective" => {
                            let p = match input.try_parse(|input| {
                                if matches!(allow_unitless_perspective, AllowUnitlessPerspective::Yes) {
                                    specified::Length::parse_non_negative_quirky(context, input, AllowQuirks::Always)
                                } else {
                                    specified::Length::parse_non_negative(context, input)
                                }
                            }) {
                                Ok(p) => generic::PerspectiveFunction::Length(p),
                                Err(..) => {
                                    input.expect_ident_matching("none")?;
                                    generic::PerspectiveFunction::None
                                }
                            };
                            Ok(generic::TransformOperation::Perspective(p))
                        },
                        _ => Err(()),
                    };
                    result.map_err(|()| {
                        location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(
                            function.clone(),
                        ))
                    })
                })
            })?
            .into(),
        ))
    }
}

impl Parse for Transform {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Transform::parse_internal(context, input, AllowUnitlessPerspective::No)
    }
}

/// The specified value of a component of a CSS `<transform-origin>`.
#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub enum OriginComponent<S> {
    /// `center`
    Center,
    /// `<length-percentage>`
    Length(LengthPercentage),
    /// `<side>`
    Side(S),
}

impl Parse for TransformOrigin {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let parse_depth = |input: &mut Parser| {
            input
                .try_parse(|i| Length::parse(context, i))
                .unwrap_or(Length::zero())
        };
        match input.try_parse(|i| OriginComponent::parse(context, i)) {
            Ok(x_origin @ OriginComponent::Center) => {
                if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {
                    let depth = parse_depth(input);
                    return Ok(Self::new(x_origin, y_origin, depth));
                }
                let y_origin = OriginComponent::Center;
                if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {
                    let x_origin = OriginComponent::Side(x_keyword);
                    let depth = parse_depth(input);
                    return Ok(Self::new(x_origin, y_origin, depth));
                }
                let depth = Length::from_px(0.);
                return Ok(Self::new(x_origin, y_origin, depth));
            },
            Ok(x_origin) => {
                if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {
                    let depth = parse_depth(input);
                    return Ok(Self::new(x_origin, y_origin, depth));
                }
                let y_origin = OriginComponent::Center;
                let depth = Length::from_px(0.);
                return Ok(Self::new(x_origin, y_origin, depth));
            },
            Err(_) => {},
        }
        let y_keyword = VerticalPositionKeyword::parse(input)?;
        let y_origin = OriginComponent::Side(y_keyword);
        if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {
            let x_origin = OriginComponent::Side(x_keyword);
            let depth = parse_depth(input);
            return Ok(Self::new(x_origin, y_origin, depth));
        }
        if input
            .try_parse(|i| i.expect_ident_matching("center"))
            .is_ok()
        {
            let x_origin = OriginComponent::Center;
            let depth = parse_depth(input);
            return Ok(Self::new(x_origin, y_origin, depth));
        }
        let x_origin = OriginComponent::Center;
        let depth = Length::from_px(0.);
        Ok(Self::new(x_origin, y_origin, depth))
    }
}

impl<S> ToComputedValue for OriginComponent<S>
where
    S: Side,
{
    type ComputedValue = ComputedLengthPercentage;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            OriginComponent::Center => {
                ComputedLengthPercentage::new_percent(ComputedPercentage(0.5))
            },
            OriginComponent::Length(ref length) => length.to_computed_value(context),
            OriginComponent::Side(ref keyword) => {
                let p = ComputedPercentage(if keyword.is_start() { 0. } else { 1. });
                ComputedLengthPercentage::new_percent(p)
            },
        }
    }

    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        OriginComponent::Length(ToComputedValue::from_computed_value(computed))
    }
}

impl<S> OriginComponent<S> {
    /// `0%`
    pub fn zero() -> Self {
        OriginComponent::Length(LengthPercentage::Percentage(NoCalcPercentage::new(0.)))
    }
}

/// A specified CSS `rotate`
pub type Rotate = generic::Rotate<Number, Angle>;

impl Parse for Rotate {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
            return Ok(generic::Rotate::None);
        }

        // Parse <angle> or [ x | y | z | <number>{3} ] && <angle>.
        //
        // The rotate axis and angle could be in any order, so we parse angle twice to cover
        // two cases. i.e. `<number>{3} <angle>` or `<angle> <number>{3}`
        let angle = input
            .try_parse(|i| specified::Angle::parse(context, i))
            .ok();
        let axis = input
            .try_parse(|i| {
                Ok(try_match_ident_ignore_ascii_case! { i,
                    "x" => (Number::new(1.), Number::new(0.), Number::new(0.)),
                    "y" => (Number::new(0.), Number::new(1.), Number::new(0.)),
                    "z" => (Number::new(0.), Number::new(0.), Number::new(1.)),
                })
            })
            .or_else(|_: ParseError| -> Result<_, ParseError> {
                input.try_parse(|i| {
                    Ok((
                        Number::parse(context, i)?,
                        Number::parse(context, i)?,
                        Number::parse(context, i)?,
                    ))
                })
            })
            .ok();
        let angle = match angle {
            Some(a) => a,
            None => specified::Angle::parse(context, input)?,
        };

        Ok(match axis {
            Some((x, y, z)) => generic::Rotate::Rotate3D(x, y, z, angle),
            None => generic::Rotate::Rotate(angle),
        })
    }
}

/// A specified CSS `translate`
pub type Translate = generic::Translate<LengthPercentage, Length>;

impl Parse for Translate {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
            return Ok(generic::Translate::None);
        }

        let tx = specified::LengthPercentage::parse(context, input)?;
        if let Ok(ty) = input.try_parse(|i| specified::LengthPercentage::parse(context, i)) {
            if let Ok(tz) = input.try_parse(|i| specified::Length::parse(context, i)) {
                // 'translate: <length-percentage> <length-percentage> <length>'
                return Ok(generic::Translate::Translate(tx, ty, tz));
            }

            // translate: <length-percentage> <length-percentage>'
            return Ok(generic::Translate::Translate(
                tx,
                ty,
                specified::Length::zero(),
            ));
        }

        // 'translate: <length-percentage> '
        Ok(generic::Translate::Translate(
            tx,
            specified::LengthPercentage::zero(),
            specified::Length::zero(),
        ))
    }
}

/// An individual value that can appear in a CSS `scale`.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
pub struct ScaleFactor(NumberOrPercentage);

impl ScaleFactor {
    fn one() -> Self {
        ScaleFactor(NumberOrPercentage::Number(Number::new(1.0)))
    }
}

impl Parse for ScaleFactor {
    /// Scale accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
    /// and then convert into an Number if it's a non-calc Percentage.
    /// https://github.com/w3c/csswg-drafts/pull/4396
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        Ok(ScaleFactor(
            NumberOrPercentage::parse(context, input)?.into_simplified_number(),
        ))
    }
}

impl ToComputedValue for ScaleFactor {
    type ComputedValue = <Number as ToComputedValue>::ComputedValue;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        self.0.to_computed_value(context).value()
    }

    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        ScaleFactor(NumberOrPercentage::Number(Number::from_computed_value(
            computed,
        )))
    }
}

impl ToFloat for ScaleFactor {
    fn to_f32(&self) -> Result<f32, ()> {
        match &self.0 {
            NumberOrPercentage::Number(n) => n.resolve().ok_or(()),
            NumberOrPercentage::Percentage(p) => p.resolve().ok_or(()),
        }
    }

    fn to_f64(&self) -> Result<f64, ()> {
        self.to_f32().map(|v| v as f64)
    }
}

/// A specified CSS `scale`
pub type Scale = generic::Scale<ScaleFactor>;

impl Parse for Scale {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
            return Ok(generic::Scale::None);
        }

        let sx = ScaleFactor::parse(context, input)?;
        if let Ok(sy) = input.try_parse(|i| ScaleFactor::parse(context, i)) {
            if let Ok(sz) = input.try_parse(|i| ScaleFactor::parse(context, i)) {
                return Ok(generic::Scale::Scale(sx, sy, sz));
            }
            return Ok(generic::Scale::Scale(sx, sy, ScaleFactor::one()));
        }
        Ok(generic::Scale::Scale(sx.clone(), sx, ScaleFactor::one()))
    }
}

Messung V0.5 in Prozent
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-08-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.