Skip to main content

style/values/specified/
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//! [Length values][length].
6//!
7//! [length]: https://drafts.csswg.org/css-values/#lengths
8
9use super::{AllowQuirks, Number, ToComputedValue};
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::derives::*;
12use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
13#[cfg(feature = "gecko")]
14use crate::gecko_bindings::structs::GeckoFontMetrics;
15use crate::parser::{Parse, ParserContext};
16use crate::typed_om::{NumericType, NumericValue, ToTyped, TypedValue, UnitValue};
17use crate::values::CSSFloat;
18use crate::values::computed::{self, CSSPixelLength, Context, FontSize};
19use crate::values::generics::NonNegative;
20use crate::values::generics::length as generics;
21use crate::values::generics::length::{
22    GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
23    GenericMargin, GenericMaxSize, GenericSize,
24};
25use crate::values::specified::NonNegativeNumber;
26use crate::values::specified::calc::{
27    AllowAnchorPositioningFunctions, CalcLengthPercentage, CalcNode, PercentageContext,
28};
29use crate::values::specified::font::QueryFontMetricsFlags;
30use crate::values::specified::percentage::NoCalcPercentage;
31use crate::values::tagged_numeric::{Extracted, NumericUnion, Unpacked};
32use crate::{Zero, ZeroNoPercent};
33use app_units::AU_PER_PX;
34use cssparser::{Parser, Token, match_ignore_ascii_case};
35use std::cmp;
36use std::fmt::{self, Write};
37use style_traits::values::specified::AllowedNumericType;
38use style_traits::{
39    CssString, CssWriter, ParseError, ParsingMode, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
40};
41use thin_vec::ThinVec;
42
43pub use super::image::Image;
44pub use super::image::{EndingShape as GradientEndingShape, Gradient};
45
46/// Number of pixels per inch
47pub const PX_PER_IN: CSSFloat = 96.;
48/// Number of pixels per centimeter
49pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;
50/// Number of pixels per millimeter
51pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;
52/// Number of pixels per quarter
53pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;
54/// Number of pixels per point
55pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;
56/// Number of pixels per pica
57pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;
58
59/// The unit of a `<length>` value. Note that if any new font-relative value is
60/// added here, `custom_properties::NonCustomReferences::from_unit`
61/// must also be updated. Consult the comment in that function as to why.
62///
63/// The variants are grouped (absolute, font-relative, viewport, container,
64/// servo-internal) so that `is_*` predicates can be implemented with simple
65/// range checks.
66#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
67#[repr(u8)]
68#[allow(missing_docs)]
69pub enum LengthUnit {
70    // Absolute lengths.
71    Px,
72    In,
73    Cm,
74    Mm,
75    Q,
76    Pt,
77    Pc,
78    // Font-relative lengths.
79    Em,
80    Ex,
81    Rex,
82    Ch,
83    Rch,
84    Cap,
85    Rcap,
86    Ic,
87    Ric,
88    Rem,
89    Lh,
90    Rlh,
91    // Viewport-percentage lengths.
92    Vw,
93    Svw,
94    Lvw,
95    Dvw,
96    Vh,
97    Svh,
98    Lvh,
99    Dvh,
100    Vmin,
101    Svmin,
102    Lvmin,
103    Dvmin,
104    Vmax,
105    Svmax,
106    Lvmax,
107    Dvmax,
108    Vb,
109    Svb,
110    Lvb,
111    Dvb,
112    Vi,
113    Svi,
114    Lvi,
115    Dvi,
116    // Container-relative lengths.
117    Cqw,
118    Cqh,
119    Cqi,
120    Cqb,
121    Cqmin,
122    Cqmax,
123    /// HTML5 "character width", as defined in HTML5 ยง 14.5.4. Internal-only.
124    ServoCharacterWidth,
125}
126
127impl LengthUnit {
128    /// Returns the length unit for the given string.
129    #[inline]
130    pub fn from_str(unit: &str) -> Result<Self, ()> {
131        Self::from_str_with_flags(ParsingMode::DEFAULT, /* in_page_rule = */ false, unit)
132    }
133
134    /// Returns the length unit for the given flags and string.
135    #[inline]
136    pub fn from_str_with_flags(
137        parsing_mode: ParsingMode,
138        in_page_rule: bool,
139        unit: &str,
140    ) -> Result<Self, ()> {
141        let allows_computational_dependence = parsing_mode.allows_computational_dependence();
142
143        Ok(match_ignore_ascii_case! { unit,
144            "px" => Self::Px,
145            "in" => Self::In,
146            "cm" => Self::Cm,
147            "mm" => Self::Mm,
148            "q" => Self::Q,
149            "pt" => Self::Pt,
150            "pc" => Self::Pc,
151            // font-relative
152            "em" if allows_computational_dependence => Self::Em,
153            "ex" if allows_computational_dependence => Self::Ex,
154            "rex" if allows_computational_dependence => Self::Rex,
155            "ch" if allows_computational_dependence => Self::Ch,
156            "rch" if allows_computational_dependence => Self::Rch,
157            "cap" if allows_computational_dependence => Self::Cap,
158            "rcap" if allows_computational_dependence => Self::Rcap,
159            "ic" if allows_computational_dependence => Self::Ic,
160            "ric" if allows_computational_dependence => Self::Ric,
161            "rem" if allows_computational_dependence => Self::Rem,
162            "lh" if allows_computational_dependence => Self::Lh,
163            "rlh" if allows_computational_dependence => Self::Rlh,
164            // viewport percentages
165            "vw" if !in_page_rule => Self::Vw,
166            "svw" if !in_page_rule => Self::Svw,
167            "lvw" if !in_page_rule => Self::Lvw,
168            "dvw" if !in_page_rule => Self::Dvw,
169            "vh" if !in_page_rule => Self::Vh,
170            "svh" if !in_page_rule => Self::Svh,
171            "lvh" if !in_page_rule => Self::Lvh,
172            "dvh" if !in_page_rule => Self::Dvh,
173            "vmin" if !in_page_rule => Self::Vmin,
174            "svmin" if !in_page_rule => Self::Svmin,
175            "lvmin" if !in_page_rule => Self::Lvmin,
176            "dvmin" if !in_page_rule => Self::Dvmin,
177            "vmax" if !in_page_rule => Self::Vmax,
178            "svmax" if !in_page_rule => Self::Svmax,
179            "lvmax" if !in_page_rule => Self::Lvmax,
180            "dvmax" if !in_page_rule => Self::Dvmax,
181            "vb" if !in_page_rule => Self::Vb,
182            "svb" if !in_page_rule => Self::Svb,
183            "lvb" if !in_page_rule => Self::Lvb,
184            "dvb" if !in_page_rule => Self::Dvb,
185            "vi" if !in_page_rule => Self::Vi,
186            "svi" if !in_page_rule => Self::Svi,
187            "lvi" if !in_page_rule => Self::Lvi,
188            "dvi" if !in_page_rule => Self::Dvi,
189            // Container query lengths. Inherit the limitation from viewport units since
190            // we may fall back to them.
191            "cqw" if !in_page_rule && cfg!(feature = "gecko") => Self::Cqw,
192            "cqh" if !in_page_rule && cfg!(feature = "gecko") => Self::Cqh,
193            "cqi" if !in_page_rule && cfg!(feature = "gecko") => Self::Cqi,
194            "cqb" if !in_page_rule && cfg!(feature = "gecko") => Self::Cqb,
195            "cqmin" if !in_page_rule && cfg!(feature = "gecko") => Self::Cqmin,
196            "cqmax" if !in_page_rule && cfg!(feature = "gecko") => Self::Cqmax,
197            _ => return Err(()),
198        })
199    }
200
201    /// Returns this unit as a string.
202    #[inline]
203    pub fn as_str(self) -> &'static str {
204        match self {
205            Self::Px => "px",
206            Self::In => "in",
207            Self::Cm => "cm",
208            Self::Mm => "mm",
209            Self::Q => "q",
210            Self::Pt => "pt",
211            Self::Pc => "pc",
212            Self::Em => NoCalcLength::EM,
213            Self::Ex => NoCalcLength::EX,
214            Self::Rex => NoCalcLength::REX,
215            Self::Ch => NoCalcLength::CH,
216            Self::Rch => NoCalcLength::RCH,
217            Self::Cap => NoCalcLength::CAP,
218            Self::Rcap => NoCalcLength::RCAP,
219            Self::Ic => NoCalcLength::IC,
220            Self::Ric => NoCalcLength::RIC,
221            Self::Rem => NoCalcLength::REM,
222            Self::Lh => NoCalcLength::LH,
223            Self::Rlh => NoCalcLength::RLH,
224            Self::Vw => "vw",
225            Self::Svw => "svw",
226            Self::Lvw => "lvw",
227            Self::Dvw => "dvw",
228            Self::Vh => "vh",
229            Self::Svh => "svh",
230            Self::Lvh => "lvh",
231            Self::Dvh => "dvh",
232            Self::Vmin => "vmin",
233            Self::Svmin => "svmin",
234            Self::Lvmin => "lvmin",
235            Self::Dvmin => "dvmin",
236            Self::Vmax => "vmax",
237            Self::Svmax => "svmax",
238            Self::Lvmax => "lvmax",
239            Self::Dvmax => "dvmax",
240            Self::Vb => "vb",
241            Self::Svb => "svb",
242            Self::Lvb => "lvb",
243            Self::Dvb => "dvb",
244            Self::Vi => "vi",
245            Self::Svi => "svi",
246            Self::Lvi => "lvi",
247            Self::Dvi => "dvi",
248            Self::Cqw => "cqw",
249            Self::Cqh => "cqh",
250            Self::Cqi => "cqi",
251            Self::Cqb => "cqb",
252            Self::Cqmin => "cqmin",
253            Self::Cqmax => "cqmax",
254            Self::ServoCharacterWidth => "",
255        }
256    }
257
258    /// Whether this is an absolute length unit (px, in, cm, mm, q, pt, pc).
259    #[inline]
260    pub fn is_absolute(self) -> bool {
261        matches!(
262            self,
263            Self::Px | Self::In | Self::Cm | Self::Mm | Self::Q | Self::Pt | Self::Pc
264        )
265    }
266
267    /// Whether this is a font-relative unit.
268    #[inline]
269    pub fn is_font_relative(self) -> bool {
270        matches!(
271            self,
272            Self::Em
273                | Self::Ex
274                | Self::Rex
275                | Self::Ch
276                | Self::Rch
277                | Self::Cap
278                | Self::Rcap
279                | Self::Ic
280                | Self::Ric
281                | Self::Rem
282                | Self::Lh
283                | Self::Rlh
284        )
285    }
286
287    /// Whether this is a viewport-percentage unit.
288    #[inline]
289    pub fn is_viewport_percentage(self) -> bool {
290        matches!(
291            self,
292            Self::Vw
293                | Self::Svw
294                | Self::Lvw
295                | Self::Dvw
296                | Self::Vh
297                | Self::Svh
298                | Self::Lvh
299                | Self::Dvh
300                | Self::Vmin
301                | Self::Svmin
302                | Self::Lvmin
303                | Self::Dvmin
304                | Self::Vmax
305                | Self::Svmax
306                | Self::Lvmax
307                | Self::Dvmax
308                | Self::Vb
309                | Self::Svb
310                | Self::Lvb
311                | Self::Dvb
312                | Self::Vi
313                | Self::Svi
314                | Self::Lvi
315                | Self::Dvi
316        )
317    }
318
319    /// Whether this is a container-relative unit.
320    #[inline]
321    pub fn is_container_relative(self) -> bool {
322        matches!(
323            self,
324            Self::Cqw | Self::Cqh | Self::Cqi | Self::Cqb | Self::Cqmin | Self::Cqmax
325        )
326    }
327
328    /// Returns the sort key for this unit. Must not be called for the internal
329    /// `ServoCharacterWidth` unit.
330    fn sort_key(self) -> crate::values::generics::calc::SortKey {
331        use crate::values::generics::calc::SortKey;
332        match self {
333            Self::Px | Self::In | Self::Cm | Self::Mm | Self::Q | Self::Pt | Self::Pc => {
334                SortKey::Px
335            },
336            Self::Em => SortKey::Em,
337            Self::Ex => SortKey::Ex,
338            Self::Rex => SortKey::Rex,
339            Self::Ch => SortKey::Ch,
340            Self::Rch => SortKey::Rch,
341            Self::Cap => SortKey::Cap,
342            Self::Rcap => SortKey::Rcap,
343            Self::Ic => SortKey::Ic,
344            Self::Ric => SortKey::Ric,
345            Self::Rem => SortKey::Rem,
346            Self::Lh => SortKey::Lh,
347            Self::Rlh => SortKey::Rlh,
348            Self::Vw => SortKey::Vw,
349            Self::Svw => SortKey::Svw,
350            Self::Lvw => SortKey::Lvw,
351            Self::Dvw => SortKey::Dvw,
352            Self::Vh => SortKey::Vh,
353            Self::Svh => SortKey::Svh,
354            Self::Lvh => SortKey::Lvh,
355            Self::Dvh => SortKey::Dvh,
356            Self::Vmin => SortKey::Vmin,
357            Self::Svmin => SortKey::Svmin,
358            Self::Lvmin => SortKey::Lvmin,
359            Self::Dvmin => SortKey::Dvmin,
360            Self::Vmax => SortKey::Vmax,
361            Self::Svmax => SortKey::Svmax,
362            Self::Lvmax => SortKey::Lvmax,
363            Self::Dvmax => SortKey::Dvmax,
364            Self::Vb => SortKey::Vb,
365            Self::Svb => SortKey::Svb,
366            Self::Lvb => SortKey::Lvb,
367            Self::Dvb => SortKey::Dvb,
368            Self::Vi => SortKey::Vi,
369            Self::Svi => SortKey::Svi,
370            Self::Lvi => SortKey::Lvi,
371            Self::Dvi => SortKey::Dvi,
372            Self::Cqw => SortKey::Cqw,
373            Self::Cqh => SortKey::Cqh,
374            Self::Cqi => SortKey::Cqi,
375            Self::Cqb => SortKey::Cqb,
376            Self::Cqmin => SortKey::Cqmin,
377            Self::Cqmax => SortKey::Cqmax,
378            Self::ServoCharacterWidth => unreachable!(),
379        }
380    }
381}
382
383/// A source to resolve font-relative units against
384#[derive(Clone, Copy, Debug, PartialEq)]
385pub enum FontBaseSize {
386    /// Use the font-size of the current element.
387    CurrentStyle,
388    /// Use the inherited font-size.
389    InheritedStyle,
390}
391
392/// A source to resolve font-relative line-height units against.
393#[derive(Clone, Copy, Debug, PartialEq)]
394pub enum LineHeightBase {
395    /// Use the line-height of the current element.
396    CurrentStyle,
397    /// Use the inherited line-height.
398    InheritedStyle,
399}
400
401impl FontBaseSize {
402    /// Calculate the actual size for a given context
403    pub fn resolve(&self, context: &Context) -> computed::FontSize {
404        let style = context.style();
405        match *self {
406            Self::CurrentStyle => style.get_font().slow_clone_font_size(),
407            Self::InheritedStyle => {
408                // If we're using the size from our inherited style, we still need to apply our
409                // own zoom.
410                let zoom = style.effective_zoom_for_inheritance;
411                style.get_parent_font().slow_clone_font_size().zoom(zoom)
412            },
413        }
414    }
415}
416
417/// https://drafts.csswg.org/css-values/#viewport-variants
418pub enum ViewportVariant {
419    /// https://drafts.csswg.org/css-values/#ua-default-viewport-size
420    UADefault,
421    /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units
422    Small,
423    /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units
424    Large,
425    /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units
426    Dynamic,
427}
428
429/// https://drafts.csswg.org/css-values/#viewport-relative-units
430#[derive(PartialEq)]
431enum ViewportUnit {
432    /// *vw units.
433    Vw,
434    /// *vh units.
435    Vh,
436    /// *vmin units.
437    Vmin,
438    /// *vmax units.
439    Vmax,
440    /// *vb units.
441    Vb,
442    /// *vi units.
443    Vi,
444}
445
446/// A `<length>` without taking `calc` expressions into account
447///
448/// <https://drafts.csswg.org/css-values/#lengths>
449#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
450#[repr(C)]
451pub struct NoCalcLength {
452    unit: LengthUnit,
453    value: CSSFloat,
454}
455
456impl NoCalcLength {
457    /// Unit identifier for `em`.
458    pub const EM: &'static str = "em";
459    /// Unit identifier for `ex`.
460    pub const EX: &'static str = "ex";
461    /// Unit identifier for `rex`.
462    pub const REX: &'static str = "rex";
463    /// Unit identifier for `ch`.
464    pub const CH: &'static str = "ch";
465    /// Unit identifier for `rch`.
466    pub const RCH: &'static str = "rch";
467    /// Unit identifier for `cap`.
468    pub const CAP: &'static str = "cap";
469    /// Unit identifier for `rcap`.
470    pub const RCAP: &'static str = "rcap";
471    /// Unit identifier for `ic`.
472    pub const IC: &'static str = "ic";
473    /// Unit identifier for `ric`.
474    pub const RIC: &'static str = "ric";
475    /// Unit identifier for `rem`.
476    pub const REM: &'static str = "rem";
477    /// Unit identifier for `lh`.
478    pub const LH: &'static str = "lh";
479    /// Unit identifier for `rlh`.
480    pub const RLH: &'static str = "rlh";
481
482    /// Creates a length with the given unit and value.
483    #[inline]
484    pub fn new(unit: LengthUnit, value: CSSFloat) -> Self {
485        Self { unit, value }
486    }
487
488    /// Returns the unit of this length.
489    #[inline]
490    pub fn length_unit(&self) -> LengthUnit {
491        self.unit
492    }
493
494    /// Return the unitless, raw value.
495    #[inline]
496    pub fn unitless_value(&self) -> CSSFloat {
497        self.value
498    }
499
500    /// Return the unit, as a string.
501    #[inline]
502    pub fn unit(&self) -> &'static str {
503        self.unit.as_str()
504    }
505
506    /// Return the canonical unit for this value, if one exists.
507    pub fn canonical_unit(&self) -> Option<&'static str> {
508        if self.unit.is_absolute() {
509            Some("px")
510        } else {
511            None
512        }
513    }
514
515    /// Convert this value to the specified unit, if possible.
516    pub fn to(&self, unit: &str) -> Result<Self, ()> {
517        let px = self.to_px_if_absolute().ok_or(())?;
518        let (target, divisor) = match_ignore_ascii_case! { unit,
519            "px" => (LengthUnit::Px, 1.0),
520            "in" => (LengthUnit::In, PX_PER_IN),
521            "cm" => (LengthUnit::Cm, PX_PER_CM),
522            "mm" => (LengthUnit::Mm, PX_PER_MM),
523            "q" => (LengthUnit::Q, PX_PER_Q),
524            "pt" => (LengthUnit::Pt, PX_PER_PT),
525            "pc" => (LengthUnit::Pc, PX_PER_PC),
526             _ => return Err(()),
527        };
528        Ok(Self::new(target, px / divisor))
529    }
530
531    /// Returns whether the value of this length without unit is less than zero.
532    pub fn is_negative(&self) -> bool {
533        self.value.is_sign_negative()
534    }
535
536    /// Returns whether the value of this length without unit is equal to zero.
537    pub fn is_zero(&self) -> bool {
538        self.value == 0.0
539    }
540
541    /// Returns whether the value of this length without unit is infinite.
542    pub fn is_infinite(&self) -> bool {
543        self.value.is_infinite()
544    }
545
546    /// Returns whether the value of this length without unit is NaN.
547    pub fn is_nan(&self) -> bool {
548        self.value.is_nan()
549    }
550
551    /// Whether text-only zoom should be applied to this length.
552    ///
553    /// Generally, font-dependent/relative units don't get text-only-zoomed,
554    /// because the font they're relative to should be zoomed already.
555    pub fn should_zoom_text(&self) -> bool {
556        !self.unit.is_font_relative() && self.unit != LengthUnit::ServoCharacterWidth
557    }
558
559    /// Returns the SortKey for this length. Must not be called on the internal
560    /// `ServoCharacterWidth` unit.
561    pub(crate) fn sort_key(&self) -> crate::values::generics::calc::SortKey {
562        self.unit.sort_key()
563    }
564
565    /// Parse a given absolute or relative dimension.
566    pub fn parse_dimension_with_flags(
567        parsing_mode: ParsingMode,
568        in_page_rule: bool,
569        value: CSSFloat,
570        unit: &str,
571    ) -> Result<Self, ()> {
572        let length_unit = LengthUnit::from_str_with_flags(parsing_mode, in_page_rule, unit)?;
573        Ok(Self::new(length_unit, value))
574    }
575
576    /// Parse a given absolute or relative dimension.
577    pub fn parse_dimension_with_context(
578        context: &ParserContext,
579        value: CSSFloat,
580        unit: &str,
581    ) -> Result<Self, ()> {
582        Self::parse_dimension_with_flags(context.parsing_mode, context.in_page_rule(), value, unit)
583    }
584
585    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
586    where
587        O: Fn(f32, f32) -> f32,
588    {
589        // For absolute lengths, normalize both to px and produce a px result.
590        if let (Some(a), Some(b)) = (self.to_px_if_absolute(), other.to_px_if_absolute()) {
591            return Ok(Self::new(LengthUnit::Px, op(a, b)));
592        }
593        if self.unit != other.unit {
594            return Err(());
595        }
596        Ok(Self::new(self.unit, op(self.value, other.value)))
597    }
598
599    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
600        // For absolute lengths, normalize to px.
601        if let Some(px) = self.to_px_if_absolute() {
602            return Self::new(LengthUnit::Px, op(px));
603        }
604        Self::new(self.unit, op(self.value))
605    }
606
607    /// Get a px value without context (so only absolute units can be handled).
608    #[inline]
609    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
610        self.to_px_if_absolute().ok_or(())
611    }
612
613    /// Get a px value without a full style context; this can handle either
614    /// absolute or (if a font metrics getter is provided) font-relative units.
615    #[cfg(feature = "gecko")]
616    #[inline]
617    pub fn to_computed_pixel_length_with_font_metrics(
618        &self,
619        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
620    ) -> Result<CSSFloat, ()> {
621        if let Some(px) = self.to_px_if_absolute() {
622            return Ok(CSSPixelLength::new(px).finite().px());
623        }
624        if !self.unit.is_font_relative() {
625            return Err(());
626        }
627        let getter = match get_font_metrics {
628            Some(g) => g,
629            None => return Err(()),
630        };
631        let metrics = getter();
632        Ok(match self.unit {
633            LengthUnit::Em => self.value * metrics.mComputedEmSize.px(),
634            LengthUnit::Ex => self.value * metrics.mXSize.px(),
635            LengthUnit::Ch => self.value * metrics.mChSize.px(),
636            LengthUnit::Cap => self.value * metrics.mCapHeight.px(),
637            LengthUnit::Ic => self.value * metrics.mIcWidth.px(),
638            // `lh`, `rlh` are unsupported as we have no line-height context
639            // `rem`, `rex`, `rch`, `rcap`, and `ric` are unsupported as we have no root font context.
640            _ => return Err(()),
641        })
642    }
643
644    /// Get an absolute length from a px value.
645    #[inline]
646    pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
647        Self::new(LengthUnit::Px, px_value)
648    }
649
650    /// Returns the value as a px-canonical length (absolute lengths only).
651    #[inline]
652    pub fn to_px_if_absolute(&self) -> Option<CSSFloat> {
653        let factor = match self.unit {
654            LengthUnit::Px => 1.0,
655            LengthUnit::In => PX_PER_IN,
656            LengthUnit::Cm => PX_PER_CM,
657            LengthUnit::Mm => PX_PER_MM,
658            LengthUnit::Q => PX_PER_Q,
659            LengthUnit::Pt => PX_PER_PT,
660            LengthUnit::Pc => PX_PER_PC,
661            _ => return None,
662        };
663        Some(self.value * factor)
664    }
665
666    /// Construct a font-relative em value.
667    #[inline]
668    pub fn from_em(value: CSSFloat) -> Self {
669        Self::new(LengthUnit::Em, value)
670    }
671
672    /// Construct an internal ServoCharacterWidth length from an i32 column count.
673    #[inline]
674    pub fn from_servo_character_width(value: i32) -> Self {
675        Self::new(LengthUnit::ServoCharacterWidth, value as CSSFloat)
676    }
677
678    /// Compute a font-relative length against the given base sizes. Must only
679    /// be called on a font-relative unit.
680    fn font_relative_to_computed_value(
681        &self,
682        context: &Context,
683        base_size: FontBaseSize,
684        line_height_base: LineHeightBase,
685    ) -> computed::Length {
686        let (reference_size, length) =
687            self.reference_font_size_and_length(context, base_size, line_height_base);
688        (reference_size * length).finite()
689    }
690
691    fn reference_font_size_and_length(
692        &self,
693        context: &Context,
694        base_size: FontBaseSize,
695        line_height_base: LineHeightBase,
696    ) -> (computed::Length, CSSFloat) {
697        fn query_font_metrics(
698            context: &Context,
699            base_size: FontBaseSize,
700            orientation: FontMetricsOrientation,
701            flags: QueryFontMetricsFlags,
702        ) -> FontMetrics {
703            context.query_font_metrics(base_size, orientation, flags)
704        }
705
706        fn ex_size(
707            context: &Context,
708            base_size: FontBaseSize,
709            reference_font_size: &FontSize,
710        ) -> computed::Length {
711            let metrics = query_font_metrics(
712                context,
713                base_size,
714                FontMetricsOrientation::Horizontal,
715                QueryFontMetricsFlags::empty(),
716            );
717            metrics.x_height_or_default(reference_font_size.used_size())
718        }
719
720        fn ch_size(
721            context: &Context,
722            base_size: FontBaseSize,
723            reference_font_size: &FontSize,
724        ) -> computed::Length {
725            let metrics = query_font_metrics(
726                context,
727                base_size,
728                FontMetricsOrientation::MatchContextPreferHorizontal,
729                QueryFontMetricsFlags::NEEDS_CH,
730            );
731            metrics.zero_advance_measure_or_default(
732                reference_font_size.used_size(),
733                context.style().writing_mode.is_upright(),
734            )
735        }
736
737        fn cap_size(context: &Context, base_size: FontBaseSize) -> computed::Length {
738            let metrics = query_font_metrics(
739                context,
740                base_size,
741                FontMetricsOrientation::Horizontal,
742                QueryFontMetricsFlags::empty(),
743            );
744            metrics.cap_height_or_default()
745        }
746
747        fn ic_size(
748            context: &Context,
749            base_size: FontBaseSize,
750            reference_font_size: &FontSize,
751        ) -> computed::Length {
752            let metrics = query_font_metrics(
753                context,
754                base_size,
755                FontMetricsOrientation::MatchContextPreferVertical,
756                QueryFontMetricsFlags::NEEDS_IC,
757            );
758            metrics.ic_width_or_default(reference_font_size.used_size())
759        }
760
761        context
762            .builder
763            .add_flags(ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS);
764
765        let reference_font_size = base_size.resolve(context);
766        let length = self.value;
767        match self.unit {
768            LengthUnit::Em => {
769                if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
770                    context
771                        .rule_cache_conditions
772                        .borrow_mut()
773                        .set_font_size_dependency(reference_font_size.computed_size);
774                }
775
776                (reference_font_size.computed_size(), length)
777            },
778            LengthUnit::Lh => {
779                let reference_size = if context.in_media_query {
780                    context
781                        .device()
782                        .calc_line_height(
783                            context.default_style().get_font(),
784                            context.style().writing_mode,
785                            None,
786                        )
787                        .0
788                } else {
789                    let line_height = context.builder.calc_line_height(
790                        context.device(),
791                        line_height_base,
792                        context.style().writing_mode,
793                    );
794                    if context.for_non_inherited_property
795                        && line_height_base == LineHeightBase::CurrentStyle
796                    {
797                        context
798                            .rule_cache_conditions
799                            .borrow_mut()
800                            .set_line_height_dependency(line_height)
801                    }
802                    line_height.0
803                };
804                (reference_size, length)
805            },
806            LengthUnit::Ex => (ex_size(context, base_size, &reference_font_size), length),
807            LengthUnit::Ch => (ch_size(context, base_size, &reference_font_size), length),
808            LengthUnit::Cap => (cap_size(context, base_size), length),
809            LengthUnit::Ic => (ic_size(context, base_size, &reference_font_size), length),
810            LengthUnit::Rex => {
811                let reference_size = if context.builder.is_root_element || context.in_media_query {
812                    ex_size(context, base_size, &reference_font_size)
813                } else {
814                    context
815                        .device()
816                        .root_font_metrics_ex()
817                        .zoom(context.builder.effective_zoom)
818                };
819                (reference_size, length)
820            },
821            LengthUnit::Rch => {
822                let reference_size = if context.builder.is_root_element || context.in_media_query {
823                    ch_size(context, base_size, &reference_font_size)
824                } else {
825                    context
826                        .device()
827                        .root_font_metrics_ch()
828                        .zoom(context.builder.effective_zoom)
829                };
830                (reference_size, length)
831            },
832            LengthUnit::Rcap => {
833                let reference_size = if context.builder.is_root_element || context.in_media_query {
834                    cap_size(context, base_size)
835                } else {
836                    context
837                        .device()
838                        .root_font_metrics_cap()
839                        .zoom(context.builder.effective_zoom)
840                };
841                (reference_size, length)
842            },
843            LengthUnit::Ric => {
844                let reference_size = if context.builder.is_root_element || context.in_media_query {
845                    ic_size(context, base_size, &reference_font_size)
846                } else {
847                    context
848                        .device()
849                        .root_font_metrics_ic()
850                        .zoom(context.builder.effective_zoom)
851                };
852                (reference_size, length)
853            },
854            LengthUnit::Rem => {
855                let reference_size = if context.builder.is_root_element || context.in_media_query {
856                    reference_font_size.computed_size()
857                } else {
858                    context
859                        .device()
860                        .root_font_size()
861                        .zoom(context.builder.effective_zoom)
862                };
863                (reference_size, length)
864            },
865            LengthUnit::Rlh => {
866                let reference_size = if context.builder.is_root_element {
867                    context
868                        .builder
869                        .calc_line_height(
870                            context.device(),
871                            line_height_base,
872                            context.style().writing_mode,
873                        )
874                        .0
875                } else if context.in_media_query {
876                    context
877                        .device()
878                        .calc_line_height(
879                            context.default_style().get_font(),
880                            context.style().writing_mode,
881                            None,
882                        )
883                        .0
884                } else {
885                    context.device().root_line_height()
886                };
887                let reference_size = reference_size.zoom(context.builder.effective_zoom);
888                (reference_size, length)
889            },
890            _ => unreachable!("reference_font_size_and_length: not a font-relative unit"),
891        }
892    }
893
894    /// Compute the viewport-percentage length. Must only be called on a
895    /// viewport-relative unit.
896    fn viewport_percentage_to_computed_value(&self, context: &Context) -> CSSPixelLength {
897        let (variant, unit) = match self.unit {
898            LengthUnit::Vw => (ViewportVariant::UADefault, ViewportUnit::Vw),
899            LengthUnit::Svw => (ViewportVariant::Small, ViewportUnit::Vw),
900            LengthUnit::Lvw => (ViewportVariant::Large, ViewportUnit::Vw),
901            LengthUnit::Dvw => (ViewportVariant::Dynamic, ViewportUnit::Vw),
902            LengthUnit::Vh => (ViewportVariant::UADefault, ViewportUnit::Vh),
903            LengthUnit::Svh => (ViewportVariant::Small, ViewportUnit::Vh),
904            LengthUnit::Lvh => (ViewportVariant::Large, ViewportUnit::Vh),
905            LengthUnit::Dvh => (ViewportVariant::Dynamic, ViewportUnit::Vh),
906            LengthUnit::Vmin => (ViewportVariant::UADefault, ViewportUnit::Vmin),
907            LengthUnit::Svmin => (ViewportVariant::Small, ViewportUnit::Vmin),
908            LengthUnit::Lvmin => (ViewportVariant::Large, ViewportUnit::Vmin),
909            LengthUnit::Dvmin => (ViewportVariant::Dynamic, ViewportUnit::Vmin),
910            LengthUnit::Vmax => (ViewportVariant::UADefault, ViewportUnit::Vmax),
911            LengthUnit::Svmax => (ViewportVariant::Small, ViewportUnit::Vmax),
912            LengthUnit::Lvmax => (ViewportVariant::Large, ViewportUnit::Vmax),
913            LengthUnit::Dvmax => (ViewportVariant::Dynamic, ViewportUnit::Vmax),
914            LengthUnit::Vb => (ViewportVariant::UADefault, ViewportUnit::Vb),
915            LengthUnit::Svb => (ViewportVariant::Small, ViewportUnit::Vb),
916            LengthUnit::Lvb => (ViewportVariant::Large, ViewportUnit::Vb),
917            LengthUnit::Dvb => (ViewportVariant::Dynamic, ViewportUnit::Vb),
918            LengthUnit::Vi => (ViewportVariant::UADefault, ViewportUnit::Vi),
919            LengthUnit::Svi => (ViewportVariant::Small, ViewportUnit::Vi),
920            LengthUnit::Lvi => (ViewportVariant::Large, ViewportUnit::Vi),
921            LengthUnit::Dvi => (ViewportVariant::Dynamic, ViewportUnit::Vi),
922            _ => {
923                unreachable!("viewport_percentage_to_computed_value: not a viewport-relative unit")
924            },
925        };
926        let factor = self.value;
927        let size = context.viewport_size_for_viewport_unit_resolution(variant);
928        let length: app_units::Au = match unit {
929            ViewportUnit::Vw => size.width,
930            ViewportUnit::Vh => size.height,
931            ViewportUnit::Vmin => cmp::min(size.width, size.height),
932            ViewportUnit::Vmax => cmp::max(size.width, size.height),
933            ViewportUnit::Vi | ViewportUnit::Vb => {
934                context
935                    .builder
936                    .add_flags(ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS);
937                context
938                    .rule_cache_conditions
939                    .borrow_mut()
940                    .set_writing_mode_dependency(context.builder.writing_mode);
941                if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
942                    size.width
943                } else {
944                    size.height
945                }
946            },
947        };
948        let length = context.builder.effective_zoom.zoom(length.0 as f32);
949
950        let trunc_scaled =
951            ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
952        CSSPixelLength::new(crate::values::normalize(trunc_scaled))
953    }
954
955    /// Compute the container-relative length. Must only be called on a
956    /// container-relative unit.
957    fn container_relative_to_computed_value(&self, context: &Context) -> CSSPixelLength {
958        if context.for_non_inherited_property {
959            context.rule_cache_conditions.borrow_mut().set_uncacheable();
960        }
961        context
962            .builder
963            .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
964
965        let size = context.get_container_size_query();
966        let factor = self.value;
967        let container_length = match self.unit {
968            LengthUnit::Cqw => size.get_container_width(context),
969            LengthUnit::Cqh => size.get_container_height(context),
970            LengthUnit::Cqi => size.get_container_inline_size(context),
971            LengthUnit::Cqb => size.get_container_block_size(context),
972            LengthUnit::Cqmin => cmp::min(
973                size.get_container_inline_size(context),
974                size.get_container_block_size(context),
975            ),
976            LengthUnit::Cqmax => cmp::max(
977                size.get_container_inline_size(context),
978                size.get_container_block_size(context),
979            ),
980            _ => {
981                unreachable!("container_relative_to_computed_value: not a container-relative unit")
982            },
983        };
984        CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
985    }
986
987    /// Computes a ServoCharacterWidth length against a reference font size.
988    fn servo_character_width_to_computed_value(
989        &self,
990        reference_font_size: computed::Length,
991    ) -> computed::Length {
992        debug_assert_eq!(self.unit, LengthUnit::ServoCharacterWidth);
993        let cols = self.value as i32 as CSSFloat;
994        // This applies the *converting a character width to pixels* algorithm
995        // as specified in HTML5 ยง 14.5.4.
996        let average_advance = reference_font_size * 0.5;
997        let max_advance = reference_font_size;
998        (average_advance * (cols - 1.0) + max_advance).finite()
999    }
1000
1001    /// Computes a length with a given font-relative base size.
1002    pub fn to_computed_value_with_base_size(
1003        &self,
1004        context: &Context,
1005        base_size: FontBaseSize,
1006        line_height_base: LineHeightBase,
1007    ) -> CSSPixelLength {
1008        if let Some(px) = self.to_px_if_absolute() {
1009            return CSSPixelLength::new(px)
1010                .zoom(context.builder.effective_zoom)
1011                .finite();
1012        }
1013        let unit = self.length_unit();
1014        if unit.is_font_relative() {
1015            return self.font_relative_to_computed_value(context, base_size, line_height_base);
1016        }
1017        if unit.is_viewport_percentage() {
1018            return self.viewport_percentage_to_computed_value(context);
1019        }
1020        if unit.is_container_relative() {
1021            return self.container_relative_to_computed_value(context);
1022        }
1023        debug_assert_eq!(unit, LengthUnit::ServoCharacterWidth);
1024        self.servo_character_width_to_computed_value(
1025            context
1026                .style()
1027                .get_font()
1028                .slow_clone_font_size()
1029                .computed_size(),
1030        )
1031    }
1032}
1033
1034impl ToComputedValue for NoCalcLength {
1035    type ComputedValue = computed::Length;
1036
1037    #[inline]
1038    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1039        self.to_computed_value_with_base_size(
1040            context,
1041            FontBaseSize::CurrentStyle,
1042            LineHeightBase::CurrentStyle,
1043        )
1044    }
1045
1046    #[inline]
1047    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1048        Self::from_px(computed.px())
1049    }
1050}
1051
1052impl ToCss for NoCalcLength {
1053    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1054    where
1055        W: Write,
1056    {
1057        crate::values::serialize_specified_dimension(
1058            self.unitless_value(),
1059            self.unit(),
1060            false,
1061            dest,
1062        )
1063    }
1064}
1065
1066impl ToTyped for NoCalcLength {
1067    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1068        let numeric_type = NumericType::length();
1069        let value = self.unitless_value();
1070        let unit = CssString::from(self.unit());
1071        dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
1072            numeric_type,
1073            value,
1074            unit,
1075        })));
1076        Ok(())
1077    }
1078}
1079
1080impl SpecifiedValueInfo for NoCalcLength {}
1081
1082impl PartialOrd for NoCalcLength {
1083    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1084        // For absolute units, compare in px.
1085        if let (Some(a), Some(b)) = (self.to_px_if_absolute(), other.to_px_if_absolute()) {
1086            return a.partial_cmp(&b);
1087        }
1088        if self.unit != other.unit {
1089            return None;
1090        }
1091        self.value.partial_cmp(&other.value)
1092    }
1093}
1094
1095impl Zero for NoCalcLength {
1096    fn zero() -> Self {
1097        Self::from_px(0.)
1098    }
1099
1100    fn is_zero(&self) -> bool {
1101        NoCalcLength::is_zero(self)
1102    }
1103}
1104
1105/// An extension to `NoCalcLength` to parse `calc` expressions.
1106/// This is commonly used for the `<length>` values.
1107///
1108/// Either stored inline as length + unit without calc or as a boxed calc node.
1109///
1110/// <https://drafts.csswg.org/css-values/#lengths>
1111#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
1112pub struct Length(NumericUnion<LengthUnit, f32, CalcLengthPercentage>);
1113
1114impl ToCss for Length {
1115    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1116    where
1117        W: Write,
1118    {
1119        match self.0.unpack() {
1120            Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value).to_css(dest),
1121            Unpacked::Boxed(calc) => calc.to_css(dest),
1122        }
1123    }
1124}
1125
1126impl ToTyped for Length {
1127    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1128        match self.0.unpack() {
1129            Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value).to_typed(dest),
1130            Unpacked::Boxed(calc) => calc.to_typed(dest),
1131        }
1132    }
1133}
1134
1135impl SpecifiedValueInfo for Length {}
1136
1137impl From<NoCalcLength> for Length {
1138    #[inline]
1139    fn from(len: NoCalcLength) -> Self {
1140        Self::new(len)
1141    }
1142}
1143
1144impl Length {
1145    /// Creates a length from a non-calc `NoCalcLength`.
1146    #[inline]
1147    pub fn new(len: NoCalcLength) -> Self {
1148        Self(NumericUnion::inline(len.unit, len.value))
1149    }
1150
1151    /// Creates a length from a `calc()` expression.
1152    #[inline]
1153    pub fn new_calc(calc: Box<CalcLengthPercentage>) -> Self {
1154        Self(NumericUnion::boxed(calc))
1155    }
1156
1157    /// Returns true if this is a `calc()` expression.
1158    #[inline]
1159    pub fn is_calc(&self) -> bool {
1160        self.0.is_boxed()
1161    }
1162
1163    #[inline]
1164    fn parse_internal(
1165        context: &ParserContext,
1166        input: &mut Parser,
1167        num_context: AllowedNumericType,
1168        allow_quirks: AllowQuirks,
1169    ) -> Result<Self, ParseError> {
1170        let token = input.next()?;
1171        match *token {
1172            Token::Dimension {
1173                value, ref unit, ..
1174            } if num_context.is_ok(context.parsing_mode, value) => {
1175                NoCalcLength::parse_dimension_with_context(context, value, unit)
1176                    .map(Self::new)
1177                    .map_err(|()| ParseError::unexpected_token())
1178            },
1179            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1180                let allowed = context.parsing_mode.allows_unitless_lengths()
1181                    || allow_quirks.allowed(context.quirks_mode)
1182                    || (value == 0. && context.parsing_mode.allows_unitless_zero_lengths());
1183
1184                if !allowed {
1185                    return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1186                }
1187
1188                Ok(Self::new(NoCalcLength::from_px(value)))
1189            },
1190            Token::Function(ref name) => {
1191                let function = CalcNode::math_function(context, name)?;
1192                let calc = CalcNode::parse_length(
1193                    context,
1194                    input,
1195                    num_context,
1196                    function,
1197                    PercentageContext::not_allowed(),
1198                )?;
1199                Ok(Self::new_calc(Box::new(calc)))
1200            },
1201            _ => Err(ParseError::unexpected_token()),
1202        }
1203    }
1204
1205    /// Parse a non-negative length
1206    #[inline]
1207    pub fn parse_non_negative(
1208        context: &ParserContext,
1209        input: &mut Parser,
1210    ) -> Result<Self, ParseError> {
1211        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1212    }
1213
1214    /// Parse a non-negative length, allowing quirks.
1215    #[inline]
1216    pub fn parse_non_negative_quirky(
1217        context: &ParserContext,
1218        input: &mut Parser,
1219        allow_quirks: AllowQuirks,
1220    ) -> Result<Self, ParseError> {
1221        Self::parse_internal(
1222            context,
1223            input,
1224            AllowedNumericType::NonNegative,
1225            allow_quirks,
1226        )
1227    }
1228
1229    /// Get an absolute length from a px value.
1230    #[inline]
1231    pub fn from_px(px_value: CSSFloat) -> Length {
1232        Self::new(NoCalcLength::from_px(px_value))
1233    }
1234
1235    /// Get a px value without context.
1236    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1237        match self.0.unpack() {
1238            Unpacked::Inline(unit, value) => {
1239                NoCalcLength::new(unit, value).to_computed_pixel_length_without_context()
1240            },
1241            Unpacked::Boxed(calc) => calc.to_computed_pixel_length_without_context(),
1242        }
1243    }
1244
1245    /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.
1246    #[cfg(feature = "gecko")]
1247    pub fn to_computed_pixel_length_with_font_metrics(
1248        &self,
1249        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1250    ) -> Result<CSSFloat, ()> {
1251        match self.0.unpack() {
1252            Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value)
1253                .to_computed_pixel_length_with_font_metrics(get_font_metrics),
1254            Unpacked::Boxed(calc) => {
1255                calc.to_computed_pixel_length_with_font_metrics(get_font_metrics)
1256            },
1257        }
1258    }
1259}
1260
1261impl Parse for Length {
1262    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1263        Self::parse_quirky(context, input, AllowQuirks::No)
1264    }
1265}
1266
1267impl ToComputedValue for Length {
1268    type ComputedValue = computed::Length;
1269
1270    #[inline]
1271    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1272        match self.0.unpack() {
1273            Unpacked::Inline(unit, value) => {
1274                NoCalcLength::new(unit, value).to_computed_value(context)
1275            },
1276            Unpacked::Boxed(calc) => {
1277                let result = calc.to_computed_value(context);
1278                debug_assert!(
1279                    result.to_length().is_some(),
1280                    "{:?} didn't resolve to a length: {:?}",
1281                    calc,
1282                    result,
1283                );
1284                result.to_length().unwrap_or_else(computed::Length::zero)
1285            },
1286        }
1287    }
1288
1289    #[inline]
1290    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1291        Self::new(NoCalcLength::from_computed_value(computed))
1292    }
1293}
1294
1295impl Zero for Length {
1296    fn zero() -> Self {
1297        Self::new(NoCalcLength::zero())
1298    }
1299
1300    fn is_zero(&self) -> bool {
1301        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
1302        // non-zero here?
1303        match self.0.unpack() {
1304            Unpacked::Inline(_, value) => value == 0.0,
1305            Unpacked::Boxed(_) => false,
1306        }
1307    }
1308}
1309
1310impl Length {
1311    /// Parses a length, with quirks.
1312    pub fn parse_quirky(
1313        context: &ParserContext,
1314        input: &mut Parser,
1315        allow_quirks: AllowQuirks,
1316    ) -> Result<Self, ParseError> {
1317        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1318    }
1319}
1320
1321/// A wrapper of Length, whose value must be >= 0.
1322pub type NonNegativeLength = NonNegative<Length>;
1323
1324impl Parse for NonNegativeLength {
1325    #[inline]
1326    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1327        Ok(NonNegative(Length::parse_non_negative(context, input)?))
1328    }
1329}
1330
1331impl From<NoCalcLength> for NonNegativeLength {
1332    #[inline]
1333    fn from(len: NoCalcLength) -> Self {
1334        NonNegative(Length::new(len))
1335    }
1336}
1337
1338impl From<Length> for NonNegativeLength {
1339    #[inline]
1340    fn from(len: Length) -> Self {
1341        NonNegative(len)
1342    }
1343}
1344
1345impl NonNegativeLength {
1346    /// Get an absolute length from a px value.
1347    #[inline]
1348    pub fn from_px(px_value: CSSFloat) -> Self {
1349        Length::from_px(px_value.max(0.)).into()
1350    }
1351
1352    /// Parses a non-negative length, optionally with quirks.
1353    #[inline]
1354    pub fn parse_quirky(
1355        context: &ParserContext,
1356        input: &mut Parser,
1357        allow_quirks: AllowQuirks,
1358    ) -> Result<Self, ParseError> {
1359        Ok(NonNegative(Length::parse_non_negative_quirky(
1360            context,
1361            input,
1362            allow_quirks,
1363        )?))
1364    }
1365}
1366
1367/// A `<length-percentage>` value. This can be either a `<length>`, a
1368/// `<percentage>`, or a combination of both via `calc()`.
1369///
1370/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
1371#[allow(missing_docs)]
1372#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1373pub enum LengthPercentage {
1374    Length(NoCalcLength),
1375    Percentage(NoCalcPercentage),
1376    Calc(Box<CalcLengthPercentage>),
1377}
1378
1379impl From<Length> for LengthPercentage {
1380    fn from(len: Length) -> LengthPercentage {
1381        match len.0.extract() {
1382            Extracted::Inline(unit, value) => {
1383                LengthPercentage::Length(NoCalcLength::new(unit, value))
1384            },
1385            Extracted::Boxed(calc) => LengthPercentage::Calc(calc),
1386        }
1387    }
1388}
1389
1390impl From<NoCalcLength> for LengthPercentage {
1391    #[inline]
1392    fn from(len: NoCalcLength) -> Self {
1393        LengthPercentage::Length(len)
1394    }
1395}
1396
1397impl From<computed::Percentage> for LengthPercentage {
1398    #[inline]
1399    fn from(pc: computed::Percentage) -> Self {
1400        LengthPercentage::Percentage(NoCalcPercentage::new(pc.0))
1401    }
1402}
1403
1404impl Parse for LengthPercentage {
1405    #[inline]
1406    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1407        Self::parse_quirky(context, input, AllowQuirks::No)
1408    }
1409}
1410
1411impl LengthPercentage {
1412    #[inline]
1413    /// Returns a `0%` value.
1414    pub fn zero_percent() -> LengthPercentage {
1415        LengthPercentage::Percentage(NoCalcPercentage::zero())
1416    }
1417
1418    #[inline]
1419    /// Returns a `100%` value.
1420    pub fn hundred_percent() -> LengthPercentage {
1421        LengthPercentage::Percentage(NoCalcPercentage::hundred())
1422    }
1423
1424    fn parse_internal(
1425        context: &ParserContext,
1426        input: &mut Parser,
1427        num_context: AllowedNumericType,
1428        allow_quirks: AllowQuirks,
1429        allow_anchor: AllowAnchorPositioningFunctions,
1430    ) -> Result<Self, ParseError> {
1431        let token = input.next()?;
1432        match *token {
1433            Token::Dimension {
1434                value, ref unit, ..
1435            } if num_context.is_ok(context.parsing_mode, value) => {
1436                NoCalcLength::parse_dimension_with_context(context, value, unit)
1437                    .map(LengthPercentage::Length)
1438                    .map_err(|()| ParseError::unexpected_token())
1439            },
1440            Token::Percentage { unit_value, .. }
1441                if num_context.is_ok(context.parsing_mode, unit_value) =>
1442            {
1443                Ok(LengthPercentage::Percentage(NoCalcPercentage::new(
1444                    unit_value,
1445                )))
1446            },
1447            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1448                let allowed = context.parsing_mode.allows_unitless_lengths()
1449                    || allow_quirks.allowed(context.quirks_mode)
1450                    || (value == 0. && context.parsing_mode.allows_unitless_zero_lengths());
1451
1452                if !allowed {
1453                    return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1454                }
1455
1456                Ok(LengthPercentage::Length(NoCalcLength::from_px(value)))
1457            },
1458            Token::Function(ref name) => {
1459                let function = CalcNode::math_function(context, name)?;
1460                let calc = CalcNode::parse_length_or_percentage(
1461                    context,
1462                    input,
1463                    num_context,
1464                    function,
1465                    allow_anchor,
1466                )?;
1467                Ok(LengthPercentage::Calc(Box::new(calc)))
1468            },
1469            _ => Err(ParseError::unexpected_token()),
1470        }
1471    }
1472
1473    /// Parses allowing the unitless length quirk.
1474    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1475    #[inline]
1476    pub fn parse_quirky(
1477        context: &ParserContext,
1478        input: &mut Parser,
1479        allow_quirks: AllowQuirks,
1480    ) -> Result<Self, ParseError> {
1481        Self::parse_internal(
1482            context,
1483            input,
1484            AllowedNumericType::All,
1485            allow_quirks,
1486            AllowAnchorPositioningFunctions::No,
1487        )
1488    }
1489
1490    /// Parses allowing the unitless length quirk, as well as allowing
1491    /// anchor-positioning related function, `anchor-size()`.
1492    #[inline]
1493    fn parse_quirky_with_anchor_size_function(
1494        context: &ParserContext,
1495        input: &mut Parser,
1496        allow_quirks: AllowQuirks,
1497    ) -> Result<Self, ParseError> {
1498        Self::parse_internal(
1499            context,
1500            input,
1501            AllowedNumericType::All,
1502            allow_quirks,
1503            AllowAnchorPositioningFunctions::AllowAnchorSize,
1504        )
1505    }
1506
1507    /// Parses allowing the unitless length quirk, as well as allowing
1508    /// anchor-positioning related functions, `anchor()` and `anchor-size()`.
1509    #[inline]
1510    pub fn parse_quirky_with_anchor_functions(
1511        context: &ParserContext,
1512        input: &mut Parser,
1513        allow_quirks: AllowQuirks,
1514    ) -> Result<Self, ParseError> {
1515        Self::parse_internal(
1516            context,
1517            input,
1518            AllowedNumericType::All,
1519            allow_quirks,
1520            AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1521        )
1522    }
1523
1524    /// Parses non-negative length, allowing the unitless length quirk,
1525    /// as well as allowing `anchor-size()`.
1526    pub fn parse_non_negative_with_anchor_size(
1527        context: &ParserContext,
1528        input: &mut Parser,
1529        allow_quirks: AllowQuirks,
1530    ) -> Result<Self, ParseError> {
1531        Self::parse_internal(
1532            context,
1533            input,
1534            AllowedNumericType::NonNegative,
1535            allow_quirks,
1536            AllowAnchorPositioningFunctions::AllowAnchorSize,
1537        )
1538    }
1539
1540    /// Parse a non-negative length.
1541    ///
1542    /// FIXME(emilio): This should be not public and we should use
1543    /// NonNegativeLengthPercentage instead.
1544    #[inline]
1545    pub fn parse_non_negative(
1546        context: &ParserContext,
1547        input: &mut Parser,
1548    ) -> Result<Self, ParseError> {
1549        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1550    }
1551
1552    /// Parse a non-negative length, with quirks.
1553    #[inline]
1554    pub fn parse_non_negative_quirky(
1555        context: &ParserContext,
1556        input: &mut Parser,
1557        allow_quirks: AllowQuirks,
1558    ) -> Result<Self, ParseError> {
1559        Self::parse_internal(
1560            context,
1561            input,
1562            AllowedNumericType::NonNegative,
1563            allow_quirks,
1564            AllowAnchorPositioningFunctions::No,
1565        )
1566    }
1567
1568    /// Computes this specified value without style context. This succeeds for
1569    /// absolute lengths, percentages, and calc() expressions combining only
1570    /// those; it fails (returns None) for anything that needs a context to
1571    /// resolve, e.g. font- or viewport-relative units.
1572    pub fn compute_without_context(&self) -> Option<computed::LengthPercentage> {
1573        use crate::values::normalize;
1574        match self {
1575            Self::Length(length) => length
1576                .to_computed_pixel_length_without_context()
1577                .map(|v| computed::LengthPercentage::new_length(computed::Length::new(v)))
1578                .ok(),
1579            Self::Percentage(pc) => Some(computed::LengthPercentage::new_percent(
1580                computed::Percentage(normalize(pc.get())),
1581            )),
1582            Self::Calc(calc) => calc.compute_without_context(),
1583        }
1584    }
1585}
1586
1587impl Zero for LengthPercentage {
1588    fn zero() -> Self {
1589        LengthPercentage::Length(NoCalcLength::zero())
1590    }
1591
1592    fn is_zero(&self) -> bool {
1593        match *self {
1594            LengthPercentage::Length(l) => l.is_zero(),
1595            LengthPercentage::Percentage(p) => p.get() == 0.0,
1596            LengthPercentage::Calc(_) => false,
1597        }
1598    }
1599}
1600
1601impl ZeroNoPercent for LengthPercentage {
1602    fn is_zero_no_percent(&self) -> bool {
1603        match *self {
1604            LengthPercentage::Percentage(_) => false,
1605            _ => self.is_zero(),
1606        }
1607    }
1608}
1609
1610/// Check if this equal to a specific percentage.
1611pub trait EqualsPercentage {
1612    /// Returns true if this is a specific percentage value. This should exclude calc() even if it
1613    /// only contains percentage component.
1614    fn equals_percentage(&self, v: CSSFloat) -> bool;
1615}
1616
1617impl EqualsPercentage for LengthPercentage {
1618    fn equals_percentage(&self, v: CSSFloat) -> bool {
1619        match *self {
1620            LengthPercentage::Percentage(p) => p.get() == v,
1621            _ => false,
1622        }
1623    }
1624}
1625
1626/// A specified type for `<length-percentage> | auto`.
1627pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
1628
1629impl LengthPercentageOrAuto {
1630    /// Returns a value representing `0%`.
1631    #[inline]
1632    pub fn zero_percent() -> Self {
1633        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
1634    }
1635
1636    /// Parses a length or a percentage, allowing the unitless length quirk.
1637    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1638    #[inline]
1639    pub fn parse_quirky(
1640        context: &ParserContext,
1641        input: &mut Parser,
1642        allow_quirks: AllowQuirks,
1643    ) -> Result<Self, ParseError> {
1644        Self::parse_with(context, input, |context, input| {
1645            LengthPercentage::parse_quirky(context, input, allow_quirks)
1646        })
1647    }
1648}
1649
1650/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
1651pub type NonNegativeLengthPercentageOrAuto =
1652    generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
1653
1654impl NonNegativeLengthPercentageOrAuto {
1655    /// Returns a value representing `0%`.
1656    #[inline]
1657    pub fn zero_percent() -> Self {
1658        generics::LengthPercentageOrAuto::LengthPercentage(
1659            NonNegativeLengthPercentage::zero_percent(),
1660        )
1661    }
1662
1663    /// Parses a non-negative length-percentage, allowing the unitless length
1664    /// quirk.
1665    #[inline]
1666    pub fn parse_quirky(
1667        context: &ParserContext,
1668        input: &mut Parser,
1669        allow_quirks: AllowQuirks,
1670    ) -> Result<Self, ParseError> {
1671        Self::parse_with(context, input, |context, input| {
1672            NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
1673        })
1674    }
1675}
1676
1677/// A wrapper of LengthPercentage, whose value must be >= 0.
1678pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
1679
1680/// Either a NonNegativeLengthPercentage or the `normal` keyword.
1681pub type NonNegativeLengthPercentageOrNormal =
1682    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
1683
1684impl From<NoCalcLength> for NonNegativeLengthPercentage {
1685    #[inline]
1686    fn from(len: NoCalcLength) -> Self {
1687        NonNegative(LengthPercentage::from(len))
1688    }
1689}
1690
1691impl Parse for NonNegativeLengthPercentage {
1692    #[inline]
1693    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1694        Self::parse_quirky(context, input, AllowQuirks::No)
1695    }
1696}
1697
1698impl NonNegativeLengthPercentage {
1699    #[inline]
1700    /// Returns a `0%` value.
1701    pub fn zero_percent() -> Self {
1702        NonNegative(LengthPercentage::zero_percent())
1703    }
1704
1705    /// Parses a length or a percentage, allowing the unitless length quirk.
1706    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1707    #[inline]
1708    pub fn parse_quirky(
1709        context: &ParserContext,
1710        input: &mut Parser,
1711        allow_quirks: AllowQuirks,
1712    ) -> Result<Self, ParseError> {
1713        LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
1714    }
1715
1716    /// Parses a length or a percentage, allowing the unitless length quirk,
1717    /// as well as allowing `anchor-size()`.
1718    #[inline]
1719    pub fn parse_non_negative_with_anchor_size(
1720        context: &ParserContext,
1721        input: &mut Parser,
1722        allow_quirks: AllowQuirks,
1723    ) -> Result<Self, ParseError> {
1724        LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
1725            .map(NonNegative)
1726    }
1727}
1728
1729/// Either a `<length>` or the `auto` keyword.
1730///
1731/// Note that we use LengthPercentage just for convenience, since it pretty much
1732/// is everything we care about, but we could just add a similar LengthOrAuto
1733/// instead if we think getting rid of this weirdness is worth it.
1734pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
1735
1736impl LengthOrAuto {
1737    /// Parses a length, allowing the unitless length quirk.
1738    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1739    #[inline]
1740    pub fn parse_quirky(
1741        context: &ParserContext,
1742        input: &mut Parser,
1743        allow_quirks: AllowQuirks,
1744    ) -> Result<Self, ParseError> {
1745        Self::parse_with(context, input, |context, input| {
1746            Length::parse_quirky(context, input, allow_quirks)
1747        })
1748    }
1749}
1750
1751/// Either a non-negative `<length>` or the `auto` keyword.
1752pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
1753
1754/// Either a `<length>` or a `<number>`.
1755pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
1756
1757/// A specified value for `min-width`, `min-height`, `width` or `height` property.
1758pub type Size = GenericSize<NonNegativeLengthPercentage>;
1759
1760impl Parse for Size {
1761    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1762        Size::parse_quirky(context, input, AllowQuirks::No)
1763    }
1764}
1765
1766macro_rules! parse_size_non_length {
1767    ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
1768     $auto_or_none:expr => $auto_or_none_ident:ident) => {{
1769        let size = $input.try_parse(|input| {
1770            Ok(try_match_ident_ignore_ascii_case! { input,
1771                "min-content" | "-moz-min-content" => $size::MinContent,
1772                "max-content" | "-moz-max-content" => $size::MaxContent,
1773                "fit-content" | "-moz-fit-content" => $size::FitContent,
1774                #[cfg(feature = "gecko")]
1775                "-moz-available" => $size::MozAvailable,
1776                "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
1777                "stretch" if is_stretch_enabled() => $size::Stretch,
1778                $auto_or_none => $size::$auto_or_none_ident,
1779            })
1780        });
1781        if size.is_ok() {
1782            return size;
1783        }
1784    }};
1785}
1786
1787fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
1788    crate::pref!("layout.css.webkit-fill-available.enabled")
1789}
1790
1791fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
1792    // For convenience at the callsites, we check both prefs here,
1793    // since both must be 'true' in order for the keyword to be
1794    // enabled in all size properties.
1795    crate::pref!("layout.css.webkit-fill-available.enabled")
1796        && crate::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
1797}
1798
1799fn is_stretch_enabled() -> bool {
1800    crate::pref!("layout.css.stretch-size-keyword.enabled")
1801}
1802
1803fn is_fit_content_function_enabled() -> bool {
1804    crate::pref!("layout.css.fit-content-function.enabled")
1805}
1806
1807macro_rules! parse_fit_content_function {
1808    ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
1809        if is_fit_content_function_enabled() {
1810            if let Ok(length) = $input.try_parse(|input| {
1811                input.expect_function_matching("fit-content")?;
1812                input.parse_nested_block(|i| {
1813                    NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
1814                })
1815            }) {
1816                return Ok($size::FitContentFunction(length));
1817            }
1818        }
1819    };
1820}
1821
1822#[derive(Clone, Copy, PartialEq, Eq)]
1823enum ParseAnchorFunctions {
1824    Yes,
1825    No,
1826}
1827
1828impl Size {
1829    /// Parses, with quirks.
1830    pub fn parse_quirky(
1831        context: &ParserContext,
1832        input: &mut Parser,
1833        allow_quirks: AllowQuirks,
1834    ) -> Result<Self, ParseError> {
1835        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
1836        Self::parse_quirky_internal(
1837            context,
1838            input,
1839            allow_quirks,
1840            allow_webkit_fill_available,
1841            ParseAnchorFunctions::Yes,
1842        )
1843    }
1844
1845    /// Parses for flex-basis: <width>
1846    pub fn parse_size_for_flex_basis_width(
1847        context: &ParserContext,
1848        input: &mut Parser,
1849    ) -> Result<Self, ParseError> {
1850        Self::parse_quirky_internal(
1851            context,
1852            input,
1853            AllowQuirks::No,
1854            true,
1855            ParseAnchorFunctions::No,
1856        )
1857    }
1858
1859    /// Parses, with quirks and configurable support for
1860    /// whether the '-webkit-fill-available' keyword is allowed.
1861    /// TODO(dholbert) Fold this function into callsites in bug 1989073 when
1862    /// removing 'layout.css.webkit-fill-available.all-size-properties.enabled'.
1863    fn parse_quirky_internal(
1864        context: &ParserContext,
1865        input: &mut Parser,
1866        allow_quirks: AllowQuirks,
1867        allow_webkit_fill_available: bool,
1868        allow_anchor_functions: ParseAnchorFunctions,
1869    ) -> Result<Self, ParseError> {
1870        parse_size_non_length!(Size, input, allow_webkit_fill_available,
1871                               "auto" => Auto);
1872        parse_fit_content_function!(Size, input, context, allow_quirks);
1873
1874        let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
1875            && crate::pref!("layout.css.anchor-positioning.enabled", gecko = true);
1876        match input
1877            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
1878        {
1879            Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
1880            Err(e) if !allow_anchor => return Err(e.into()),
1881            Err(_) => (),
1882        };
1883        if let Ok(length) = input.try_parse(|i| {
1884            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
1885                context,
1886                i,
1887                allow_quirks,
1888            )
1889        }) {
1890            return Ok(GenericSize::AnchorContainingCalcFunction(length));
1891        }
1892        Ok(Self::AnchorSizeFunction(Box::new(
1893            GenericAnchorSizeFunction::parse(context, input)?,
1894        )))
1895    }
1896
1897    /// Parse a size for width or height, where -webkit-fill-available
1898    /// support is only controlled by one pref (vs. other properties where
1899    /// there's an additional pref check):
1900    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
1901    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
1902    pub fn parse_size_for_width_or_height_quirky(
1903        context: &ParserContext,
1904        input: &mut Parser,
1905        allow_quirks: AllowQuirks,
1906    ) -> Result<Self, ParseError> {
1907        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
1908        Self::parse_quirky_internal(
1909            context,
1910            input,
1911            allow_quirks,
1912            allow_webkit_fill_available,
1913            ParseAnchorFunctions::Yes,
1914        )
1915    }
1916
1917    /// Parse a size for width or height, where -webkit-fill-available
1918    /// support is only controlled by one pref (vs. other properties where
1919    /// there's an additional pref check):
1920    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
1921    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
1922    pub fn parse_size_for_width_or_height(
1923        context: &ParserContext,
1924        input: &mut Parser,
1925    ) -> Result<Self, ParseError> {
1926        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
1927        Self::parse_quirky_internal(
1928            context,
1929            input,
1930            AllowQuirks::No,
1931            allow_webkit_fill_available,
1932            ParseAnchorFunctions::Yes,
1933        )
1934    }
1935
1936    /// Returns `0%`.
1937    #[inline]
1938    pub fn zero_percent() -> Self {
1939        GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
1940    }
1941}
1942
1943/// A specified value for `max-width` or `max-height` property.
1944pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
1945
1946impl Parse for MaxSize {
1947    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1948        MaxSize::parse_quirky(context, input, AllowQuirks::No)
1949    }
1950}
1951
1952impl MaxSize {
1953    /// Parses, with quirks.
1954    pub fn parse_quirky(
1955        context: &ParserContext,
1956        input: &mut Parser,
1957        allow_quirks: AllowQuirks,
1958    ) -> Result<Self, ParseError> {
1959        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
1960        parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
1961                               "none" => None);
1962        parse_fit_content_function!(MaxSize, input, context, allow_quirks);
1963
1964        match input
1965            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
1966        {
1967            Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
1968            Err(e) if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => {
1969                return Err(e.into())
1970            },
1971            Err(_) => (),
1972        };
1973        if let Ok(length) = input.try_parse(|i| {
1974            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
1975                context,
1976                i,
1977                allow_quirks,
1978            )
1979        }) {
1980            return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
1981        }
1982        Ok(Self::AnchorSizeFunction(Box::new(
1983            GenericAnchorSizeFunction::parse(context, input)?,
1984        )))
1985    }
1986}
1987
1988/// A specified non-negative `<length>` | `<number>`.
1989pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
1990
1991/// A specified value for `margin` properties.
1992pub type Margin = GenericMargin<LengthPercentage>;
1993
1994impl Margin {
1995    /// Parses a margin type, allowing the unitless length quirk.
1996    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1997    #[inline]
1998    pub fn parse_quirky(
1999        context: &ParserContext,
2000        input: &mut Parser,
2001        allow_quirks: AllowQuirks,
2002    ) -> Result<Self, ParseError> {
2003        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2004        {
2005            return Ok(Self::LengthPercentage(l));
2006        }
2007        match input.try_parse(|i| i.expect_ident_matching("auto")) {
2008            Ok(_) => return Ok(Self::Auto),
2009            Err(e) if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => {
2010                return Err(e.into())
2011            },
2012            Err(_) => (),
2013        };
2014        if let Ok(l) = input.try_parse(|i| {
2015            LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2016        }) {
2017            return Ok(Self::AnchorContainingCalcFunction(l));
2018        }
2019        let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
2020        Ok(Self::AnchorSizeFunction(Box::new(inner)))
2021    }
2022}
2023
2024impl Parse for Margin {
2025    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
2026        Self::parse_quirky(context, input, AllowQuirks::No)
2027    }
2028}