style/values/generics/
length.rs

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