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