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::computed::{self, CSSPixelLength, Context, FontSize};
18use crate::values::generics::length as generics;
19use crate::values::generics::length::{
20    GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
21    GenericMargin, GenericMaxSize, GenericSize,
22};
23use crate::values::generics::NonNegative;
24use crate::values::specified::calc::{
25    AllowAnchorPositioningFunctions, CalcLengthPercentage, CalcNode, PercentageContext,
26};
27use crate::values::specified::font::QueryFontMetricsFlags;
28use crate::values::specified::percentage::NoCalcPercentage;
29use crate::values::specified::NonNegativeNumber;
30use crate::values::tagged_numeric::{Extracted, NumericUnion, Unpacked};
31use crate::values::CSSFloat;
32use crate::{Zero, ZeroNoPercent};
33use app_units::AU_PER_PX;
34use cssparser::{match_ignore_ascii_case, Parser, Token};
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().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().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.style().get_font().clone_font_size().computed_size(),
1026        )
1027    }
1028}
1029
1030impl ToComputedValue for NoCalcLength {
1031    type ComputedValue = computed::Length;
1032
1033    #[inline]
1034    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1035        self.to_computed_value_with_base_size(
1036            context,
1037            FontBaseSize::CurrentStyle,
1038            LineHeightBase::CurrentStyle,
1039        )
1040    }
1041
1042    #[inline]
1043    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1044        Self::from_px(computed.px())
1045    }
1046}
1047
1048impl ToCss for NoCalcLength {
1049    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1050    where
1051        W: Write,
1052    {
1053        crate::values::serialize_specified_dimension(
1054            self.unitless_value(),
1055            self.unit(),
1056            false,
1057            dest,
1058        )
1059    }
1060}
1061
1062impl ToTyped for NoCalcLength {
1063    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1064        let numeric_type = NumericType::length();
1065        let value = self.unitless_value();
1066        let unit = CssString::from(self.unit());
1067        dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
1068            numeric_type,
1069            value,
1070            unit,
1071        })));
1072        Ok(())
1073    }
1074}
1075
1076impl SpecifiedValueInfo for NoCalcLength {}
1077
1078impl PartialOrd for NoCalcLength {
1079    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1080        // For absolute units, compare in px.
1081        if let (Some(a), Some(b)) = (self.to_px_if_absolute(), other.to_px_if_absolute()) {
1082            return a.partial_cmp(&b);
1083        }
1084        if self.unit != other.unit {
1085            return None;
1086        }
1087        self.value.partial_cmp(&other.value)
1088    }
1089}
1090
1091impl Zero for NoCalcLength {
1092    fn zero() -> Self {
1093        Self::from_px(0.)
1094    }
1095
1096    fn is_zero(&self) -> bool {
1097        NoCalcLength::is_zero(self)
1098    }
1099}
1100
1101/// An extension to `NoCalcLength` to parse `calc` expressions.
1102/// This is commonly used for the `<length>` values.
1103///
1104/// Either stored inline as length + unit without calc or as a boxed calc node.
1105///
1106/// <https://drafts.csswg.org/css-values/#lengths>
1107#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
1108pub struct Length(NumericUnion<LengthUnit, f32, CalcLengthPercentage>);
1109
1110impl ToCss for Length {
1111    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1112    where
1113        W: Write,
1114    {
1115        match self.0.unpack() {
1116            Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value).to_css(dest),
1117            Unpacked::Boxed(calc) => calc.to_css(dest),
1118        }
1119    }
1120}
1121
1122impl ToTyped for Length {
1123    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1124        match self.0.unpack() {
1125            Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value).to_typed(dest),
1126            Unpacked::Boxed(calc) => calc.to_typed(dest),
1127        }
1128    }
1129}
1130
1131impl SpecifiedValueInfo for Length {}
1132
1133impl From<NoCalcLength> for Length {
1134    #[inline]
1135    fn from(len: NoCalcLength) -> Self {
1136        Self::new(len)
1137    }
1138}
1139
1140impl Length {
1141    /// Creates a length from a non-calc `NoCalcLength`.
1142    #[inline]
1143    pub fn new(len: NoCalcLength) -> Self {
1144        Self(NumericUnion::inline(len.unit, len.value))
1145    }
1146
1147    /// Creates a length from a `calc()` expression.
1148    #[inline]
1149    pub fn new_calc(calc: Box<CalcLengthPercentage>) -> Self {
1150        Self(NumericUnion::boxed(calc))
1151    }
1152
1153    /// Returns true if this is a `calc()` expression.
1154    #[inline]
1155    pub fn is_calc(&self) -> bool {
1156        self.0.is_boxed()
1157    }
1158
1159    #[inline]
1160    fn parse_internal(
1161        context: &ParserContext,
1162        input: &mut Parser,
1163        num_context: AllowedNumericType,
1164        allow_quirks: AllowQuirks,
1165    ) -> Result<Self, ParseError> {
1166        let token = input.next()?;
1167        match *token {
1168            Token::Dimension {
1169                value, ref unit, ..
1170            } if num_context.is_ok(context.parsing_mode, value) => {
1171                NoCalcLength::parse_dimension_with_context(context, value, unit)
1172                    .map(Self::new)
1173                    .map_err(|()| ParseError::unexpected_token())
1174            },
1175            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1176                let allowed = context.parsing_mode.allows_unitless_lengths()
1177                    || allow_quirks.allowed(context.quirks_mode)
1178                    || (value == 0. && context.parsing_mode.allows_unitless_zero_lengths());
1179
1180                if !allowed {
1181                    return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1182                }
1183
1184                Ok(Self::new(NoCalcLength::from_px(value)))
1185            },
1186            Token::Function(ref name) => {
1187                let function = CalcNode::math_function(context, name)?;
1188                let calc = CalcNode::parse_length(
1189                    context,
1190                    input,
1191                    num_context,
1192                    function,
1193                    PercentageContext::not_allowed(),
1194                )?;
1195                Ok(Self::new_calc(Box::new(calc)))
1196            },
1197            _ => Err(ParseError::unexpected_token()),
1198        }
1199    }
1200
1201    /// Parse a non-negative length
1202    #[inline]
1203    pub fn parse_non_negative(
1204        context: &ParserContext,
1205        input: &mut Parser,
1206    ) -> Result<Self, ParseError> {
1207        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1208    }
1209
1210    /// Parse a non-negative length, allowing quirks.
1211    #[inline]
1212    pub fn parse_non_negative_quirky(
1213        context: &ParserContext,
1214        input: &mut Parser,
1215        allow_quirks: AllowQuirks,
1216    ) -> Result<Self, ParseError> {
1217        Self::parse_internal(
1218            context,
1219            input,
1220            AllowedNumericType::NonNegative,
1221            allow_quirks,
1222        )
1223    }
1224
1225    /// Get an absolute length from a px value.
1226    #[inline]
1227    pub fn from_px(px_value: CSSFloat) -> Length {
1228        Self::new(NoCalcLength::from_px(px_value))
1229    }
1230
1231    /// Get a px value without context.
1232    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1233        match self.0.unpack() {
1234            Unpacked::Inline(unit, value) => {
1235                NoCalcLength::new(unit, value).to_computed_pixel_length_without_context()
1236            },
1237            Unpacked::Boxed(calc) => calc.to_computed_pixel_length_without_context(),
1238        }
1239    }
1240
1241    /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.
1242    #[cfg(feature = "gecko")]
1243    pub fn to_computed_pixel_length_with_font_metrics(
1244        &self,
1245        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1246    ) -> Result<CSSFloat, ()> {
1247        match self.0.unpack() {
1248            Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value)
1249                .to_computed_pixel_length_with_font_metrics(get_font_metrics),
1250            Unpacked::Boxed(calc) => {
1251                calc.to_computed_pixel_length_with_font_metrics(get_font_metrics)
1252            },
1253        }
1254    }
1255}
1256
1257impl Parse for Length {
1258    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1259        Self::parse_quirky(context, input, AllowQuirks::No)
1260    }
1261}
1262
1263impl ToComputedValue for Length {
1264    type ComputedValue = computed::Length;
1265
1266    #[inline]
1267    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1268        match self.0.unpack() {
1269            Unpacked::Inline(unit, value) => {
1270                NoCalcLength::new(unit, value).to_computed_value(context)
1271            },
1272            Unpacked::Boxed(calc) => {
1273                let result = calc.to_computed_value(context);
1274                debug_assert!(
1275                    result.to_length().is_some(),
1276                    "{:?} didn't resolve to a length: {:?}",
1277                    calc,
1278                    result,
1279                );
1280                result.to_length().unwrap_or_else(computed::Length::zero)
1281            },
1282        }
1283    }
1284
1285    #[inline]
1286    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1287        Self::new(NoCalcLength::from_computed_value(computed))
1288    }
1289}
1290
1291impl Zero for Length {
1292    fn zero() -> Self {
1293        Self::new(NoCalcLength::zero())
1294    }
1295
1296    fn is_zero(&self) -> bool {
1297        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
1298        // non-zero here?
1299        match self.0.unpack() {
1300            Unpacked::Inline(_, value) => value == 0.0,
1301            Unpacked::Boxed(_) => false,
1302        }
1303    }
1304}
1305
1306impl Length {
1307    /// Parses a length, with quirks.
1308    pub fn parse_quirky(
1309        context: &ParserContext,
1310        input: &mut Parser,
1311        allow_quirks: AllowQuirks,
1312    ) -> Result<Self, ParseError> {
1313        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1314    }
1315}
1316
1317/// A wrapper of Length, whose value must be >= 0.
1318pub type NonNegativeLength = NonNegative<Length>;
1319
1320impl Parse for NonNegativeLength {
1321    #[inline]
1322    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1323        Ok(NonNegative(Length::parse_non_negative(context, input)?))
1324    }
1325}
1326
1327impl From<NoCalcLength> for NonNegativeLength {
1328    #[inline]
1329    fn from(len: NoCalcLength) -> Self {
1330        NonNegative(Length::new(len))
1331    }
1332}
1333
1334impl From<Length> for NonNegativeLength {
1335    #[inline]
1336    fn from(len: Length) -> Self {
1337        NonNegative(len)
1338    }
1339}
1340
1341impl NonNegativeLength {
1342    /// Get an absolute length from a px value.
1343    #[inline]
1344    pub fn from_px(px_value: CSSFloat) -> Self {
1345        Length::from_px(px_value.max(0.)).into()
1346    }
1347
1348    /// Parses a non-negative length, optionally with quirks.
1349    #[inline]
1350    pub fn parse_quirky(
1351        context: &ParserContext,
1352        input: &mut Parser,
1353        allow_quirks: AllowQuirks,
1354    ) -> Result<Self, ParseError> {
1355        Ok(NonNegative(Length::parse_non_negative_quirky(
1356            context,
1357            input,
1358            allow_quirks,
1359        )?))
1360    }
1361}
1362
1363/// A `<length-percentage>` value. This can be either a `<length>`, a
1364/// `<percentage>`, or a combination of both via `calc()`.
1365///
1366/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
1367#[allow(missing_docs)]
1368#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1369pub enum LengthPercentage {
1370    Length(NoCalcLength),
1371    Percentage(NoCalcPercentage),
1372    Calc(Box<CalcLengthPercentage>),
1373}
1374
1375impl From<Length> for LengthPercentage {
1376    fn from(len: Length) -> LengthPercentage {
1377        match len.0.extract() {
1378            Extracted::Inline(unit, value) => {
1379                LengthPercentage::Length(NoCalcLength::new(unit, value))
1380            },
1381            Extracted::Boxed(calc) => LengthPercentage::Calc(calc),
1382        }
1383    }
1384}
1385
1386impl From<NoCalcLength> for LengthPercentage {
1387    #[inline]
1388    fn from(len: NoCalcLength) -> Self {
1389        LengthPercentage::Length(len)
1390    }
1391}
1392
1393impl From<computed::Percentage> for LengthPercentage {
1394    #[inline]
1395    fn from(pc: computed::Percentage) -> Self {
1396        LengthPercentage::Percentage(NoCalcPercentage::new(pc.0))
1397    }
1398}
1399
1400impl Parse for LengthPercentage {
1401    #[inline]
1402    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1403        Self::parse_quirky(context, input, AllowQuirks::No)
1404    }
1405}
1406
1407impl LengthPercentage {
1408    #[inline]
1409    /// Returns a `0%` value.
1410    pub fn zero_percent() -> LengthPercentage {
1411        LengthPercentage::Percentage(NoCalcPercentage::zero())
1412    }
1413
1414    #[inline]
1415    /// Returns a `100%` value.
1416    pub fn hundred_percent() -> LengthPercentage {
1417        LengthPercentage::Percentage(NoCalcPercentage::hundred())
1418    }
1419
1420    fn parse_internal(
1421        context: &ParserContext,
1422        input: &mut Parser,
1423        num_context: AllowedNumericType,
1424        allow_quirks: AllowQuirks,
1425        allow_anchor: AllowAnchorPositioningFunctions,
1426    ) -> Result<Self, ParseError> {
1427        let token = input.next()?;
1428        match *token {
1429            Token::Dimension {
1430                value, ref unit, ..
1431            } if num_context.is_ok(context.parsing_mode, value) => {
1432                NoCalcLength::parse_dimension_with_context(context, value, unit)
1433                    .map(LengthPercentage::Length)
1434                    .map_err(|()| ParseError::unexpected_token())
1435            },
1436            Token::Percentage { unit_value, .. }
1437                if num_context.is_ok(context.parsing_mode, unit_value) =>
1438            {
1439                Ok(LengthPercentage::Percentage(NoCalcPercentage::new(
1440                    unit_value,
1441                )))
1442            },
1443            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1444                let allowed = context.parsing_mode.allows_unitless_lengths()
1445                    || allow_quirks.allowed(context.quirks_mode)
1446                    || (value == 0. && context.parsing_mode.allows_unitless_zero_lengths());
1447
1448                if !allowed {
1449                    return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1450                }
1451
1452                Ok(LengthPercentage::Length(NoCalcLength::from_px(value)))
1453            },
1454            Token::Function(ref name) => {
1455                let function = CalcNode::math_function(context, name)?;
1456                let calc = CalcNode::parse_length_or_percentage(
1457                    context,
1458                    input,
1459                    num_context,
1460                    function,
1461                    allow_anchor,
1462                )?;
1463                Ok(LengthPercentage::Calc(Box::new(calc)))
1464            },
1465            _ => Err(ParseError::unexpected_token()),
1466        }
1467    }
1468
1469    /// Parses allowing the unitless length quirk.
1470    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1471    #[inline]
1472    pub fn parse_quirky(
1473        context: &ParserContext,
1474        input: &mut Parser,
1475        allow_quirks: AllowQuirks,
1476    ) -> Result<Self, ParseError> {
1477        Self::parse_internal(
1478            context,
1479            input,
1480            AllowedNumericType::All,
1481            allow_quirks,
1482            AllowAnchorPositioningFunctions::No,
1483        )
1484    }
1485
1486    /// Parses allowing the unitless length quirk, as well as allowing
1487    /// anchor-positioning related function, `anchor-size()`.
1488    #[inline]
1489    fn parse_quirky_with_anchor_size_function(
1490        context: &ParserContext,
1491        input: &mut Parser,
1492        allow_quirks: AllowQuirks,
1493    ) -> Result<Self, ParseError> {
1494        Self::parse_internal(
1495            context,
1496            input,
1497            AllowedNumericType::All,
1498            allow_quirks,
1499            AllowAnchorPositioningFunctions::AllowAnchorSize,
1500        )
1501    }
1502
1503    /// Parses allowing the unitless length quirk, as well as allowing
1504    /// anchor-positioning related functions, `anchor()` and `anchor-size()`.
1505    #[inline]
1506    pub fn parse_quirky_with_anchor_functions(
1507        context: &ParserContext,
1508        input: &mut Parser,
1509        allow_quirks: AllowQuirks,
1510    ) -> Result<Self, ParseError> {
1511        Self::parse_internal(
1512            context,
1513            input,
1514            AllowedNumericType::All,
1515            allow_quirks,
1516            AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1517        )
1518    }
1519
1520    /// Parses non-negative length, allowing the unitless length quirk,
1521    /// as well as allowing `anchor-size()`.
1522    pub fn parse_non_negative_with_anchor_size(
1523        context: &ParserContext,
1524        input: &mut Parser,
1525        allow_quirks: AllowQuirks,
1526    ) -> Result<Self, ParseError> {
1527        Self::parse_internal(
1528            context,
1529            input,
1530            AllowedNumericType::NonNegative,
1531            allow_quirks,
1532            AllowAnchorPositioningFunctions::AllowAnchorSize,
1533        )
1534    }
1535
1536    /// Parse a non-negative length.
1537    ///
1538    /// FIXME(emilio): This should be not public and we should use
1539    /// NonNegativeLengthPercentage instead.
1540    #[inline]
1541    pub fn parse_non_negative(
1542        context: &ParserContext,
1543        input: &mut Parser,
1544    ) -> Result<Self, ParseError> {
1545        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1546    }
1547
1548    /// Parse a non-negative length, with quirks.
1549    #[inline]
1550    pub fn parse_non_negative_quirky(
1551        context: &ParserContext,
1552        input: &mut Parser,
1553        allow_quirks: AllowQuirks,
1554    ) -> Result<Self, ParseError> {
1555        Self::parse_internal(
1556            context,
1557            input,
1558            AllowedNumericType::NonNegative,
1559            allow_quirks,
1560            AllowAnchorPositioningFunctions::No,
1561        )
1562    }
1563
1564    /// Computes this specified value without style context. This succeeds for
1565    /// absolute lengths, percentages, and calc() expressions combining only
1566    /// those; it fails (returns None) for anything that needs a context to
1567    /// resolve, e.g. font- or viewport-relative units.
1568    pub fn compute_without_context(&self) -> Option<computed::LengthPercentage> {
1569        use crate::values::normalize;
1570        match self {
1571            Self::Length(length) => length
1572                .to_computed_pixel_length_without_context()
1573                .map(|v| computed::LengthPercentage::new_length(computed::Length::new(v)))
1574                .ok(),
1575            Self::Percentage(pc) => Some(computed::LengthPercentage::new_percent(
1576                computed::Percentage(normalize(pc.get())),
1577            )),
1578            Self::Calc(calc) => calc.compute_without_context(),
1579        }
1580    }
1581}
1582
1583impl Zero for LengthPercentage {
1584    fn zero() -> Self {
1585        LengthPercentage::Length(NoCalcLength::zero())
1586    }
1587
1588    fn is_zero(&self) -> bool {
1589        match *self {
1590            LengthPercentage::Length(l) => l.is_zero(),
1591            LengthPercentage::Percentage(p) => p.get() == 0.0,
1592            LengthPercentage::Calc(_) => false,
1593        }
1594    }
1595}
1596
1597impl ZeroNoPercent for LengthPercentage {
1598    fn is_zero_no_percent(&self) -> bool {
1599        match *self {
1600            LengthPercentage::Percentage(_) => false,
1601            _ => self.is_zero(),
1602        }
1603    }
1604}
1605
1606/// Check if this equal to a specific percentage.
1607pub trait EqualsPercentage {
1608    /// Returns true if this is a specific percentage value. This should exclude calc() even if it
1609    /// only contains percentage component.
1610    fn equals_percentage(&self, v: CSSFloat) -> bool;
1611}
1612
1613impl EqualsPercentage for LengthPercentage {
1614    fn equals_percentage(&self, v: CSSFloat) -> bool {
1615        match *self {
1616            LengthPercentage::Percentage(p) => p.get() == v,
1617            _ => false,
1618        }
1619    }
1620}
1621
1622/// A specified type for `<length-percentage> | auto`.
1623pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
1624
1625impl LengthPercentageOrAuto {
1626    /// Returns a value representing `0%`.
1627    #[inline]
1628    pub fn zero_percent() -> Self {
1629        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
1630    }
1631
1632    /// Parses a length or a percentage, allowing the unitless length quirk.
1633    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1634    #[inline]
1635    pub fn parse_quirky(
1636        context: &ParserContext,
1637        input: &mut Parser,
1638        allow_quirks: AllowQuirks,
1639    ) -> Result<Self, ParseError> {
1640        Self::parse_with(context, input, |context, input| {
1641            LengthPercentage::parse_quirky(context, input, allow_quirks)
1642        })
1643    }
1644}
1645
1646/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
1647pub type NonNegativeLengthPercentageOrAuto =
1648    generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
1649
1650impl NonNegativeLengthPercentageOrAuto {
1651    /// Returns a value representing `0%`.
1652    #[inline]
1653    pub fn zero_percent() -> Self {
1654        generics::LengthPercentageOrAuto::LengthPercentage(
1655            NonNegativeLengthPercentage::zero_percent(),
1656        )
1657    }
1658
1659    /// Parses a non-negative length-percentage, allowing the unitless length
1660    /// quirk.
1661    #[inline]
1662    pub fn parse_quirky(
1663        context: &ParserContext,
1664        input: &mut Parser,
1665        allow_quirks: AllowQuirks,
1666    ) -> Result<Self, ParseError> {
1667        Self::parse_with(context, input, |context, input| {
1668            NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
1669        })
1670    }
1671}
1672
1673/// A wrapper of LengthPercentage, whose value must be >= 0.
1674pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
1675
1676/// Either a NonNegativeLengthPercentage or the `normal` keyword.
1677pub type NonNegativeLengthPercentageOrNormal =
1678    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
1679
1680impl From<NoCalcLength> for NonNegativeLengthPercentage {
1681    #[inline]
1682    fn from(len: NoCalcLength) -> Self {
1683        NonNegative(LengthPercentage::from(len))
1684    }
1685}
1686
1687impl Parse for NonNegativeLengthPercentage {
1688    #[inline]
1689    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1690        Self::parse_quirky(context, input, AllowQuirks::No)
1691    }
1692}
1693
1694impl NonNegativeLengthPercentage {
1695    #[inline]
1696    /// Returns a `0%` value.
1697    pub fn zero_percent() -> Self {
1698        NonNegative(LengthPercentage::zero_percent())
1699    }
1700
1701    /// Parses a length or a percentage, allowing the unitless length quirk.
1702    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1703    #[inline]
1704    pub fn parse_quirky(
1705        context: &ParserContext,
1706        input: &mut Parser,
1707        allow_quirks: AllowQuirks,
1708    ) -> Result<Self, ParseError> {
1709        LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
1710    }
1711
1712    /// Parses a length or a percentage, allowing the unitless length quirk,
1713    /// as well as allowing `anchor-size()`.
1714    #[inline]
1715    pub fn parse_non_negative_with_anchor_size(
1716        context: &ParserContext,
1717        input: &mut Parser,
1718        allow_quirks: AllowQuirks,
1719    ) -> Result<Self, ParseError> {
1720        LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
1721            .map(NonNegative)
1722    }
1723}
1724
1725/// Either a `<length>` or the `auto` keyword.
1726///
1727/// Note that we use LengthPercentage just for convenience, since it pretty much
1728/// is everything we care about, but we could just add a similar LengthOrAuto
1729/// instead if we think getting rid of this weirdness is worth it.
1730pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
1731
1732impl LengthOrAuto {
1733    /// Parses a length, allowing the unitless length quirk.
1734    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1735    #[inline]
1736    pub fn parse_quirky(
1737        context: &ParserContext,
1738        input: &mut Parser,
1739        allow_quirks: AllowQuirks,
1740    ) -> Result<Self, ParseError> {
1741        Self::parse_with(context, input, |context, input| {
1742            Length::parse_quirky(context, input, allow_quirks)
1743        })
1744    }
1745}
1746
1747/// Either a non-negative `<length>` or the `auto` keyword.
1748pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
1749
1750/// Either a `<length>` or a `<number>`.
1751pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
1752
1753/// A specified value for `min-width`, `min-height`, `width` or `height` property.
1754pub type Size = GenericSize<NonNegativeLengthPercentage>;
1755
1756impl Parse for Size {
1757    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1758        Size::parse_quirky(context, input, AllowQuirks::No)
1759    }
1760}
1761
1762macro_rules! parse_size_non_length {
1763    ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
1764     $auto_or_none:expr => $auto_or_none_ident:ident) => {{
1765        let size = $input.try_parse(|input| {
1766            Ok(try_match_ident_ignore_ascii_case! { input,
1767                "min-content" | "-moz-min-content" => $size::MinContent,
1768                "max-content" | "-moz-max-content" => $size::MaxContent,
1769                "fit-content" | "-moz-fit-content" => $size::FitContent,
1770                #[cfg(feature = "gecko")]
1771                "-moz-available" => $size::MozAvailable,
1772                "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
1773                "stretch" if is_stretch_enabled() => $size::Stretch,
1774                $auto_or_none => $size::$auto_or_none_ident,
1775            })
1776        });
1777        if size.is_ok() {
1778            return size;
1779        }
1780    }};
1781}
1782
1783fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
1784    crate::pref!("layout.css.webkit-fill-available.enabled")
1785}
1786
1787fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
1788    // For convenience at the callsites, we check both prefs here,
1789    // since both must be 'true' in order for the keyword to be
1790    // enabled in all size properties.
1791    crate::pref!("layout.css.webkit-fill-available.enabled")
1792        && crate::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
1793}
1794
1795fn is_stretch_enabled() -> bool {
1796    crate::pref!("layout.css.stretch-size-keyword.enabled")
1797}
1798
1799fn is_fit_content_function_enabled() -> bool {
1800    crate::pref!("layout.css.fit-content-function.enabled")
1801}
1802
1803macro_rules! parse_fit_content_function {
1804    ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
1805        if is_fit_content_function_enabled() {
1806            if let Ok(length) = $input.try_parse(|input| {
1807                input.expect_function_matching("fit-content")?;
1808                input.parse_nested_block(|i| {
1809                    NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
1810                })
1811            }) {
1812                return Ok($size::FitContentFunction(length));
1813            }
1814        }
1815    };
1816}
1817
1818#[derive(Clone, Copy, PartialEq, Eq)]
1819enum ParseAnchorFunctions {
1820    Yes,
1821    No,
1822}
1823
1824impl Size {
1825    /// Parses, with quirks.
1826    pub fn parse_quirky(
1827        context: &ParserContext,
1828        input: &mut Parser,
1829        allow_quirks: AllowQuirks,
1830    ) -> Result<Self, ParseError> {
1831        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
1832        Self::parse_quirky_internal(
1833            context,
1834            input,
1835            allow_quirks,
1836            allow_webkit_fill_available,
1837            ParseAnchorFunctions::Yes,
1838        )
1839    }
1840
1841    /// Parses for flex-basis: <width>
1842    pub fn parse_size_for_flex_basis_width(
1843        context: &ParserContext,
1844        input: &mut Parser,
1845    ) -> Result<Self, ParseError> {
1846        Self::parse_quirky_internal(
1847            context,
1848            input,
1849            AllowQuirks::No,
1850            true,
1851            ParseAnchorFunctions::No,
1852        )
1853    }
1854
1855    /// Parses, with quirks and configurable support for
1856    /// whether the '-webkit-fill-available' keyword is allowed.
1857    /// TODO(dholbert) Fold this function into callsites in bug 1989073 when
1858    /// removing 'layout.css.webkit-fill-available.all-size-properties.enabled'.
1859    fn parse_quirky_internal(
1860        context: &ParserContext,
1861        input: &mut Parser,
1862        allow_quirks: AllowQuirks,
1863        allow_webkit_fill_available: bool,
1864        allow_anchor_functions: ParseAnchorFunctions,
1865    ) -> Result<Self, ParseError> {
1866        parse_size_non_length!(Size, input, allow_webkit_fill_available,
1867                               "auto" => Auto);
1868        parse_fit_content_function!(Size, input, context, allow_quirks);
1869
1870        let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
1871            && crate::pref!("layout.css.anchor-positioning.enabled", gecko = true);
1872        match input
1873            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
1874        {
1875            Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
1876            Err(e) if !allow_anchor => return Err(e.into()),
1877            Err(_) => (),
1878        };
1879        if let Ok(length) = input.try_parse(|i| {
1880            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
1881                context,
1882                i,
1883                allow_quirks,
1884            )
1885        }) {
1886            return Ok(GenericSize::AnchorContainingCalcFunction(length));
1887        }
1888        Ok(Self::AnchorSizeFunction(Box::new(
1889            GenericAnchorSizeFunction::parse(context, input)?,
1890        )))
1891    }
1892
1893    /// Parse a size for width or height, where -webkit-fill-available
1894    /// support is only controlled by one pref (vs. other properties where
1895    /// there's an additional pref check):
1896    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
1897    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
1898    pub fn parse_size_for_width_or_height_quirky(
1899        context: &ParserContext,
1900        input: &mut Parser,
1901        allow_quirks: AllowQuirks,
1902    ) -> Result<Self, ParseError> {
1903        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
1904        Self::parse_quirky_internal(
1905            context,
1906            input,
1907            allow_quirks,
1908            allow_webkit_fill_available,
1909            ParseAnchorFunctions::Yes,
1910        )
1911    }
1912
1913    /// Parse a size for width or height, where -webkit-fill-available
1914    /// support is only controlled by one pref (vs. other properties where
1915    /// there's an additional pref check):
1916    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with
1917    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.
1918    pub fn parse_size_for_width_or_height(
1919        context: &ParserContext,
1920        input: &mut Parser,
1921    ) -> Result<Self, ParseError> {
1922        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
1923        Self::parse_quirky_internal(
1924            context,
1925            input,
1926            AllowQuirks::No,
1927            allow_webkit_fill_available,
1928            ParseAnchorFunctions::Yes,
1929        )
1930    }
1931
1932    /// Returns `0%`.
1933    #[inline]
1934    pub fn zero_percent() -> Self {
1935        GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
1936    }
1937}
1938
1939/// A specified value for `max-width` or `max-height` property.
1940pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
1941
1942impl Parse for MaxSize {
1943    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1944        MaxSize::parse_quirky(context, input, AllowQuirks::No)
1945    }
1946}
1947
1948impl MaxSize {
1949    /// Parses, with quirks.
1950    pub fn parse_quirky(
1951        context: &ParserContext,
1952        input: &mut Parser,
1953        allow_quirks: AllowQuirks,
1954    ) -> Result<Self, ParseError> {
1955        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
1956        parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
1957                               "none" => None);
1958        parse_fit_content_function!(MaxSize, input, context, allow_quirks);
1959
1960        match input
1961            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
1962        {
1963            Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
1964            Err(e) if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => {
1965                return Err(e.into())
1966            },
1967            Err(_) => (),
1968        };
1969        if let Ok(length) = input.try_parse(|i| {
1970            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
1971                context,
1972                i,
1973                allow_quirks,
1974            )
1975        }) {
1976            return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
1977        }
1978        Ok(Self::AnchorSizeFunction(Box::new(
1979            GenericAnchorSizeFunction::parse(context, input)?,
1980        )))
1981    }
1982}
1983
1984/// A specified non-negative `<length>` | `<number>`.
1985pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
1986
1987/// A specified value for `margin` properties.
1988pub type Margin = GenericMargin<LengthPercentage>;
1989
1990impl Margin {
1991    /// Parses a margin type, allowing the unitless length quirk.
1992    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1993    #[inline]
1994    pub fn parse_quirky(
1995        context: &ParserContext,
1996        input: &mut Parser,
1997        allow_quirks: AllowQuirks,
1998    ) -> Result<Self, ParseError> {
1999        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2000        {
2001            return Ok(Self::LengthPercentage(l));
2002        }
2003        match input.try_parse(|i| i.expect_ident_matching("auto")) {
2004            Ok(_) => return Ok(Self::Auto),
2005            Err(e) if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => {
2006                return Err(e.into())
2007            },
2008            Err(_) => (),
2009        };
2010        if let Ok(l) = input.try_parse(|i| {
2011            LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2012        }) {
2013            return Ok(Self::AnchorContainingCalcFunction(l));
2014        }
2015        let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
2016        Ok(Self::AnchorSizeFunction(Box::new(inner)))
2017    }
2018}
2019
2020impl Parse for Margin {
2021    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
2022        Self::parse_quirky(context, input, AllowQuirks::No)
2023    }
2024}