Skip to main content

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