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