1use super::{AllowQuirks, Number, ToComputedValue};
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::derives::*;
12use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
13#[cfg(feature = "gecko")]
14use crate::gecko_bindings::structs::GeckoFontMetrics;
15use crate::parser::{Parse, ParserContext};
16use crate::typed_om::{NumericType, NumericValue, ToTyped, TypedValue, UnitValue};
17use crate::values::CSSFloat;
18use crate::values::computed::{self, CSSPixelLength, Context, FontSize};
19use crate::values::generics::NonNegative;
20use crate::values::generics::length as generics;
21use crate::values::generics::length::{
22 GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
23 GenericMargin, GenericMaxSize, GenericSize,
24};
25use crate::values::specified::NonNegativeNumber;
26use crate::values::specified::calc::{
27 AllowAnchorPositioningFunctions, CalcLengthPercentage, CalcNode, PercentageContext,
28};
29use crate::values::specified::font::QueryFontMetricsFlags;
30use crate::values::specified::percentage::NoCalcPercentage;
31use crate::values::tagged_numeric::{Extracted, NumericUnion, Unpacked};
32use crate::{Zero, ZeroNoPercent};
33use app_units::AU_PER_PX;
34use cssparser::{Parser, Token, match_ignore_ascii_case};
35use std::cmp;
36use std::fmt::{self, Write};
37use style_traits::values::specified::AllowedNumericType;
38use style_traits::{
39 CssString, CssWriter, ParseError, ParsingMode, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
40};
41use thin_vec::ThinVec;
42
43pub use super::image::Image;
44pub use super::image::{EndingShape as GradientEndingShape, Gradient};
45
46pub const PX_PER_IN: CSSFloat = 96.;
48pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;
50pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;
52pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;
54pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;
56pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;
58
59#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
67#[repr(u8)]
68#[allow(missing_docs)]
69pub enum LengthUnit {
70 Px,
72 In,
73 Cm,
74 Mm,
75 Q,
76 Pt,
77 Pc,
78 Em,
80 Ex,
81 Rex,
82 Ch,
83 Rch,
84 Cap,
85 Rcap,
86 Ic,
87 Ric,
88 Rem,
89 Lh,
90 Rlh,
91 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 Cqw,
118 Cqh,
119 Cqi,
120 Cqb,
121 Cqmin,
122 Cqmax,
123 ServoCharacterWidth,
125}
126
127impl LengthUnit {
128 #[inline]
130 pub fn from_str(unit: &str) -> Result<Self, ()> {
131 Self::from_str_with_flags(ParsingMode::DEFAULT, false, unit)
132 }
133
134 #[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 "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 "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 "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 #[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 #[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 #[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 #[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 #[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 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#[derive(Clone, Copy, Debug, PartialEq)]
385pub enum FontBaseSize {
386 CurrentStyle,
388 InheritedStyle,
390}
391
392#[derive(Clone, Copy, Debug, PartialEq)]
394pub enum LineHeightBase {
395 CurrentStyle,
397 InheritedStyle,
399}
400
401impl FontBaseSize {
402 pub fn resolve(&self, context: &Context) -> computed::FontSize {
404 let style = context.style();
405 match *self {
406 Self::CurrentStyle => style.get_font().slow_clone_font_size(),
407 Self::InheritedStyle => {
408 let zoom = style.effective_zoom_for_inheritance;
411 style.get_parent_font().slow_clone_font_size().zoom(zoom)
412 },
413 }
414 }
415}
416
417pub enum ViewportVariant {
419 UADefault,
421 Small,
423 Large,
425 Dynamic,
427}
428
429#[derive(PartialEq)]
431enum ViewportUnit {
432 Vw,
434 Vh,
436 Vmin,
438 Vmax,
440 Vb,
442 Vi,
444}
445
446#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
450#[repr(C)]
451pub struct NoCalcLength {
452 unit: LengthUnit,
453 value: CSSFloat,
454}
455
456impl NoCalcLength {
457 pub const EM: &'static str = "em";
459 pub const EX: &'static str = "ex";
461 pub const REX: &'static str = "rex";
463 pub const CH: &'static str = "ch";
465 pub const RCH: &'static str = "rch";
467 pub const CAP: &'static str = "cap";
469 pub const RCAP: &'static str = "rcap";
471 pub const IC: &'static str = "ic";
473 pub const RIC: &'static str = "ric";
475 pub const REM: &'static str = "rem";
477 pub const LH: &'static str = "lh";
479 pub const RLH: &'static str = "rlh";
481
482 #[inline]
484 pub fn new(unit: LengthUnit, value: CSSFloat) -> Self {
485 Self { unit, value }
486 }
487
488 #[inline]
490 pub fn length_unit(&self) -> LengthUnit {
491 self.unit
492 }
493
494 #[inline]
496 pub fn unitless_value(&self) -> CSSFloat {
497 self.value
498 }
499
500 #[inline]
502 pub fn unit(&self) -> &'static str {
503 self.unit.as_str()
504 }
505
506 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 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 pub fn is_negative(&self) -> bool {
533 self.value.is_sign_negative()
534 }
535
536 pub fn is_zero(&self) -> bool {
538 self.value == 0.0
539 }
540
541 pub fn is_infinite(&self) -> bool {
543 self.value.is_infinite()
544 }
545
546 pub fn is_nan(&self) -> bool {
548 self.value.is_nan()
549 }
550
551 pub fn should_zoom_text(&self) -> bool {
556 !self.unit.is_font_relative() && self.unit != LengthUnit::ServoCharacterWidth
557 }
558
559 pub(crate) fn sort_key(&self) -> crate::values::generics::calc::SortKey {
562 self.unit.sort_key()
563 }
564
565 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 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 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 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 #[inline]
609 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
610 self.to_px_if_absolute().ok_or(())
611 }
612
613 #[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 _ => return Err(()),
641 })
642 }
643
644 #[inline]
646 pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
647 Self::new(LengthUnit::Px, px_value)
648 }
649
650 #[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 #[inline]
668 pub fn from_em(value: CSSFloat) -> Self {
669 Self::new(LengthUnit::Em, value)
670 }
671
672 #[inline]
674 pub fn from_servo_character_width(value: i32) -> Self {
675 Self::new(LengthUnit::ServoCharacterWidth, value as CSSFloat)
676 }
677
678 fn font_relative_to_computed_value(
681 &self,
682 context: &Context,
683 base_size: FontBaseSize,
684 line_height_base: LineHeightBase,
685 ) -> computed::Length {
686 let (reference_size, length) =
687 self.reference_font_size_and_length(context, base_size, line_height_base);
688 (reference_size * length).finite()
689 }
690
691 fn reference_font_size_and_length(
692 &self,
693 context: &Context,
694 base_size: FontBaseSize,
695 line_height_base: LineHeightBase,
696 ) -> (computed::Length, CSSFloat) {
697 fn query_font_metrics(
698 context: &Context,
699 base_size: FontBaseSize,
700 orientation: FontMetricsOrientation,
701 flags: QueryFontMetricsFlags,
702 ) -> FontMetrics {
703 context.query_font_metrics(base_size, orientation, flags)
704 }
705
706 fn ex_size(
707 context: &Context,
708 base_size: FontBaseSize,
709 reference_font_size: &FontSize,
710 ) -> computed::Length {
711 let metrics = query_font_metrics(
712 context,
713 base_size,
714 FontMetricsOrientation::Horizontal,
715 QueryFontMetricsFlags::empty(),
716 );
717 metrics.x_height_or_default(reference_font_size.used_size())
718 }
719
720 fn ch_size(
721 context: &Context,
722 base_size: FontBaseSize,
723 reference_font_size: &FontSize,
724 ) -> computed::Length {
725 let metrics = query_font_metrics(
726 context,
727 base_size,
728 FontMetricsOrientation::MatchContextPreferHorizontal,
729 QueryFontMetricsFlags::NEEDS_CH,
730 );
731 metrics.zero_advance_measure_or_default(
732 reference_font_size.used_size(),
733 context.style().writing_mode.is_upright(),
734 )
735 }
736
737 fn cap_size(context: &Context, base_size: FontBaseSize) -> computed::Length {
738 let metrics = query_font_metrics(
739 context,
740 base_size,
741 FontMetricsOrientation::Horizontal,
742 QueryFontMetricsFlags::empty(),
743 );
744 metrics.cap_height_or_default()
745 }
746
747 fn ic_size(
748 context: &Context,
749 base_size: FontBaseSize,
750 reference_font_size: &FontSize,
751 ) -> computed::Length {
752 let metrics = query_font_metrics(
753 context,
754 base_size,
755 FontMetricsOrientation::MatchContextPreferVertical,
756 QueryFontMetricsFlags::NEEDS_IC,
757 );
758 metrics.ic_width_or_default(reference_font_size.used_size())
759 }
760
761 context
762 .builder
763 .add_flags(ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS);
764
765 let reference_font_size = base_size.resolve(context);
766 let length = self.value;
767 match self.unit {
768 LengthUnit::Em => {
769 if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
770 context
771 .rule_cache_conditions
772 .borrow_mut()
773 .set_font_size_dependency(reference_font_size.computed_size);
774 }
775
776 (reference_font_size.computed_size(), length)
777 },
778 LengthUnit::Lh => {
779 let reference_size = if context.in_media_query {
780 context
781 .device()
782 .calc_line_height(
783 context.default_style().get_font(),
784 context.style().writing_mode,
785 None,
786 )
787 .0
788 } else {
789 let line_height = context.builder.calc_line_height(
790 context.device(),
791 line_height_base,
792 context.style().writing_mode,
793 );
794 if context.for_non_inherited_property
795 && line_height_base == LineHeightBase::CurrentStyle
796 {
797 context
798 .rule_cache_conditions
799 .borrow_mut()
800 .set_line_height_dependency(line_height)
801 }
802 line_height.0
803 };
804 (reference_size, length)
805 },
806 LengthUnit::Ex => (ex_size(context, base_size, &reference_font_size), length),
807 LengthUnit::Ch => (ch_size(context, base_size, &reference_font_size), length),
808 LengthUnit::Cap => (cap_size(context, base_size), length),
809 LengthUnit::Ic => (ic_size(context, base_size, &reference_font_size), length),
810 LengthUnit::Rex => {
811 let reference_size = if context.builder.is_root_element || context.in_media_query {
812 ex_size(context, base_size, &reference_font_size)
813 } else {
814 context
815 .device()
816 .root_font_metrics_ex()
817 .zoom(context.builder.effective_zoom)
818 };
819 (reference_size, length)
820 },
821 LengthUnit::Rch => {
822 let reference_size = if context.builder.is_root_element || context.in_media_query {
823 ch_size(context, base_size, &reference_font_size)
824 } else {
825 context
826 .device()
827 .root_font_metrics_ch()
828 .zoom(context.builder.effective_zoom)
829 };
830 (reference_size, length)
831 },
832 LengthUnit::Rcap => {
833 let reference_size = if context.builder.is_root_element || context.in_media_query {
834 cap_size(context, base_size)
835 } else {
836 context
837 .device()
838 .root_font_metrics_cap()
839 .zoom(context.builder.effective_zoom)
840 };
841 (reference_size, length)
842 },
843 LengthUnit::Ric => {
844 let reference_size = if context.builder.is_root_element || context.in_media_query {
845 ic_size(context, base_size, &reference_font_size)
846 } else {
847 context
848 .device()
849 .root_font_metrics_ic()
850 .zoom(context.builder.effective_zoom)
851 };
852 (reference_size, length)
853 },
854 LengthUnit::Rem => {
855 let reference_size = if context.builder.is_root_element || context.in_media_query {
856 reference_font_size.computed_size()
857 } else {
858 context
859 .device()
860 .root_font_size()
861 .zoom(context.builder.effective_zoom)
862 };
863 (reference_size, length)
864 },
865 LengthUnit::Rlh => {
866 let reference_size = if context.builder.is_root_element {
867 context
868 .builder
869 .calc_line_height(
870 context.device(),
871 line_height_base,
872 context.style().writing_mode,
873 )
874 .0
875 } else if context.in_media_query {
876 context
877 .device()
878 .calc_line_height(
879 context.default_style().get_font(),
880 context.style().writing_mode,
881 None,
882 )
883 .0
884 } else {
885 context.device().root_line_height()
886 };
887 let reference_size = reference_size.zoom(context.builder.effective_zoom);
888 (reference_size, length)
889 },
890 _ => unreachable!("reference_font_size_and_length: not a font-relative unit"),
891 }
892 }
893
894 fn viewport_percentage_to_computed_value(&self, context: &Context) -> CSSPixelLength {
897 let (variant, unit) = match self.unit {
898 LengthUnit::Vw => (ViewportVariant::UADefault, ViewportUnit::Vw),
899 LengthUnit::Svw => (ViewportVariant::Small, ViewportUnit::Vw),
900 LengthUnit::Lvw => (ViewportVariant::Large, ViewportUnit::Vw),
901 LengthUnit::Dvw => (ViewportVariant::Dynamic, ViewportUnit::Vw),
902 LengthUnit::Vh => (ViewportVariant::UADefault, ViewportUnit::Vh),
903 LengthUnit::Svh => (ViewportVariant::Small, ViewportUnit::Vh),
904 LengthUnit::Lvh => (ViewportVariant::Large, ViewportUnit::Vh),
905 LengthUnit::Dvh => (ViewportVariant::Dynamic, ViewportUnit::Vh),
906 LengthUnit::Vmin => (ViewportVariant::UADefault, ViewportUnit::Vmin),
907 LengthUnit::Svmin => (ViewportVariant::Small, ViewportUnit::Vmin),
908 LengthUnit::Lvmin => (ViewportVariant::Large, ViewportUnit::Vmin),
909 LengthUnit::Dvmin => (ViewportVariant::Dynamic, ViewportUnit::Vmin),
910 LengthUnit::Vmax => (ViewportVariant::UADefault, ViewportUnit::Vmax),
911 LengthUnit::Svmax => (ViewportVariant::Small, ViewportUnit::Vmax),
912 LengthUnit::Lvmax => (ViewportVariant::Large, ViewportUnit::Vmax),
913 LengthUnit::Dvmax => (ViewportVariant::Dynamic, ViewportUnit::Vmax),
914 LengthUnit::Vb => (ViewportVariant::UADefault, ViewportUnit::Vb),
915 LengthUnit::Svb => (ViewportVariant::Small, ViewportUnit::Vb),
916 LengthUnit::Lvb => (ViewportVariant::Large, ViewportUnit::Vb),
917 LengthUnit::Dvb => (ViewportVariant::Dynamic, ViewportUnit::Vb),
918 LengthUnit::Vi => (ViewportVariant::UADefault, ViewportUnit::Vi),
919 LengthUnit::Svi => (ViewportVariant::Small, ViewportUnit::Vi),
920 LengthUnit::Lvi => (ViewportVariant::Large, ViewportUnit::Vi),
921 LengthUnit::Dvi => (ViewportVariant::Dynamic, ViewportUnit::Vi),
922 _ => {
923 unreachable!("viewport_percentage_to_computed_value: not a viewport-relative unit")
924 },
925 };
926 let factor = self.value;
927 let size = context.viewport_size_for_viewport_unit_resolution(variant);
928 let length: app_units::Au = match unit {
929 ViewportUnit::Vw => size.width,
930 ViewportUnit::Vh => size.height,
931 ViewportUnit::Vmin => cmp::min(size.width, size.height),
932 ViewportUnit::Vmax => cmp::max(size.width, size.height),
933 ViewportUnit::Vi | ViewportUnit::Vb => {
934 context
935 .builder
936 .add_flags(ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS);
937 context
938 .rule_cache_conditions
939 .borrow_mut()
940 .set_writing_mode_dependency(context.builder.writing_mode);
941 if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
942 size.width
943 } else {
944 size.height
945 }
946 },
947 };
948 let length = context.builder.effective_zoom.zoom(length.0 as f32);
949
950 let trunc_scaled =
951 ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
952 CSSPixelLength::new(crate::values::normalize(trunc_scaled))
953 }
954
955 fn container_relative_to_computed_value(&self, context: &Context) -> CSSPixelLength {
958 if context.for_non_inherited_property {
959 context.rule_cache_conditions.borrow_mut().set_uncacheable();
960 }
961 context
962 .builder
963 .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
964
965 let size = context.get_container_size_query();
966 let factor = self.value;
967 let container_length = match self.unit {
968 LengthUnit::Cqw => size.get_container_width(context),
969 LengthUnit::Cqh => size.get_container_height(context),
970 LengthUnit::Cqi => size.get_container_inline_size(context),
971 LengthUnit::Cqb => size.get_container_block_size(context),
972 LengthUnit::Cqmin => cmp::min(
973 size.get_container_inline_size(context),
974 size.get_container_block_size(context),
975 ),
976 LengthUnit::Cqmax => cmp::max(
977 size.get_container_inline_size(context),
978 size.get_container_block_size(context),
979 ),
980 _ => {
981 unreachable!("container_relative_to_computed_value: not a container-relative unit")
982 },
983 };
984 CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
985 }
986
987 fn servo_character_width_to_computed_value(
989 &self,
990 reference_font_size: computed::Length,
991 ) -> computed::Length {
992 debug_assert_eq!(self.unit, LengthUnit::ServoCharacterWidth);
993 let cols = self.value as i32 as CSSFloat;
994 let average_advance = reference_font_size * 0.5;
997 let max_advance = reference_font_size;
998 (average_advance * (cols - 1.0) + max_advance).finite()
999 }
1000
1001 pub fn to_computed_value_with_base_size(
1003 &self,
1004 context: &Context,
1005 base_size: FontBaseSize,
1006 line_height_base: LineHeightBase,
1007 ) -> CSSPixelLength {
1008 if let Some(px) = self.to_px_if_absolute() {
1009 return CSSPixelLength::new(px)
1010 .zoom(context.builder.effective_zoom)
1011 .finite();
1012 }
1013 let unit = self.length_unit();
1014 if unit.is_font_relative() {
1015 return self.font_relative_to_computed_value(context, base_size, line_height_base);
1016 }
1017 if unit.is_viewport_percentage() {
1018 return self.viewport_percentage_to_computed_value(context);
1019 }
1020 if unit.is_container_relative() {
1021 return self.container_relative_to_computed_value(context);
1022 }
1023 debug_assert_eq!(unit, LengthUnit::ServoCharacterWidth);
1024 self.servo_character_width_to_computed_value(
1025 context
1026 .style()
1027 .get_font()
1028 .slow_clone_font_size()
1029 .computed_size(),
1030 )
1031 }
1032}
1033
1034impl ToComputedValue for NoCalcLength {
1035 type ComputedValue = computed::Length;
1036
1037 #[inline]
1038 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1039 self.to_computed_value_with_base_size(
1040 context,
1041 FontBaseSize::CurrentStyle,
1042 LineHeightBase::CurrentStyle,
1043 )
1044 }
1045
1046 #[inline]
1047 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1048 Self::from_px(computed.px())
1049 }
1050}
1051
1052impl ToCss for NoCalcLength {
1053 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1054 where
1055 W: Write,
1056 {
1057 crate::values::serialize_specified_dimension(
1058 self.unitless_value(),
1059 self.unit(),
1060 false,
1061 dest,
1062 )
1063 }
1064}
1065
1066impl ToTyped for NoCalcLength {
1067 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1068 let numeric_type = NumericType::length();
1069 let value = self.unitless_value();
1070 let unit = CssString::from(self.unit());
1071 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
1072 numeric_type,
1073 value,
1074 unit,
1075 })));
1076 Ok(())
1077 }
1078}
1079
1080impl SpecifiedValueInfo for NoCalcLength {}
1081
1082impl PartialOrd for NoCalcLength {
1083 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1084 if let (Some(a), Some(b)) = (self.to_px_if_absolute(), other.to_px_if_absolute()) {
1086 return a.partial_cmp(&b);
1087 }
1088 if self.unit != other.unit {
1089 return None;
1090 }
1091 self.value.partial_cmp(&other.value)
1092 }
1093}
1094
1095impl Zero for NoCalcLength {
1096 fn zero() -> Self {
1097 Self::from_px(0.)
1098 }
1099
1100 fn is_zero(&self) -> bool {
1101 NoCalcLength::is_zero(self)
1102 }
1103}
1104
1105#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
1112pub struct Length(NumericUnion<LengthUnit, f32, CalcLengthPercentage>);
1113
1114impl ToCss for Length {
1115 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1116 where
1117 W: Write,
1118 {
1119 match self.0.unpack() {
1120 Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value).to_css(dest),
1121 Unpacked::Boxed(calc) => calc.to_css(dest),
1122 }
1123 }
1124}
1125
1126impl ToTyped for Length {
1127 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1128 match self.0.unpack() {
1129 Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value).to_typed(dest),
1130 Unpacked::Boxed(calc) => calc.to_typed(dest),
1131 }
1132 }
1133}
1134
1135impl SpecifiedValueInfo for Length {}
1136
1137impl From<NoCalcLength> for Length {
1138 #[inline]
1139 fn from(len: NoCalcLength) -> Self {
1140 Self::new(len)
1141 }
1142}
1143
1144impl Length {
1145 #[inline]
1147 pub fn new(len: NoCalcLength) -> Self {
1148 Self(NumericUnion::inline(len.unit, len.value))
1149 }
1150
1151 #[inline]
1153 pub fn new_calc(calc: Box<CalcLengthPercentage>) -> Self {
1154 Self(NumericUnion::boxed(calc))
1155 }
1156
1157 #[inline]
1159 pub fn is_calc(&self) -> bool {
1160 self.0.is_boxed()
1161 }
1162
1163 #[inline]
1164 fn parse_internal(
1165 context: &ParserContext,
1166 input: &mut Parser,
1167 num_context: AllowedNumericType,
1168 allow_quirks: AllowQuirks,
1169 ) -> Result<Self, ParseError> {
1170 let token = input.next()?;
1171 match *token {
1172 Token::Dimension {
1173 value, ref unit, ..
1174 } if num_context.is_ok(context.parsing_mode, value) => {
1175 NoCalcLength::parse_dimension_with_context(context, value, unit)
1176 .map(Self::new)
1177 .map_err(|()| ParseError::unexpected_token())
1178 },
1179 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1180 let allowed = context.parsing_mode.allows_unitless_lengths()
1181 || allow_quirks.allowed(context.quirks_mode)
1182 || (value == 0. && context.parsing_mode.allows_unitless_zero_lengths());
1183
1184 if !allowed {
1185 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1186 }
1187
1188 Ok(Self::new(NoCalcLength::from_px(value)))
1189 },
1190 Token::Function(ref name) => {
1191 let function = CalcNode::math_function(context, name)?;
1192 let calc = CalcNode::parse_length(
1193 context,
1194 input,
1195 num_context,
1196 function,
1197 PercentageContext::not_allowed(),
1198 )?;
1199 Ok(Self::new_calc(Box::new(calc)))
1200 },
1201 _ => Err(ParseError::unexpected_token()),
1202 }
1203 }
1204
1205 #[inline]
1207 pub fn parse_non_negative(
1208 context: &ParserContext,
1209 input: &mut Parser,
1210 ) -> Result<Self, ParseError> {
1211 Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1212 }
1213
1214 #[inline]
1216 pub fn parse_non_negative_quirky(
1217 context: &ParserContext,
1218 input: &mut Parser,
1219 allow_quirks: AllowQuirks,
1220 ) -> Result<Self, ParseError> {
1221 Self::parse_internal(
1222 context,
1223 input,
1224 AllowedNumericType::NonNegative,
1225 allow_quirks,
1226 )
1227 }
1228
1229 #[inline]
1231 pub fn from_px(px_value: CSSFloat) -> Length {
1232 Self::new(NoCalcLength::from_px(px_value))
1233 }
1234
1235 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1237 match self.0.unpack() {
1238 Unpacked::Inline(unit, value) => {
1239 NoCalcLength::new(unit, value).to_computed_pixel_length_without_context()
1240 },
1241 Unpacked::Boxed(calc) => calc.to_computed_pixel_length_without_context(),
1242 }
1243 }
1244
1245 #[cfg(feature = "gecko")]
1247 pub fn to_computed_pixel_length_with_font_metrics(
1248 &self,
1249 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1250 ) -> Result<CSSFloat, ()> {
1251 match self.0.unpack() {
1252 Unpacked::Inline(unit, value) => NoCalcLength::new(unit, value)
1253 .to_computed_pixel_length_with_font_metrics(get_font_metrics),
1254 Unpacked::Boxed(calc) => {
1255 calc.to_computed_pixel_length_with_font_metrics(get_font_metrics)
1256 },
1257 }
1258 }
1259}
1260
1261impl Parse for Length {
1262 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1263 Self::parse_quirky(context, input, AllowQuirks::No)
1264 }
1265}
1266
1267impl ToComputedValue for Length {
1268 type ComputedValue = computed::Length;
1269
1270 #[inline]
1271 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1272 match self.0.unpack() {
1273 Unpacked::Inline(unit, value) => {
1274 NoCalcLength::new(unit, value).to_computed_value(context)
1275 },
1276 Unpacked::Boxed(calc) => {
1277 let result = calc.to_computed_value(context);
1278 debug_assert!(
1279 result.to_length().is_some(),
1280 "{:?} didn't resolve to a length: {:?}",
1281 calc,
1282 result,
1283 );
1284 result.to_length().unwrap_or_else(computed::Length::zero)
1285 },
1286 }
1287 }
1288
1289 #[inline]
1290 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1291 Self::new(NoCalcLength::from_computed_value(computed))
1292 }
1293}
1294
1295impl Zero for Length {
1296 fn zero() -> Self {
1297 Self::new(NoCalcLength::zero())
1298 }
1299
1300 fn is_zero(&self) -> bool {
1301 match self.0.unpack() {
1304 Unpacked::Inline(_, value) => value == 0.0,
1305 Unpacked::Boxed(_) => false,
1306 }
1307 }
1308}
1309
1310impl Length {
1311 pub fn parse_quirky(
1313 context: &ParserContext,
1314 input: &mut Parser,
1315 allow_quirks: AllowQuirks,
1316 ) -> Result<Self, ParseError> {
1317 Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1318 }
1319}
1320
1321pub type NonNegativeLength = NonNegative<Length>;
1323
1324impl Parse for NonNegativeLength {
1325 #[inline]
1326 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1327 Ok(NonNegative(Length::parse_non_negative(context, input)?))
1328 }
1329}
1330
1331impl From<NoCalcLength> for NonNegativeLength {
1332 #[inline]
1333 fn from(len: NoCalcLength) -> Self {
1334 NonNegative(Length::new(len))
1335 }
1336}
1337
1338impl From<Length> for NonNegativeLength {
1339 #[inline]
1340 fn from(len: Length) -> Self {
1341 NonNegative(len)
1342 }
1343}
1344
1345impl NonNegativeLength {
1346 #[inline]
1348 pub fn from_px(px_value: CSSFloat) -> Self {
1349 Length::from_px(px_value.max(0.)).into()
1350 }
1351
1352 #[inline]
1354 pub fn parse_quirky(
1355 context: &ParserContext,
1356 input: &mut Parser,
1357 allow_quirks: AllowQuirks,
1358 ) -> Result<Self, ParseError> {
1359 Ok(NonNegative(Length::parse_non_negative_quirky(
1360 context,
1361 input,
1362 allow_quirks,
1363 )?))
1364 }
1365}
1366
1367#[allow(missing_docs)]
1372#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1373pub enum LengthPercentage {
1374 Length(NoCalcLength),
1375 Percentage(NoCalcPercentage),
1376 Calc(Box<CalcLengthPercentage>),
1377}
1378
1379impl From<Length> for LengthPercentage {
1380 fn from(len: Length) -> LengthPercentage {
1381 match len.0.extract() {
1382 Extracted::Inline(unit, value) => {
1383 LengthPercentage::Length(NoCalcLength::new(unit, value))
1384 },
1385 Extracted::Boxed(calc) => LengthPercentage::Calc(calc),
1386 }
1387 }
1388}
1389
1390impl From<NoCalcLength> for LengthPercentage {
1391 #[inline]
1392 fn from(len: NoCalcLength) -> Self {
1393 LengthPercentage::Length(len)
1394 }
1395}
1396
1397impl From<computed::Percentage> for LengthPercentage {
1398 #[inline]
1399 fn from(pc: computed::Percentage) -> Self {
1400 LengthPercentage::Percentage(NoCalcPercentage::new(pc.0))
1401 }
1402}
1403
1404impl Parse for LengthPercentage {
1405 #[inline]
1406 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1407 Self::parse_quirky(context, input, AllowQuirks::No)
1408 }
1409}
1410
1411impl LengthPercentage {
1412 #[inline]
1413 pub fn zero_percent() -> LengthPercentage {
1415 LengthPercentage::Percentage(NoCalcPercentage::zero())
1416 }
1417
1418 #[inline]
1419 pub fn hundred_percent() -> LengthPercentage {
1421 LengthPercentage::Percentage(NoCalcPercentage::hundred())
1422 }
1423
1424 fn parse_internal(
1425 context: &ParserContext,
1426 input: &mut Parser,
1427 num_context: AllowedNumericType,
1428 allow_quirks: AllowQuirks,
1429 allow_anchor: AllowAnchorPositioningFunctions,
1430 ) -> Result<Self, ParseError> {
1431 let token = input.next()?;
1432 match *token {
1433 Token::Dimension {
1434 value, ref unit, ..
1435 } if num_context.is_ok(context.parsing_mode, value) => {
1436 NoCalcLength::parse_dimension_with_context(context, value, unit)
1437 .map(LengthPercentage::Length)
1438 .map_err(|()| ParseError::unexpected_token())
1439 },
1440 Token::Percentage { unit_value, .. }
1441 if num_context.is_ok(context.parsing_mode, unit_value) =>
1442 {
1443 Ok(LengthPercentage::Percentage(NoCalcPercentage::new(
1444 unit_value,
1445 )))
1446 },
1447 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1448 let allowed = context.parsing_mode.allows_unitless_lengths()
1449 || allow_quirks.allowed(context.quirks_mode)
1450 || (value == 0. && context.parsing_mode.allows_unitless_zero_lengths());
1451
1452 if !allowed {
1453 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1454 }
1455
1456 Ok(LengthPercentage::Length(NoCalcLength::from_px(value)))
1457 },
1458 Token::Function(ref name) => {
1459 let function = CalcNode::math_function(context, name)?;
1460 let calc = CalcNode::parse_length_or_percentage(
1461 context,
1462 input,
1463 num_context,
1464 function,
1465 allow_anchor,
1466 )?;
1467 Ok(LengthPercentage::Calc(Box::new(calc)))
1468 },
1469 _ => Err(ParseError::unexpected_token()),
1470 }
1471 }
1472
1473 #[inline]
1476 pub fn parse_quirky(
1477 context: &ParserContext,
1478 input: &mut Parser,
1479 allow_quirks: AllowQuirks,
1480 ) -> Result<Self, ParseError> {
1481 Self::parse_internal(
1482 context,
1483 input,
1484 AllowedNumericType::All,
1485 allow_quirks,
1486 AllowAnchorPositioningFunctions::No,
1487 )
1488 }
1489
1490 #[inline]
1493 fn parse_quirky_with_anchor_size_function(
1494 context: &ParserContext,
1495 input: &mut Parser,
1496 allow_quirks: AllowQuirks,
1497 ) -> Result<Self, ParseError> {
1498 Self::parse_internal(
1499 context,
1500 input,
1501 AllowedNumericType::All,
1502 allow_quirks,
1503 AllowAnchorPositioningFunctions::AllowAnchorSize,
1504 )
1505 }
1506
1507 #[inline]
1510 pub fn parse_quirky_with_anchor_functions(
1511 context: &ParserContext,
1512 input: &mut Parser,
1513 allow_quirks: AllowQuirks,
1514 ) -> Result<Self, ParseError> {
1515 Self::parse_internal(
1516 context,
1517 input,
1518 AllowedNumericType::All,
1519 allow_quirks,
1520 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1521 )
1522 }
1523
1524 pub fn parse_non_negative_with_anchor_size(
1527 context: &ParserContext,
1528 input: &mut Parser,
1529 allow_quirks: AllowQuirks,
1530 ) -> Result<Self, ParseError> {
1531 Self::parse_internal(
1532 context,
1533 input,
1534 AllowedNumericType::NonNegative,
1535 allow_quirks,
1536 AllowAnchorPositioningFunctions::AllowAnchorSize,
1537 )
1538 }
1539
1540 #[inline]
1545 pub fn parse_non_negative(
1546 context: &ParserContext,
1547 input: &mut Parser,
1548 ) -> Result<Self, ParseError> {
1549 Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1550 }
1551
1552 #[inline]
1554 pub fn parse_non_negative_quirky(
1555 context: &ParserContext,
1556 input: &mut Parser,
1557 allow_quirks: AllowQuirks,
1558 ) -> Result<Self, ParseError> {
1559 Self::parse_internal(
1560 context,
1561 input,
1562 AllowedNumericType::NonNegative,
1563 allow_quirks,
1564 AllowAnchorPositioningFunctions::No,
1565 )
1566 }
1567
1568 pub fn compute_without_context(&self) -> Option<computed::LengthPercentage> {
1573 use crate::values::normalize;
1574 match self {
1575 Self::Length(length) => length
1576 .to_computed_pixel_length_without_context()
1577 .map(|v| computed::LengthPercentage::new_length(computed::Length::new(v)))
1578 .ok(),
1579 Self::Percentage(pc) => Some(computed::LengthPercentage::new_percent(
1580 computed::Percentage(normalize(pc.get())),
1581 )),
1582 Self::Calc(calc) => calc.compute_without_context(),
1583 }
1584 }
1585}
1586
1587impl Zero for LengthPercentage {
1588 fn zero() -> Self {
1589 LengthPercentage::Length(NoCalcLength::zero())
1590 }
1591
1592 fn is_zero(&self) -> bool {
1593 match *self {
1594 LengthPercentage::Length(l) => l.is_zero(),
1595 LengthPercentage::Percentage(p) => p.get() == 0.0,
1596 LengthPercentage::Calc(_) => false,
1597 }
1598 }
1599}
1600
1601impl ZeroNoPercent for LengthPercentage {
1602 fn is_zero_no_percent(&self) -> bool {
1603 match *self {
1604 LengthPercentage::Percentage(_) => false,
1605 _ => self.is_zero(),
1606 }
1607 }
1608}
1609
1610pub trait EqualsPercentage {
1612 fn equals_percentage(&self, v: CSSFloat) -> bool;
1615}
1616
1617impl EqualsPercentage for LengthPercentage {
1618 fn equals_percentage(&self, v: CSSFloat) -> bool {
1619 match *self {
1620 LengthPercentage::Percentage(p) => p.get() == v,
1621 _ => false,
1622 }
1623 }
1624}
1625
1626pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
1628
1629impl LengthPercentageOrAuto {
1630 #[inline]
1632 pub fn zero_percent() -> Self {
1633 generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
1634 }
1635
1636 #[inline]
1639 pub fn parse_quirky(
1640 context: &ParserContext,
1641 input: &mut Parser,
1642 allow_quirks: AllowQuirks,
1643 ) -> Result<Self, ParseError> {
1644 Self::parse_with(context, input, |context, input| {
1645 LengthPercentage::parse_quirky(context, input, allow_quirks)
1646 })
1647 }
1648}
1649
1650pub type NonNegativeLengthPercentageOrAuto =
1652 generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
1653
1654impl NonNegativeLengthPercentageOrAuto {
1655 #[inline]
1657 pub fn zero_percent() -> Self {
1658 generics::LengthPercentageOrAuto::LengthPercentage(
1659 NonNegativeLengthPercentage::zero_percent(),
1660 )
1661 }
1662
1663 #[inline]
1666 pub fn parse_quirky(
1667 context: &ParserContext,
1668 input: &mut Parser,
1669 allow_quirks: AllowQuirks,
1670 ) -> Result<Self, ParseError> {
1671 Self::parse_with(context, input, |context, input| {
1672 NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
1673 })
1674 }
1675}
1676
1677pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
1679
1680pub type NonNegativeLengthPercentageOrNormal =
1682 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
1683
1684impl From<NoCalcLength> for NonNegativeLengthPercentage {
1685 #[inline]
1686 fn from(len: NoCalcLength) -> Self {
1687 NonNegative(LengthPercentage::from(len))
1688 }
1689}
1690
1691impl Parse for NonNegativeLengthPercentage {
1692 #[inline]
1693 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1694 Self::parse_quirky(context, input, AllowQuirks::No)
1695 }
1696}
1697
1698impl NonNegativeLengthPercentage {
1699 #[inline]
1700 pub fn zero_percent() -> Self {
1702 NonNegative(LengthPercentage::zero_percent())
1703 }
1704
1705 #[inline]
1708 pub fn parse_quirky(
1709 context: &ParserContext,
1710 input: &mut Parser,
1711 allow_quirks: AllowQuirks,
1712 ) -> Result<Self, ParseError> {
1713 LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
1714 }
1715
1716 #[inline]
1719 pub fn parse_non_negative_with_anchor_size(
1720 context: &ParserContext,
1721 input: &mut Parser,
1722 allow_quirks: AllowQuirks,
1723 ) -> Result<Self, ParseError> {
1724 LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
1725 .map(NonNegative)
1726 }
1727}
1728
1729pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
1735
1736impl LengthOrAuto {
1737 #[inline]
1740 pub fn parse_quirky(
1741 context: &ParserContext,
1742 input: &mut Parser,
1743 allow_quirks: AllowQuirks,
1744 ) -> Result<Self, ParseError> {
1745 Self::parse_with(context, input, |context, input| {
1746 Length::parse_quirky(context, input, allow_quirks)
1747 })
1748 }
1749}
1750
1751pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
1753
1754pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
1756
1757pub type Size = GenericSize<NonNegativeLengthPercentage>;
1759
1760impl Parse for Size {
1761 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1762 Size::parse_quirky(context, input, AllowQuirks::No)
1763 }
1764}
1765
1766macro_rules! parse_size_non_length {
1767 ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
1768 $auto_or_none:expr => $auto_or_none_ident:ident) => {{
1769 let size = $input.try_parse(|input| {
1770 Ok(try_match_ident_ignore_ascii_case! { input,
1771 "min-content" | "-moz-min-content" => $size::MinContent,
1772 "max-content" | "-moz-max-content" => $size::MaxContent,
1773 "fit-content" | "-moz-fit-content" => $size::FitContent,
1774 #[cfg(feature = "gecko")]
1775 "-moz-available" => $size::MozAvailable,
1776 "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
1777 "stretch" if is_stretch_enabled() => $size::Stretch,
1778 $auto_or_none => $size::$auto_or_none_ident,
1779 })
1780 });
1781 if size.is_ok() {
1782 return size;
1783 }
1784 }};
1785}
1786
1787fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
1788 crate::pref!("layout.css.webkit-fill-available.enabled")
1789}
1790
1791fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
1792 crate::pref!("layout.css.webkit-fill-available.enabled")
1796 && crate::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
1797}
1798
1799fn is_stretch_enabled() -> bool {
1800 crate::pref!("layout.css.stretch-size-keyword.enabled")
1801}
1802
1803fn is_fit_content_function_enabled() -> bool {
1804 crate::pref!("layout.css.fit-content-function.enabled")
1805}
1806
1807macro_rules! parse_fit_content_function {
1808 ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
1809 if is_fit_content_function_enabled() {
1810 if let Ok(length) = $input.try_parse(|input| {
1811 input.expect_function_matching("fit-content")?;
1812 input.parse_nested_block(|i| {
1813 NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
1814 })
1815 }) {
1816 return Ok($size::FitContentFunction(length));
1817 }
1818 }
1819 };
1820}
1821
1822#[derive(Clone, Copy, PartialEq, Eq)]
1823enum ParseAnchorFunctions {
1824 Yes,
1825 No,
1826}
1827
1828impl Size {
1829 pub fn parse_quirky(
1831 context: &ParserContext,
1832 input: &mut Parser,
1833 allow_quirks: AllowQuirks,
1834 ) -> Result<Self, ParseError> {
1835 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
1836 Self::parse_quirky_internal(
1837 context,
1838 input,
1839 allow_quirks,
1840 allow_webkit_fill_available,
1841 ParseAnchorFunctions::Yes,
1842 )
1843 }
1844
1845 pub fn parse_size_for_flex_basis_width(
1847 context: &ParserContext,
1848 input: &mut Parser,
1849 ) -> Result<Self, ParseError> {
1850 Self::parse_quirky_internal(
1851 context,
1852 input,
1853 AllowQuirks::No,
1854 true,
1855 ParseAnchorFunctions::No,
1856 )
1857 }
1858
1859 fn parse_quirky_internal(
1864 context: &ParserContext,
1865 input: &mut Parser,
1866 allow_quirks: AllowQuirks,
1867 allow_webkit_fill_available: bool,
1868 allow_anchor_functions: ParseAnchorFunctions,
1869 ) -> Result<Self, ParseError> {
1870 parse_size_non_length!(Size, input, allow_webkit_fill_available,
1871 "auto" => Auto);
1872 parse_fit_content_function!(Size, input, context, allow_quirks);
1873
1874 let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
1875 && crate::pref!("layout.css.anchor-positioning.enabled", gecko = true);
1876 match input
1877 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
1878 {
1879 Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
1880 Err(e) if !allow_anchor => return Err(e.into()),
1881 Err(_) => (),
1882 };
1883 if let Ok(length) = input.try_parse(|i| {
1884 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
1885 context,
1886 i,
1887 allow_quirks,
1888 )
1889 }) {
1890 return Ok(GenericSize::AnchorContainingCalcFunction(length));
1891 }
1892 Ok(Self::AnchorSizeFunction(Box::new(
1893 GenericAnchorSizeFunction::parse(context, input)?,
1894 )))
1895 }
1896
1897 pub fn parse_size_for_width_or_height_quirky(
1903 context: &ParserContext,
1904 input: &mut Parser,
1905 allow_quirks: AllowQuirks,
1906 ) -> Result<Self, ParseError> {
1907 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
1908 Self::parse_quirky_internal(
1909 context,
1910 input,
1911 allow_quirks,
1912 allow_webkit_fill_available,
1913 ParseAnchorFunctions::Yes,
1914 )
1915 }
1916
1917 pub fn parse_size_for_width_or_height(
1923 context: &ParserContext,
1924 input: &mut Parser,
1925 ) -> Result<Self, ParseError> {
1926 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
1927 Self::parse_quirky_internal(
1928 context,
1929 input,
1930 AllowQuirks::No,
1931 allow_webkit_fill_available,
1932 ParseAnchorFunctions::Yes,
1933 )
1934 }
1935
1936 #[inline]
1938 pub fn zero_percent() -> Self {
1939 GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
1940 }
1941}
1942
1943pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
1945
1946impl Parse for MaxSize {
1947 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1948 MaxSize::parse_quirky(context, input, AllowQuirks::No)
1949 }
1950}
1951
1952impl MaxSize {
1953 pub fn parse_quirky(
1955 context: &ParserContext,
1956 input: &mut Parser,
1957 allow_quirks: AllowQuirks,
1958 ) -> Result<Self, ParseError> {
1959 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
1960 parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
1961 "none" => None);
1962 parse_fit_content_function!(MaxSize, input, context, allow_quirks);
1963
1964 match input
1965 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
1966 {
1967 Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
1968 Err(e) if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => {
1969 return Err(e.into())
1970 },
1971 Err(_) => (),
1972 };
1973 if let Ok(length) = input.try_parse(|i| {
1974 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
1975 context,
1976 i,
1977 allow_quirks,
1978 )
1979 }) {
1980 return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
1981 }
1982 Ok(Self::AnchorSizeFunction(Box::new(
1983 GenericAnchorSizeFunction::parse(context, input)?,
1984 )))
1985 }
1986}
1987
1988pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
1990
1991pub type Margin = GenericMargin<LengthPercentage>;
1993
1994impl Margin {
1995 #[inline]
1998 pub fn parse_quirky(
1999 context: &ParserContext,
2000 input: &mut Parser,
2001 allow_quirks: AllowQuirks,
2002 ) -> Result<Self, ParseError> {
2003 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2004 {
2005 return Ok(Self::LengthPercentage(l));
2006 }
2007 match input.try_parse(|i| i.expect_ident_matching("auto")) {
2008 Ok(_) => return Ok(Self::Auto),
2009 Err(e) if !crate::pref!("layout.css.anchor-positioning.enabled", gecko = true) => {
2010 return Err(e.into())
2011 },
2012 Err(_) => (),
2013 };
2014 if let Ok(l) = input.try_parse(|i| {
2015 LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2016 }) {
2017 return Ok(Self::AnchorContainingCalcFunction(l));
2018 }
2019 let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
2020 Ok(Self::AnchorSizeFunction(Box::new(inner)))
2021 }
2022}
2023
2024impl Parse for Margin {
2025 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
2026 Self::parse_quirky(context, input, AllowQuirks::No)
2027 }
2028}