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