tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

length.rs (18200B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! Generic types for CSS values related to length.
      6 
      7 use crate::derives::*;
      8 use crate::logical_geometry::PhysicalSide;
      9 use crate::parser::{Parse, ParserContext};
     10 use crate::values::computed::position::TryTacticAdjustment;
     11 use crate::values::generics::box_::PositionProperty;
     12 use crate::values::generics::Optional;
     13 use crate::values::DashedIdent;
     14 use crate::Zero;
     15 use cssparser::Parser;
     16 use std::fmt::Write;
     17 use style_traits::ParseError;
     18 use style_traits::StyleParseErrorKind;
     19 use style_traits::ToCss;
     20 use style_traits::{CssWriter, SpecifiedValueInfo};
     21 
     22 /// A `<length-percentage> | auto` value.
     23 #[allow(missing_docs)]
     24 #[derive(
     25    Animate,
     26    Clone,
     27    ComputeSquaredDistance,
     28    Copy,
     29    Debug,
     30    MallocSizeOf,
     31    PartialEq,
     32    SpecifiedValueInfo,
     33    ToAnimatedValue,
     34    ToAnimatedZero,
     35    ToComputedValue,
     36    ToCss,
     37    ToResolvedValue,
     38    ToShmem,
     39    ToTyped,
     40 )]
     41 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
     42 #[repr(C, u8)]
     43 #[typed_value(derive_fields)]
     44 pub enum GenericLengthPercentageOrAuto<LengthPercent> {
     45    LengthPercentage(LengthPercent),
     46    Auto,
     47 }
     48 
     49 pub use self::GenericLengthPercentageOrAuto as LengthPercentageOrAuto;
     50 
     51 impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> {
     52    /// `auto` value.
     53    #[inline]
     54    pub fn auto() -> Self {
     55        LengthPercentageOrAuto::Auto
     56    }
     57 
     58    /// Whether this is the `auto` value.
     59    #[inline]
     60    pub fn is_auto(&self) -> bool {
     61        matches!(*self, LengthPercentageOrAuto::Auto)
     62    }
     63 
     64    /// A helper function to parse this with quirks or not and so forth.
     65    pub fn parse_with<'i, 't>(
     66        context: &ParserContext,
     67        input: &mut Parser<'i, 't>,
     68        parser: impl FnOnce(
     69            &ParserContext,
     70            &mut Parser<'i, 't>,
     71        ) -> Result<LengthPercentage, ParseError<'i>>,
     72    ) -> Result<Self, ParseError<'i>> {
     73        if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
     74            return Ok(LengthPercentageOrAuto::Auto);
     75        }
     76 
     77        Ok(LengthPercentageOrAuto::LengthPercentage(parser(
     78            context, input,
     79        )?))
     80    }
     81 }
     82 
     83 impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage>
     84 where
     85    LengthPercentage: Clone,
     86 {
     87    /// Resolves `auto` values by calling `f`.
     88    #[inline]
     89    pub fn auto_is(&self, f: impl FnOnce() -> LengthPercentage) -> LengthPercentage {
     90        match self {
     91            LengthPercentageOrAuto::LengthPercentage(length) => length.clone(),
     92            LengthPercentageOrAuto::Auto => f(),
     93        }
     94    }
     95 
     96    /// Returns the non-`auto` value, if any.
     97    #[inline]
     98    pub fn non_auto(&self) -> Option<LengthPercentage> {
     99        match self {
    100            LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()),
    101            LengthPercentageOrAuto::Auto => None,
    102        }
    103    }
    104 
    105    /// Maps the length of this value.
    106    pub fn map<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> {
    107        match self {
    108            LengthPercentageOrAuto::LengthPercentage(l) => {
    109                LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
    110            },
    111            LengthPercentageOrAuto::Auto => LengthPercentageOrAuto::Auto,
    112        }
    113    }
    114 }
    115 
    116 impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {
    117    fn zero() -> Self {
    118        LengthPercentageOrAuto::LengthPercentage(Zero::zero())
    119    }
    120 
    121    fn is_zero(&self) -> bool {
    122        match *self {
    123            LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(),
    124            LengthPercentageOrAuto::Auto => false,
    125        }
    126    }
    127 }
    128 
    129 impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> {
    130    fn parse<'i, 't>(
    131        context: &ParserContext,
    132        input: &mut Parser<'i, 't>,
    133    ) -> Result<Self, ParseError<'i>> {
    134        Self::parse_with(context, input, LengthPercentage::parse)
    135    }
    136 }
    137 
    138 /// A generic value for the `width`, `height`, `min-width`, or `min-height` property.
    139 ///
    140 /// Unlike `max-width` or `max-height` properties, a Size can be `auto`,
    141 /// and cannot be `none`.
    142 ///
    143 /// Note that it only accepts non-negative values.
    144 #[allow(missing_docs)]
    145 #[derive(
    146    Animate,
    147    ComputeSquaredDistance,
    148    Clone,
    149    Debug,
    150    MallocSizeOf,
    151    PartialEq,
    152    ToAnimatedValue,
    153    ToAnimatedZero,
    154    ToComputedValue,
    155    ToCss,
    156    ToResolvedValue,
    157    ToShmem,
    158    ToTyped,
    159 )]
    160 #[repr(C, u8)]
    161 pub enum GenericSize<LengthPercent> {
    162    LengthPercentage(LengthPercent),
    163    Auto,
    164    #[animation(error)]
    165    MaxContent,
    166    #[animation(error)]
    167    MinContent,
    168    #[animation(error)]
    169    FitContent,
    170    #[cfg(feature = "gecko")]
    171    #[animation(error)]
    172    MozAvailable,
    173    #[animation(error)]
    174    WebkitFillAvailable,
    175    #[animation(error)]
    176    Stretch,
    177    #[animation(error)]
    178    #[css(function = "fit-content")]
    179    FitContentFunction(LengthPercent),
    180    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
    181    AnchorContainingCalcFunction(LengthPercent),
    182 }
    183 
    184 impl<LengthPercent> SpecifiedValueInfo for GenericSize<LengthPercent>
    185 where
    186    LengthPercent: SpecifiedValueInfo,
    187 {
    188    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
    189        LengthPercent::collect_completion_keywords(f);
    190        f(&["auto", "fit-content", "max-content", "min-content"]);
    191        if cfg!(feature = "gecko") {
    192            f(&["-moz-available"]);
    193        }
    194        if static_prefs::pref!("layout.css.stretch-size-keyword.enabled") {
    195            f(&["stretch"]);
    196        }
    197        if static_prefs::pref!("layout.css.webkit-fill-available.enabled") {
    198            f(&["-webkit-fill-available"]);
    199        }
    200        if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
    201            f(&["anchor-size"]);
    202        }
    203    }
    204 }
    205 
    206 pub use self::GenericSize as Size;
    207 
    208 impl<LengthPercentage> Size<LengthPercentage> {
    209    /// `auto` value.
    210    #[inline]
    211    pub fn auto() -> Self {
    212        Size::Auto
    213    }
    214 
    215    /// Returns whether we're the auto value.
    216    #[inline]
    217    pub fn is_auto(&self) -> bool {
    218        matches!(*self, Size::Auto)
    219    }
    220 }
    221 
    222 /// A generic value for the `max-width` or `max-height` property.
    223 #[allow(missing_docs)]
    224 #[derive(
    225    Animate,
    226    Clone,
    227    ComputeSquaredDistance,
    228    Debug,
    229    MallocSizeOf,
    230    PartialEq,
    231    ToAnimatedValue,
    232    ToAnimatedZero,
    233    ToComputedValue,
    234    ToCss,
    235    ToResolvedValue,
    236    ToShmem,
    237    ToTyped,
    238 )]
    239 #[repr(C, u8)]
    240 pub enum GenericMaxSize<LengthPercent> {
    241    LengthPercentage(LengthPercent),
    242    None,
    243    #[animation(error)]
    244    MaxContent,
    245    #[animation(error)]
    246    MinContent,
    247    #[animation(error)]
    248    FitContent,
    249    #[cfg(feature = "gecko")]
    250    #[animation(error)]
    251    MozAvailable,
    252    #[animation(error)]
    253    WebkitFillAvailable,
    254    #[animation(error)]
    255    Stretch,
    256    #[animation(error)]
    257    #[css(function = "fit-content")]
    258    FitContentFunction(LengthPercent),
    259    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
    260    AnchorContainingCalcFunction(LengthPercent),
    261 }
    262 
    263 impl<LP> SpecifiedValueInfo for GenericMaxSize<LP>
    264 where
    265    LP: SpecifiedValueInfo,
    266 {
    267    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
    268        LP::collect_completion_keywords(f);
    269        f(&["none", "fit-content", "max-content", "min-content"]);
    270        if cfg!(feature = "gecko") {
    271            f(&["-moz-available"]);
    272        }
    273        if static_prefs::pref!("layout.css.stretch-size-keyword.enabled") {
    274            f(&["stretch"]);
    275        }
    276        if static_prefs::pref!("layout.css.webkit-fill-available.enabled") {
    277            f(&["-webkit-fill-available"]);
    278        }
    279        if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
    280            f(&["anchor-size"]);
    281        }
    282    }
    283 }
    284 
    285 pub use self::GenericMaxSize as MaxSize;
    286 
    287 impl<LengthPercentage> MaxSize<LengthPercentage> {
    288    /// `none` value.
    289    #[inline]
    290    pub fn none() -> Self {
    291        MaxSize::None
    292    }
    293 }
    294 
    295 /// A generic `<length>` | `<number>` value for the `tab-size` property.
    296 #[derive(
    297    Animate,
    298    Clone,
    299    ComputeSquaredDistance,
    300    Copy,
    301    Debug,
    302    MallocSizeOf,
    303    Parse,
    304    PartialEq,
    305    SpecifiedValueInfo,
    306    ToAnimatedValue,
    307    ToAnimatedZero,
    308    ToComputedValue,
    309    ToCss,
    310    ToResolvedValue,
    311    ToShmem,
    312    ToTyped,
    313 )]
    314 #[repr(C, u8)]
    315 pub enum GenericLengthOrNumber<L, N> {
    316    /// A number.
    317    ///
    318    /// NOTE: Numbers need to be before lengths, in order to parse them
    319    /// first, since `0` should be a number, not the `0px` length.
    320    Number(N),
    321    /// A length.
    322    Length(L),
    323 }
    324 
    325 pub use self::GenericLengthOrNumber as LengthOrNumber;
    326 
    327 impl<L, N: Zero> Zero for LengthOrNumber<L, N> {
    328    fn zero() -> Self {
    329        LengthOrNumber::Number(Zero::zero())
    330    }
    331 
    332    fn is_zero(&self) -> bool {
    333        match *self {
    334            LengthOrNumber::Number(ref n) => n.is_zero(),
    335            LengthOrNumber::Length(..) => false,
    336        }
    337    }
    338 }
    339 
    340 /// A generic `<length-percentage>` | normal` value.
    341 #[derive(
    342    Animate,
    343    Clone,
    344    ComputeSquaredDistance,
    345    Copy,
    346    Debug,
    347    MallocSizeOf,
    348    Parse,
    349    PartialEq,
    350    SpecifiedValueInfo,
    351    ToAnimatedValue,
    352    ToAnimatedZero,
    353    ToComputedValue,
    354    ToCss,
    355    ToResolvedValue,
    356    ToShmem,
    357    ToTyped,
    358 )]
    359 #[repr(C, u8)]
    360 #[allow(missing_docs)]
    361 pub enum GenericLengthPercentageOrNormal<LengthPercent> {
    362    LengthPercentage(LengthPercent),
    363    Normal,
    364 }
    365 
    366 pub use self::GenericLengthPercentageOrNormal as LengthPercentageOrNormal;
    367 
    368 impl<LengthPercent> LengthPercentageOrNormal<LengthPercent> {
    369    /// Returns the normal value.
    370    #[inline]
    371    pub fn normal() -> Self {
    372        LengthPercentageOrNormal::Normal
    373    }
    374 }
    375 
    376 /// Anchor size function used by sizing, margin and inset properties.
    377 /// This resolves to the size of the anchor at computed time.
    378 ///
    379 /// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor-size
    380 #[derive(
    381    Animate,
    382    Clone,
    383    ComputeSquaredDistance,
    384    Debug,
    385    MallocSizeOf,
    386    PartialEq,
    387    SpecifiedValueInfo,
    388    ToShmem,
    389    ToAnimatedValue,
    390    ToAnimatedZero,
    391    ToComputedValue,
    392    ToResolvedValue,
    393    Serialize,
    394    Deserialize,
    395 )]
    396 #[repr(C)]
    397 pub struct GenericAnchorSizeFunction<Fallback> {
    398    /// Anchor name of the element to anchor to.
    399    /// If omitted (i.e. empty), selects the implicit anchor element.
    400    #[animation(constant)]
    401    pub target_element: DashedIdent,
    402    /// Size of the positioned element, expressed in that of the anchor element.
    403    /// If omitted, defaults to the axis of the property the function is used in.
    404    pub size: AnchorSizeKeyword,
    405    /// Value to use in case the anchor function is invalid.
    406    pub fallback: Optional<Fallback>,
    407 }
    408 
    409 impl<Fallback: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSizeFunction<Fallback> {
    410    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
    411        self.size.try_tactic_adjustment(old_side, new_side);
    412        if let Some(fallback) = self.fallback.as_mut() {
    413            fallback.try_tactic_adjustment(old_side, new_side);
    414        }
    415    }
    416 }
    417 
    418 impl<Fallback> ToCss for GenericAnchorSizeFunction<Fallback>
    419 where
    420    Fallback: ToCss,
    421 {
    422    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result
    423    where
    424        W: Write,
    425    {
    426        dest.write_str("anchor-size(")?;
    427        let mut previous_entry_printed = false;
    428        if !self.target_element.is_empty() {
    429            previous_entry_printed = true;
    430            self.target_element.to_css(dest)?;
    431        }
    432        if self.size != AnchorSizeKeyword::None {
    433            if previous_entry_printed {
    434                dest.write_str(" ")?;
    435            }
    436            previous_entry_printed = true;
    437            self.size.to_css(dest)?;
    438        }
    439        if let Some(f) = self.fallback.as_ref() {
    440            if previous_entry_printed {
    441                dest.write_str(", ")?;
    442            }
    443            f.to_css(dest)?;
    444        }
    445        dest.write_str(")")
    446    }
    447 }
    448 
    449 impl<Fallback> Parse for GenericAnchorSizeFunction<Fallback>
    450 where
    451    Fallback: Parse,
    452 {
    453    fn parse<'i, 't>(
    454        context: &ParserContext,
    455        input: &mut Parser<'i, 't>,
    456    ) -> Result<Self, ParseError<'i>> {
    457        if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
    458            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    459        }
    460        input.expect_function_matching("anchor-size")?;
    461        Self::parse_inner(context, input, |i| Fallback::parse(context, i))
    462    }
    463 }
    464 impl<Fallback> GenericAnchorSizeFunction<Fallback> {
    465    /// Is the anchor-size use valid for given property?
    466    pub fn valid_for(&self, position_property: PositionProperty) -> bool {
    467        position_property.is_absolutely_positioned()
    468    }
    469 }
    470 
    471 /// Result of resolving an anchor function.
    472 pub enum AnchorResolutionResult<'a, LengthPercentage> {
    473    /// Function resolved to a valid anchor.
    474    Resolved(LengthPercentage),
    475    /// Referenced anchor is invalid, but fallback is used.
    476    Fallback(&'a LengthPercentage),
    477    /// Referenced anchor is invalid.
    478    Invalid,
    479 }
    480 
    481 impl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> {
    482    /// Return result for an invalid anchor function, depending on if it has any fallback.
    483    pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self {
    484        if let Some(fb) = fallback {
    485            return Self::Fallback(fb);
    486        }
    487        Self::Invalid
    488    }
    489 }
    490 
    491 impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage> {
    492    /// Parse the inner part of `anchor-size()`, after the parser has consumed "anchor-size(".
    493    pub fn parse_inner<'i, 't, F>(
    494        context: &ParserContext,
    495        input: &mut Parser<'i, 't>,
    496        f: F,
    497    ) -> Result<Self, ParseError<'i>>
    498    where
    499        F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>,
    500    {
    501        input.parse_nested_block(|i| {
    502            let mut target_element = i
    503                .try_parse(|i| DashedIdent::parse(context, i))
    504                .unwrap_or(DashedIdent::empty());
    505            let size = i
    506                .try_parse(AnchorSizeKeyword::parse)
    507                .unwrap_or(AnchorSizeKeyword::None);
    508            if target_element.is_empty() {
    509                target_element = i
    510                    .try_parse(|i| DashedIdent::parse(context, i))
    511                    .unwrap_or(DashedIdent::empty());
    512            }
    513            let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None;
    514            let fallback = i
    515                .try_parse(|i| {
    516                    if previous_parsed {
    517                        i.expect_comma()?;
    518                    }
    519                    f(i)
    520                })
    521                .ok();
    522            Ok(GenericAnchorSizeFunction {
    523                target_element,
    524                size: size.into(),
    525                fallback: fallback.into(),
    526            })
    527        })
    528    }
    529 }
    530 
    531 /// Keyword values for the anchor size function.
    532 #[derive(
    533    Animate,
    534    Clone,
    535    ComputeSquaredDistance,
    536    Copy,
    537    Debug,
    538    MallocSizeOf,
    539    PartialEq,
    540    Parse,
    541    SpecifiedValueInfo,
    542    ToCss,
    543    ToShmem,
    544    ToAnimatedValue,
    545    ToAnimatedZero,
    546    ToComputedValue,
    547    ToResolvedValue,
    548    Serialize,
    549    Deserialize,
    550 )]
    551 #[repr(u8)]
    552 pub enum AnchorSizeKeyword {
    553    /// Magic value for nothing.
    554    #[css(skip)]
    555    None,
    556    /// Width of the anchor element.
    557    Width,
    558    /// Height of the anchor element.
    559    Height,
    560    /// Block size of the anchor element.
    561    Block,
    562    /// Inline size of the anchor element.
    563    Inline,
    564    /// Same as `Block`, resolved against the positioned element's writing mode.
    565    SelfBlock,
    566    /// Same as `Inline`, resolved against the positioned element's writing mode.
    567    SelfInline,
    568 }
    569 
    570 impl TryTacticAdjustment for AnchorSizeKeyword {
    571    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
    572        if old_side.parallel_to(new_side) {
    573            return;
    574        }
    575        *self = match *self {
    576            Self::None => Self::None,
    577            Self::Width => Self::Height,
    578            Self::Height => Self::Width,
    579            Self::Block => Self::Inline,
    580            Self::Inline => Self::Block,
    581            Self::SelfBlock => Self::SelfInline,
    582            Self::SelfInline => Self::SelfBlock,
    583        }
    584    }
    585 }
    586 
    587 /// Specified type for `margin` properties, which allows
    588 /// the use of the `anchor-size()` function.
    589 #[derive(
    590    Animate,
    591    Clone,
    592    ComputeSquaredDistance,
    593    Debug,
    594    MallocSizeOf,
    595    PartialEq,
    596    ToCss,
    597    ToShmem,
    598    ToAnimatedValue,
    599    ToAnimatedZero,
    600    ToComputedValue,
    601    ToResolvedValue,
    602    ToTyped,
    603 )]
    604 #[repr(C)]
    605 pub enum GenericMargin<LP> {
    606    /// A `<length-percentage>` value.
    607    LengthPercentage(LP),
    608    /// An `auto` value.
    609    Auto,
    610    /// Margin size defined by the anchor element.
    611    ///
    612    /// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor-size
    613    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),
    614    /// A `<length-percentage>` value, guaranteed to contain `calc()`,
    615    /// which then is guaranteed to contain `anchor()` or `anchor-size()`.
    616    AnchorContainingCalcFunction(LP),
    617 }
    618 
    619 #[cfg(feature = "servo")]
    620 impl<LP> GenericMargin<LP> {
    621    /// Return true if it is 'auto'.
    622    #[inline]
    623    pub fn is_auto(&self) -> bool {
    624        matches!(self, Self::Auto)
    625    }
    626 }
    627 
    628 impl<LP> SpecifiedValueInfo for GenericMargin<LP>
    629 where
    630    LP: SpecifiedValueInfo,
    631 {
    632    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {
    633        LP::collect_completion_keywords(f);
    634        f(&["auto"]);
    635        if static_prefs::pref!("layout.css.anchor-positioning.enabled") {
    636            f(&["anchor-size"]);
    637        }
    638    }
    639 }
    640 
    641 impl<LP> Zero for GenericMargin<LP>
    642 where
    643    LP: Zero,
    644 {
    645    fn is_zero(&self) -> bool {
    646        match self {
    647            Self::LengthPercentage(l) => l.is_zero(),
    648            Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => {
    649                false
    650            },
    651        }
    652    }
    653 
    654    fn zero() -> Self {
    655        Self::LengthPercentage(LP::zero())
    656    }
    657 }