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