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