1use super::{AllowQuirks, Number, Percentage, 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::values::computed::{self, CSSPixelLength, Context, FontSize};
17use crate::values::generics::length as generics;
18use crate::values::generics::length::{
19 GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,
20 GenericMargin, GenericMaxSize, GenericSize,
21};
22use crate::values::generics::NonNegative;
23use crate::values::specified::calc::{self, AllowAnchorPositioningFunctions, CalcNode};
24use crate::values::specified::font::QueryFontMetricsFlags;
25use crate::values::specified::NonNegativeNumber;
26use crate::values::CSSFloat;
27use crate::{Zero, ZeroNoPercent};
28use app_units::AU_PER_PX;
29use cssparser::{match_ignore_ascii_case, Parser, Token};
30use debug_unreachable::debug_unreachable;
31use std::cmp;
32use std::fmt::{self, Write};
33use style_traits::values::specified::AllowedNumericType;
34use style_traits::{
35 CssString, CssWriter, NumericValue, ParseError, ParsingMode, SpecifiedValueInfo,
36 StyleParseErrorKind, ToCss, ToTyped, TypedValue, UnitValue,
37};
38use thin_vec::ThinVec;
39
40pub use super::image::Image;
41pub use super::image::{EndingShape as GradientEndingShape, Gradient};
42pub use crate::values::specified::calc::CalcLengthPercentage;
43
44pub const PX_PER_IN: CSSFloat = 96.;
46pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;
48pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;
50pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;
52pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;
54pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;
56
57#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
61#[repr(u8)]
62pub enum FontRelativeLength {
63 #[css(dimension)]
65 Em(CSSFloat),
66 #[css(dimension)]
68 Ex(CSSFloat),
69 #[css(dimension)]
71 Rex(CSSFloat),
72 #[css(dimension)]
74 Ch(CSSFloat),
75 #[css(dimension)]
77 Rch(CSSFloat),
78 #[css(dimension)]
80 Cap(CSSFloat),
81 #[css(dimension)]
83 Rcap(CSSFloat),
84 #[css(dimension)]
86 Ic(CSSFloat),
87 #[css(dimension)]
89 Ric(CSSFloat),
90 #[css(dimension)]
92 Rem(CSSFloat),
93 #[css(dimension)]
95 Lh(CSSFloat),
96 #[css(dimension)]
98 Rlh(CSSFloat),
99}
100
101#[derive(Clone, Copy, Debug, PartialEq)]
103pub enum FontBaseSize {
104 CurrentStyle,
106 InheritedStyle,
108}
109
110#[derive(Clone, Copy, Debug, PartialEq)]
112pub enum LineHeightBase {
113 CurrentStyle,
115 InheritedStyle,
117}
118
119impl FontBaseSize {
120 pub fn resolve(&self, context: &Context) -> computed::FontSize {
122 let style = context.style();
123 match *self {
124 Self::CurrentStyle => style.get_font().clone_font_size(),
125 Self::InheritedStyle => {
126 let zoom = style.effective_zoom_for_inheritance;
129 style.get_parent_font().clone_font_size().zoom(zoom)
130 },
131 }
132 }
133}
134
135impl FontRelativeLength {
136 pub const EM: &'static str = "em";
138 pub const EX: &'static str = "ex";
140 pub const REX: &'static str = "rex";
142 pub const CH: &'static str = "ch";
144 pub const RCH: &'static str = "rch";
146 pub const CAP: &'static str = "cap";
148 pub const RCAP: &'static str = "rcap";
150 pub const IC: &'static str = "ic";
152 pub const RIC: &'static str = "ric";
154 pub const REM: &'static str = "rem";
156 pub const LH: &'static str = "lh";
158 pub const RLH: &'static str = "rlh";
160
161 fn unitless_value(&self) -> CSSFloat {
163 match *self {
164 Self::Em(v)
165 | Self::Ex(v)
166 | Self::Rex(v)
167 | Self::Ch(v)
168 | Self::Rch(v)
169 | Self::Cap(v)
170 | Self::Rcap(v)
171 | Self::Ic(v)
172 | Self::Ric(v)
173 | Self::Rem(v)
174 | Self::Lh(v)
175 | Self::Rlh(v) => v,
176 }
177 }
178
179 fn unit(&self) -> &'static str {
181 match *self {
182 Self::Em(_) => Self::EM,
183 Self::Ex(_) => Self::EX,
184 Self::Rex(_) => Self::REX,
185 Self::Ch(_) => Self::CH,
186 Self::Rch(_) => Self::RCH,
187 Self::Cap(_) => Self::CAP,
188 Self::Rcap(_) => Self::RCAP,
189 Self::Ic(_) => Self::IC,
190 Self::Ric(_) => Self::RIC,
191 Self::Rem(_) => Self::REM,
192 Self::Lh(_) => Self::LH,
193 Self::Rlh(_) => Self::RLH,
194 }
195 }
196
197 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
198 where
199 O: Fn(f32, f32) -> f32,
200 {
201 use self::FontRelativeLength::*;
202
203 if std::mem::discriminant(self) != std::mem::discriminant(other) {
204 return Err(());
205 }
206
207 Ok(match (self, other) {
208 (&Em(one), &Em(other)) => Em(op(one, other)),
209 (&Ex(one), &Ex(other)) => Ex(op(one, other)),
210 (&Rex(one), &Rex(other)) => Rex(op(one, other)),
211 (&Ch(one), &Ch(other)) => Ch(op(one, other)),
212 (&Rch(one), &Rch(other)) => Rch(op(one, other)),
213 (&Cap(one), &Cap(other)) => Cap(op(one, other)),
214 (&Rcap(one), &Rcap(other)) => Rcap(op(one, other)),
215 (&Ic(one), &Ic(other)) => Ic(op(one, other)),
216 (&Ric(one), &Ric(other)) => Ric(op(one, other)),
217 (&Rem(one), &Rem(other)) => Rem(op(one, other)),
218 (&Lh(one), &Lh(other)) => Lh(op(one, other)),
219 (&Rlh(one), &Rlh(other)) => Rlh(op(one, other)),
220 _ => unsafe {
223 match *self {
224 Em(..) | Rem(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..)
225 | Ic(..) | Ric(..) | Lh(..) | Rlh(..) => {},
226 }
227 debug_unreachable!("Forgot to handle unit in try_op()")
228 },
229 })
230 }
231
232 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
233 match self {
234 Self::Em(x) => Self::Em(op(*x)),
235 Self::Ex(x) => Self::Ex(op(*x)),
236 Self::Rex(x) => Self::Rex(op(*x)),
237 Self::Ch(x) => Self::Ch(op(*x)),
238 Self::Rch(x) => Self::Rch(op(*x)),
239 Self::Cap(x) => Self::Cap(op(*x)),
240 Self::Rcap(x) => Self::Rcap(op(*x)),
241 Self::Ic(x) => Self::Ic(op(*x)),
242 Self::Ric(x) => Self::Ric(op(*x)),
243 Self::Rem(x) => Self::Rem(op(*x)),
244 Self::Lh(x) => Self::Lh(op(*x)),
245 Self::Rlh(x) => Self::Rlh(op(*x)),
246 }
247 }
248
249 pub fn to_computed_value(
251 &self,
252 context: &Context,
253 base_size: FontBaseSize,
254 line_height_base: LineHeightBase,
255 ) -> computed::Length {
256 let (reference_size, length) =
257 self.reference_font_size_and_length(context, base_size, line_height_base);
258 (reference_size * length).finite()
259 }
260
261 #[cfg(feature = "gecko")]
263 pub fn to_computed_pixel_length_with_font_metrics(
264 &self,
265 get_font_metrics: impl Fn() -> GeckoFontMetrics,
266 ) -> Result<CSSFloat, ()> {
267 let metrics = get_font_metrics();
268 Ok(match *self {
269 Self::Em(v) => v * metrics.mComputedEmSize.px(),
270 Self::Ex(v) => v * metrics.mXSize.px(),
271 Self::Ch(v) => v * metrics.mChSize.px(),
272 Self::Cap(v) => v * metrics.mCapHeight.px(),
273 Self::Ic(v) => v * metrics.mIcWidth.px(),
274 Self::Lh(_)
277 | Self::Rlh(_)
278 | Self::Rem(_)
279 | Self::Rex(_)
280 | Self::Rch(_)
281 | Self::Rcap(_)
282 | Self::Ric(_) => return Err(()),
283 })
284 }
285
286 fn reference_font_size_and_length(
294 &self,
295 context: &Context,
296 base_size: FontBaseSize,
297 line_height_base: LineHeightBase,
298 ) -> (computed::Length, CSSFloat) {
299 fn query_font_metrics(
300 context: &Context,
301 base_size: FontBaseSize,
302 orientation: FontMetricsOrientation,
303 flags: QueryFontMetricsFlags,
304 ) -> FontMetrics {
305 context.query_font_metrics(base_size, orientation, flags)
306 }
307
308 fn ex_size(
309 context: &Context,
310 base_size: FontBaseSize,
311 reference_font_size: &FontSize,
312 ) -> computed::Length {
313 let metrics = query_font_metrics(
315 context,
316 base_size,
317 FontMetricsOrientation::Horizontal,
318 QueryFontMetricsFlags::empty(),
319 );
320 metrics.x_height_or_default(reference_font_size.used_size())
321 }
322
323 fn ch_size(
324 context: &Context,
325 base_size: FontBaseSize,
326 reference_font_size: &FontSize,
327 ) -> computed::Length {
328 let metrics = query_font_metrics(
336 context,
337 base_size,
338 FontMetricsOrientation::MatchContextPreferHorizontal,
339 QueryFontMetricsFlags::NEEDS_CH,
340 );
341 metrics.zero_advance_measure_or_default(
342 reference_font_size.used_size(),
343 context.style().writing_mode.is_upright(),
344 )
345 }
346
347 fn cap_size(context: &Context, base_size: FontBaseSize) -> computed::Length {
348 let metrics = query_font_metrics(
349 context,
350 base_size,
351 FontMetricsOrientation::Horizontal,
352 QueryFontMetricsFlags::empty(),
353 );
354 metrics.cap_height_or_default()
355 }
356
357 fn ic_size(
358 context: &Context,
359 base_size: FontBaseSize,
360 reference_font_size: &FontSize,
361 ) -> computed::Length {
362 let metrics = query_font_metrics(
363 context,
364 base_size,
365 FontMetricsOrientation::MatchContextPreferVertical,
366 QueryFontMetricsFlags::NEEDS_IC,
367 );
368 metrics.ic_width_or_default(reference_font_size.used_size())
369 }
370
371 if context.in_container_query {
372 context
373 .builder
374 .add_flags(ComputedValueFlags::DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY);
375 }
376
377 let reference_font_size = base_size.resolve(context);
378 match *self {
379 Self::Em(length) => {
381 if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
382 context
383 .rule_cache_conditions
384 .borrow_mut()
385 .set_font_size_dependency(reference_font_size.computed_size);
386 }
387
388 (reference_font_size.computed_size(), length)
389 },
390 Self::Lh(length) => {
391 let reference_size = if context.in_media_query {
397 context
398 .device()
399 .calc_line_height(
400 &context.default_style().get_font(),
401 context.style().writing_mode,
402 None,
403 )
404 .0
405 } else {
406 let line_height = context.builder.calc_line_height(
407 context.device(),
408 line_height_base,
409 context.style().writing_mode,
410 );
411 if context.for_non_inherited_property
412 && line_height_base == LineHeightBase::CurrentStyle
413 {
414 context
415 .rule_cache_conditions
416 .borrow_mut()
417 .set_line_height_dependency(line_height)
418 }
419 line_height.0
420 };
421 (reference_size, length)
422 },
423 Self::Ex(length) => (ex_size(context, base_size, &reference_font_size), length),
424 Self::Ch(length) => (ch_size(context, base_size, &reference_font_size), length),
425 Self::Cap(length) => (cap_size(context, base_size), length),
426 Self::Ic(length) => (ic_size(context, base_size, &reference_font_size), length),
427
428 Self::Rex(length) => {
430 let reference_size = if context.builder.is_root_element || context.in_media_query {
431 ex_size(context, base_size, &reference_font_size)
432 } else {
433 context
434 .device()
435 .root_font_metrics_ex()
436 .zoom(context.builder.effective_zoom)
437 };
438 (reference_size, length)
439 },
440 Self::Rch(length) => {
441 let reference_size = if context.builder.is_root_element || context.in_media_query {
442 ch_size(context, base_size, &reference_font_size)
443 } else {
444 context
445 .device()
446 .root_font_metrics_ch()
447 .zoom(context.builder.effective_zoom)
448 };
449 (reference_size, length)
450 },
451 Self::Rcap(length) => {
452 let reference_size = if context.builder.is_root_element || context.in_media_query {
453 cap_size(context, base_size)
454 } else {
455 context
456 .device()
457 .root_font_metrics_cap()
458 .zoom(context.builder.effective_zoom)
459 };
460 (reference_size, length)
461 },
462 Self::Ric(length) => {
463 let reference_size = if context.builder.is_root_element || context.in_media_query {
464 ic_size(context, base_size, &reference_font_size)
465 } else {
466 context
467 .device()
468 .root_font_metrics_ic()
469 .zoom(context.builder.effective_zoom)
470 };
471 (reference_size, length)
472 },
473 Self::Rem(length) => {
474 let reference_size = if context.builder.is_root_element || context.in_media_query {
481 reference_font_size.computed_size()
482 } else {
483 context
484 .device()
485 .root_font_size()
486 .zoom(context.builder.effective_zoom)
487 };
488 (reference_size, length)
489 },
490 Self::Rlh(length) => {
491 let reference_size = if context.builder.is_root_element {
497 context
498 .builder
499 .calc_line_height(
500 context.device(),
501 line_height_base,
502 context.style().writing_mode,
503 )
504 .0
505 } else if context.in_media_query {
506 context
507 .device()
508 .calc_line_height(
509 &context.default_style().get_font(),
510 context.style().writing_mode,
511 None,
512 )
513 .0
514 } else {
515 context.device().root_line_height()
516 };
517 let reference_size = reference_size.zoom(context.builder.effective_zoom);
518 (reference_size, length)
519 },
520 }
521 }
522}
523
524pub enum ViewportVariant {
526 UADefault,
528 Small,
530 Large,
532 Dynamic,
534}
535
536#[derive(PartialEq)]
538enum ViewportUnit {
539 Vw,
541 Vh,
543 Vmin,
545 Vmax,
547 Vb,
549 Vi,
551}
552
553#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
557#[repr(u8)]
558pub enum ViewportPercentageLength {
559 #[css(dimension)]
561 Vw(CSSFloat),
562 #[css(dimension)]
564 Svw(CSSFloat),
565 #[css(dimension)]
567 Lvw(CSSFloat),
568 #[css(dimension)]
570 Dvw(CSSFloat),
571 #[css(dimension)]
573 Vh(CSSFloat),
574 #[css(dimension)]
576 Svh(CSSFloat),
577 #[css(dimension)]
579 Lvh(CSSFloat),
580 #[css(dimension)]
582 Dvh(CSSFloat),
583 #[css(dimension)]
585 Vmin(CSSFloat),
586 #[css(dimension)]
588 Svmin(CSSFloat),
589 #[css(dimension)]
591 Lvmin(CSSFloat),
592 #[css(dimension)]
594 Dvmin(CSSFloat),
595 #[css(dimension)]
597 Vmax(CSSFloat),
598 #[css(dimension)]
600 Svmax(CSSFloat),
601 #[css(dimension)]
603 Lvmax(CSSFloat),
604 #[css(dimension)]
606 Dvmax(CSSFloat),
607 #[css(dimension)]
609 Vb(CSSFloat),
610 #[css(dimension)]
612 Svb(CSSFloat),
613 #[css(dimension)]
615 Lvb(CSSFloat),
616 #[css(dimension)]
618 Dvb(CSSFloat),
619 #[css(dimension)]
621 Vi(CSSFloat),
622 #[css(dimension)]
624 Svi(CSSFloat),
625 #[css(dimension)]
627 Lvi(CSSFloat),
628 #[css(dimension)]
630 Dvi(CSSFloat),
631}
632
633impl ViewportPercentageLength {
634 fn unitless_value(&self) -> CSSFloat {
636 self.unpack().2
637 }
638
639 fn unit(&self) -> &'static str {
641 match *self {
642 Self::Vw(_) => "vw",
643 Self::Lvw(_) => "lvw",
644 Self::Svw(_) => "svw",
645 Self::Dvw(_) => "dvw",
646 Self::Vh(_) => "vh",
647 Self::Svh(_) => "svh",
648 Self::Lvh(_) => "lvh",
649 Self::Dvh(_) => "dvh",
650 Self::Vmin(_) => "vmin",
651 Self::Svmin(_) => "svmin",
652 Self::Lvmin(_) => "lvmin",
653 Self::Dvmin(_) => "dvmin",
654 Self::Vmax(_) => "vmax",
655 Self::Svmax(_) => "svmax",
656 Self::Lvmax(_) => "lvmax",
657 Self::Dvmax(_) => "dvmax",
658 Self::Vb(_) => "vb",
659 Self::Svb(_) => "svb",
660 Self::Lvb(_) => "lvb",
661 Self::Dvb(_) => "dvb",
662 Self::Vi(_) => "vi",
663 Self::Svi(_) => "svi",
664 Self::Lvi(_) => "lvi",
665 Self::Dvi(_) => "dvi",
666 }
667 }
668
669 fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
670 match *self {
671 Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v),
672 Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v),
673 Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v),
674 Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v),
675 Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v),
676 Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v),
677 Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v),
678 Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v),
679 Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v),
680 Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v),
681 Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v),
682 Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v),
683 Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v),
684 Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v),
685 Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v),
686 Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v),
687 Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v),
688 Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v),
689 Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v),
690 Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v),
691 Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v),
692 Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v),
693 Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v),
694 Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v),
695 }
696 }
697
698 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
699 where
700 O: Fn(f32, f32) -> f32,
701 {
702 use self::ViewportPercentageLength::*;
703
704 if std::mem::discriminant(self) != std::mem::discriminant(other) {
705 return Err(());
706 }
707
708 Ok(match (self, other) {
709 (&Vw(one), &Vw(other)) => Vw(op(one, other)),
710 (&Svw(one), &Svw(other)) => Svw(op(one, other)),
711 (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),
712 (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),
713 (&Vh(one), &Vh(other)) => Vh(op(one, other)),
714 (&Svh(one), &Svh(other)) => Svh(op(one, other)),
715 (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),
716 (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),
717 (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),
718 (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),
719 (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),
720 (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),
721 (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),
722 (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),
723 (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),
724 (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),
725 (&Vb(one), &Vb(other)) => Vb(op(one, other)),
726 (&Svb(one), &Svb(other)) => Svb(op(one, other)),
727 (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),
728 (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),
729 (&Vi(one), &Vi(other)) => Vi(op(one, other)),
730 (&Svi(one), &Svi(other)) => Svi(op(one, other)),
731 (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),
732 (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),
733 _ => unsafe {
736 match *self {
737 Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
738 | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
739 | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
740 | Svi(..) | Lvi(..) | Dvi(..) => {},
741 }
742 debug_unreachable!("Forgot to handle unit in try_op()")
743 },
744 })
745 }
746
747 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
748 match self {
749 Self::Vw(x) => Self::Vw(op(*x)),
750 Self::Svw(x) => Self::Svw(op(*x)),
751 Self::Lvw(x) => Self::Lvw(op(*x)),
752 Self::Dvw(x) => Self::Dvw(op(*x)),
753 Self::Vh(x) => Self::Vh(op(*x)),
754 Self::Svh(x) => Self::Svh(op(*x)),
755 Self::Lvh(x) => Self::Lvh(op(*x)),
756 Self::Dvh(x) => Self::Dvh(op(*x)),
757 Self::Vmin(x) => Self::Vmin(op(*x)),
758 Self::Svmin(x) => Self::Svmin(op(*x)),
759 Self::Lvmin(x) => Self::Lvmin(op(*x)),
760 Self::Dvmin(x) => Self::Dvmin(op(*x)),
761 Self::Vmax(x) => Self::Vmax(op(*x)),
762 Self::Svmax(x) => Self::Svmax(op(*x)),
763 Self::Lvmax(x) => Self::Lvmax(op(*x)),
764 Self::Dvmax(x) => Self::Dvmax(op(*x)),
765 Self::Vb(x) => Self::Vb(op(*x)),
766 Self::Svb(x) => Self::Svb(op(*x)),
767 Self::Lvb(x) => Self::Lvb(op(*x)),
768 Self::Dvb(x) => Self::Dvb(op(*x)),
769 Self::Vi(x) => Self::Vi(op(*x)),
770 Self::Svi(x) => Self::Svi(op(*x)),
771 Self::Lvi(x) => Self::Lvi(op(*x)),
772 Self::Dvi(x) => Self::Dvi(op(*x)),
773 }
774 }
775
776 pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
778 let (variant, unit, factor) = self.unpack();
779 let size = context.viewport_size_for_viewport_unit_resolution(variant);
780 let length: app_units::Au = match unit {
781 ViewportUnit::Vw => size.width,
782 ViewportUnit::Vh => size.height,
783 ViewportUnit::Vmin => cmp::min(size.width, size.height),
784 ViewportUnit::Vmax => cmp::max(size.width, size.height),
785 ViewportUnit::Vi | ViewportUnit::Vb => {
786 context
787 .rule_cache_conditions
788 .borrow_mut()
789 .set_writing_mode_dependency(context.builder.writing_mode);
790 if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
791 size.width
792 } else {
793 size.height
794 }
795 },
796 };
797
798 let length = context.builder.effective_zoom.zoom(length.0 as f32);
800
801 let trunc_scaled =
806 ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
807 CSSPixelLength::new(crate::values::normalize(trunc_scaled))
808 }
809}
810
811#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
813#[repr(C)]
814pub struct CharacterWidth(pub i32);
815
816impl CharacterWidth {
817 pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {
819 let average_advance = reference_font_size * 0.5;
824 let max_advance = reference_font_size;
825 (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite()
826 }
827}
828
829#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
831#[repr(u8)]
832pub enum AbsoluteLength {
833 #[css(dimension)]
835 Px(CSSFloat),
836 #[css(dimension)]
838 In(CSSFloat),
839 #[css(dimension)]
841 Cm(CSSFloat),
842 #[css(dimension)]
844 Mm(CSSFloat),
845 #[css(dimension)]
847 Q(CSSFloat),
848 #[css(dimension)]
850 Pt(CSSFloat),
851 #[css(dimension)]
853 Pc(CSSFloat),
854}
855
856impl AbsoluteLength {
857 fn unitless_value(&self) -> CSSFloat {
859 match *self {
860 Self::Px(v)
861 | Self::In(v)
862 | Self::Cm(v)
863 | Self::Mm(v)
864 | Self::Q(v)
865 | Self::Pt(v)
866 | Self::Pc(v) => v,
867 }
868 }
869
870 fn unit(&self) -> &'static str {
872 match *self {
873 Self::Px(_) => "px",
874 Self::In(_) => "in",
875 Self::Cm(_) => "cm",
876 Self::Mm(_) => "mm",
877 Self::Q(_) => "q",
878 Self::Pt(_) => "pt",
879 Self::Pc(_) => "pc",
880 }
881 }
882
883 fn canonical_unit(&self) -> Option<&'static str> {
885 Some("px")
886 }
887
888 fn to(&self, unit: &str) -> Result<Self, ()> {
890 let px = self.to_px();
891
892 Ok(match_ignore_ascii_case! { unit,
893 "px" => Self::Px(px),
894 "in" => Self::In(px / PX_PER_IN),
895 "cm" => Self::Cm(px / PX_PER_CM),
896 "mm" => Self::Mm(px / PX_PER_MM),
897 "q" => Self::Q(px / PX_PER_Q),
898 "pt" => Self::Pt(px / PX_PER_PT),
899 "pc" => Self::Pc(px / PX_PER_PC),
900 _ => return Err(()),
901 })
902 }
903
904 #[inline]
906 pub fn to_px(&self) -> CSSFloat {
907 match *self {
908 Self::Px(value) => value,
909 Self::In(value) => value * PX_PER_IN,
910 Self::Cm(value) => value * PX_PER_CM,
911 Self::Mm(value) => value * PX_PER_MM,
912 Self::Q(value) => value * PX_PER_Q,
913 Self::Pt(value) => value * PX_PER_PT,
914 Self::Pc(value) => value * PX_PER_PC,
915 }
916 }
917
918 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
919 where
920 O: Fn(f32, f32) -> f32,
921 {
922 Ok(Self::Px(op(self.to_px(), other.to_px())))
923 }
924
925 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
926 Self::Px(op(self.to_px()))
927 }
928}
929
930impl ToComputedValue for AbsoluteLength {
931 type ComputedValue = CSSPixelLength;
932
933 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
934 CSSPixelLength::new(self.to_px())
935 .zoom(context.builder.effective_zoom)
936 .finite()
937 }
938
939 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
940 Self::Px(computed.px())
941 }
942}
943
944impl PartialOrd for AbsoluteLength {
945 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
946 self.to_px().partial_cmp(&other.to_px())
947 }
948}
949
950#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
954#[repr(u8)]
955pub enum ContainerRelativeLength {
956 #[css(dimension)]
958 Cqw(CSSFloat),
959 #[css(dimension)]
961 Cqh(CSSFloat),
962 #[css(dimension)]
964 Cqi(CSSFloat),
965 #[css(dimension)]
967 Cqb(CSSFloat),
968 #[css(dimension)]
970 Cqmin(CSSFloat),
971 #[css(dimension)]
973 Cqmax(CSSFloat),
974}
975
976impl ContainerRelativeLength {
977 fn unitless_value(&self) -> CSSFloat {
978 match *self {
979 Self::Cqw(v)
980 | Self::Cqh(v)
981 | Self::Cqi(v)
982 | Self::Cqb(v)
983 | Self::Cqmin(v)
984 | Self::Cqmax(v) => v,
985 }
986 }
987
988 fn unit(&self) -> &'static str {
990 match *self {
991 Self::Cqw(_) => "cqw",
992 Self::Cqh(_) => "cqh",
993 Self::Cqi(_) => "cqi",
994 Self::Cqb(_) => "cqb",
995 Self::Cqmin(_) => "cqmin",
996 Self::Cqmax(_) => "cqmax",
997 }
998 }
999
1000 pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
1001 where
1002 O: Fn(f32, f32) -> f32,
1003 {
1004 use self::ContainerRelativeLength::*;
1005
1006 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1007 return Err(());
1008 }
1009
1010 Ok(match (self, other) {
1011 (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
1012 (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
1013 (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
1014 (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
1015 (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
1016 (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),
1017
1018 _ => unsafe {
1022 match *self {
1023 Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1024 }
1025 debug_unreachable!("Forgot to handle unit in try_op()")
1026 },
1027 })
1028 }
1029
1030 pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1031 match self {
1032 Self::Cqw(x) => Self::Cqw(op(*x)),
1033 Self::Cqh(x) => Self::Cqh(op(*x)),
1034 Self::Cqi(x) => Self::Cqi(op(*x)),
1035 Self::Cqb(x) => Self::Cqb(op(*x)),
1036 Self::Cqmin(x) => Self::Cqmin(op(*x)),
1037 Self::Cqmax(x) => Self::Cqmax(op(*x)),
1038 }
1039 }
1040
1041 pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
1043 if context.for_non_inherited_property {
1044 context.rule_cache_conditions.borrow_mut().set_uncacheable();
1045 }
1046 context
1047 .builder
1048 .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
1049
1050 let size = context.get_container_size_query();
1054 let (factor, container_length) = match *self {
1055 Self::Cqw(v) => (v, size.get_container_width(context)),
1056 Self::Cqh(v) => (v, size.get_container_height(context)),
1057 Self::Cqi(v) => (v, size.get_container_inline_size(context)),
1058 Self::Cqb(v) => (v, size.get_container_block_size(context)),
1059 Self::Cqmin(v) => (
1060 v,
1061 cmp::min(
1062 size.get_container_inline_size(context),
1063 size.get_container_block_size(context),
1064 ),
1065 ),
1066 Self::Cqmax(v) => (
1067 v,
1068 cmp::max(
1069 size.get_container_inline_size(context),
1070 size.get_container_block_size(context),
1071 ),
1072 ),
1073 };
1074 CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
1075 }
1076}
1077
1078#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
1082#[repr(u8)]
1083pub enum NoCalcLength {
1084 Absolute(AbsoluteLength),
1088
1089 FontRelative(FontRelativeLength),
1093
1094 ViewportPercentage(ViewportPercentageLength),
1098
1099 ContainerRelative(ContainerRelativeLength),
1103 ServoCharacterWidth(CharacterWidth),
1108}
1109
1110impl NoCalcLength {
1111 pub fn unitless_value(&self) -> CSSFloat {
1113 match *self {
1114 Self::Absolute(v) => v.unitless_value(),
1115 Self::FontRelative(v) => v.unitless_value(),
1116 Self::ViewportPercentage(v) => v.unitless_value(),
1117 Self::ContainerRelative(v) => v.unitless_value(),
1118 Self::ServoCharacterWidth(c) => c.0 as f32,
1119 }
1120 }
1121
1122 pub fn unit(&self) -> &'static str {
1124 match *self {
1125 Self::Absolute(v) => v.unit(),
1126 Self::FontRelative(v) => v.unit(),
1127 Self::ViewportPercentage(v) => v.unit(),
1128 Self::ContainerRelative(v) => v.unit(),
1129 Self::ServoCharacterWidth(_) => "",
1130 }
1131 }
1132
1133 pub fn canonical_unit(&self) -> Option<&'static str> {
1135 match *self {
1136 Self::Absolute(v) => v.canonical_unit(),
1137 _ => None,
1138 }
1139 }
1140
1141 pub fn to(&self, unit: &str) -> Result<Self, ()> {
1143 match self {
1144 Self::Absolute(v) => Ok(Self::Absolute(v.to(unit)?)),
1145 _ => Err(()),
1146 }
1147 }
1148
1149 pub fn is_negative(&self) -> bool {
1151 self.unitless_value().is_sign_negative()
1152 }
1153
1154 pub fn is_zero(&self) -> bool {
1156 self.unitless_value() == 0.0
1157 }
1158
1159 pub fn is_infinite(&self) -> bool {
1161 self.unitless_value().is_infinite()
1162 }
1163
1164 pub fn is_nan(&self) -> bool {
1166 self.unitless_value().is_nan()
1167 }
1168
1169 pub fn should_zoom_text(&self) -> bool {
1174 match *self {
1175 Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,
1176 Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,
1177 }
1178 }
1179
1180 pub fn parse_dimension_with_flags(
1182 parsing_mode: ParsingMode,
1183 in_page_rule: bool,
1184 value: CSSFloat,
1185 unit: &str,
1186 ) -> Result<Self, ()> {
1187 let allows_computational_dependence = parsing_mode.allows_computational_dependence();
1188
1189 Ok(match_ignore_ascii_case! { unit,
1190 "px" => Self::Absolute(AbsoluteLength::Px(value)),
1191 "in" => Self::Absolute(AbsoluteLength::In(value)),
1192 "cm" => Self::Absolute(AbsoluteLength::Cm(value)),
1193 "mm" => Self::Absolute(AbsoluteLength::Mm(value)),
1194 "q" => Self::Absolute(AbsoluteLength::Q(value)),
1195 "pt" => Self::Absolute(AbsoluteLength::Pt(value)),
1196 "pc" => Self::Absolute(AbsoluteLength::Pc(value)),
1197 "em" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Em(value)),
1199 "ex" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ex(value)),
1200 "rex" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rex(value)),
1201 "ch" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ch(value)),
1202 "rch" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rch(value)),
1203 "cap" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Cap(value)),
1204 "rcap" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rcap(value)),
1205 "ic" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ic(value)),
1206 "ric" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ric(value)),
1207 "rem" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rem(value)),
1208 "lh" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Lh(value)),
1209 "rlh" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rlh(value)),
1210 "vw" if !in_page_rule => {
1212 Self::ViewportPercentage(ViewportPercentageLength::Vw(value))
1213 },
1214 "svw" if !in_page_rule => {
1215 Self::ViewportPercentage(ViewportPercentageLength::Svw(value))
1216 },
1217 "lvw" if !in_page_rule => {
1218 Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))
1219 },
1220 "dvw" if !in_page_rule => {
1221 Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))
1222 },
1223 "vh" if !in_page_rule => {
1224 Self::ViewportPercentage(ViewportPercentageLength::Vh(value))
1225 },
1226 "svh" if !in_page_rule => {
1227 Self::ViewportPercentage(ViewportPercentageLength::Svh(value))
1228 },
1229 "lvh" if !in_page_rule => {
1230 Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))
1231 },
1232 "dvh" if !in_page_rule => {
1233 Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))
1234 },
1235 "vmin" if !in_page_rule => {
1236 Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))
1237 },
1238 "svmin" if !in_page_rule => {
1239 Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))
1240 },
1241 "lvmin" if !in_page_rule => {
1242 Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))
1243 },
1244 "dvmin" if !in_page_rule => {
1245 Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))
1246 },
1247 "vmax" if !in_page_rule => {
1248 Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))
1249 },
1250 "svmax" if !in_page_rule => {
1251 Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))
1252 },
1253 "lvmax" if !in_page_rule => {
1254 Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))
1255 },
1256 "dvmax" if !in_page_rule => {
1257 Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))
1258 },
1259 "vb" if !in_page_rule => {
1260 Self::ViewportPercentage(ViewportPercentageLength::Vb(value))
1261 },
1262 "svb" if !in_page_rule => {
1263 Self::ViewportPercentage(ViewportPercentageLength::Svb(value))
1264 },
1265 "lvb" if !in_page_rule => {
1266 Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))
1267 },
1268 "dvb" if !in_page_rule => {
1269 Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))
1270 },
1271 "vi" if !in_page_rule => {
1272 Self::ViewportPercentage(ViewportPercentageLength::Vi(value))
1273 },
1274 "svi" if !in_page_rule => {
1275 Self::ViewportPercentage(ViewportPercentageLength::Svi(value))
1276 },
1277 "lvi" if !in_page_rule => {
1278 Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))
1279 },
1280 "dvi" if !in_page_rule => {
1281 Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))
1282 },
1283 "cqw" if !in_page_rule && cfg!(feature = "gecko") => {
1286 Self::ContainerRelative(ContainerRelativeLength::Cqw(value))
1287 },
1288 "cqh" if !in_page_rule && cfg!(feature = "gecko") => {
1289 Self::ContainerRelative(ContainerRelativeLength::Cqh(value))
1290 },
1291 "cqi" if !in_page_rule && cfg!(feature = "gecko") => {
1292 Self::ContainerRelative(ContainerRelativeLength::Cqi(value))
1293 },
1294 "cqb" if !in_page_rule && cfg!(feature = "gecko") => {
1295 Self::ContainerRelative(ContainerRelativeLength::Cqb(value))
1296 },
1297 "cqmin" if !in_page_rule && cfg!(feature = "gecko") => {
1298 Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))
1299 },
1300 "cqmax" if !in_page_rule && cfg!(feature = "gecko") => {
1301 Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))
1302 },
1303 _ => return Err(()),
1304 })
1305 }
1306
1307 pub fn parse_dimension_with_context(
1309 context: &ParserContext,
1310 value: CSSFloat,
1311 unit: &str,
1312 ) -> Result<Self, ()> {
1313 Self::parse_dimension_with_flags(context.parsing_mode, context.in_page_rule(), value, unit)
1314 }
1315
1316 pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
1317 where
1318 O: Fn(f32, f32) -> f32,
1319 {
1320 use self::NoCalcLength::*;
1321
1322 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1323 return Err(());
1324 }
1325
1326 Ok(match (self, other) {
1327 (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
1328 (&FontRelative(ref one), &FontRelative(ref other)) => {
1329 FontRelative(one.try_op(other, op)?)
1330 },
1331 (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1332 ViewportPercentage(one.try_op(other, op)?)
1333 },
1334 (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
1335 ContainerRelative(one.try_op(other, op)?)
1336 },
1337 (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1338 ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
1339 },
1340 _ => unsafe {
1343 match *self {
1344 Absolute(..)
1345 | FontRelative(..)
1346 | ViewportPercentage(..)
1347 | ContainerRelative(..)
1348 | ServoCharacterWidth(..) => {},
1349 }
1350 debug_unreachable!("Forgot to handle unit in try_op()")
1351 },
1352 })
1353 }
1354
1355 pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1356 use self::NoCalcLength::*;
1357
1358 match self {
1359 Absolute(ref one) => Absolute(one.map(op)),
1360 FontRelative(ref one) => FontRelative(one.map(op)),
1361 ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),
1362 ContainerRelative(ref one) => ContainerRelative(one.map(op)),
1363 ServoCharacterWidth(ref one) => {
1364 ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))
1365 },
1366 }
1367 }
1368
1369 #[inline]
1371 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1372 match *self {
1373 Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1374 _ => Err(()),
1375 }
1376 }
1377
1378 #[cfg(feature = "gecko")]
1381 #[inline]
1382 pub fn to_computed_pixel_length_with_font_metrics(
1383 &self,
1384 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1385 ) -> Result<CSSFloat, ()> {
1386 match *self {
1387 Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1388 Self::FontRelative(fr) => {
1389 if let Some(getter) = get_font_metrics {
1390 fr.to_computed_pixel_length_with_font_metrics(getter)
1391 } else {
1392 Err(())
1393 }
1394 },
1395 _ => Err(()),
1396 }
1397 }
1398
1399 #[inline]
1401 pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
1402 NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
1403 }
1404}
1405
1406impl ToCss for NoCalcLength {
1407 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1408 where
1409 W: Write,
1410 {
1411 crate::values::serialize_specified_dimension(
1412 self.unitless_value(),
1413 self.unit(),
1414 false,
1415 dest,
1416 )
1417 }
1418}
1419
1420impl ToTyped for NoCalcLength {
1421 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1422 let value = self.unitless_value();
1423 let unit = CssString::from(self.unit());
1424 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
1425 value,
1426 unit,
1427 })));
1428 Ok(())
1429 }
1430}
1431
1432impl SpecifiedValueInfo for NoCalcLength {}
1433
1434impl PartialOrd for NoCalcLength {
1435 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1436 use self::NoCalcLength::*;
1437
1438 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1439 return None;
1440 }
1441
1442 match (self, other) {
1443 (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
1444 (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
1445 (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1446 one.partial_cmp(other)
1447 },
1448 (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),
1449 (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1450 one.0.partial_cmp(&other.0)
1451 },
1452 _ => unsafe {
1455 match *self {
1456 Absolute(..)
1457 | FontRelative(..)
1458 | ViewportPercentage(..)
1459 | ContainerRelative(..)
1460 | ServoCharacterWidth(..) => {},
1461 }
1462 debug_unreachable!("Forgot an arm in partial_cmp?")
1463 },
1464 }
1465 }
1466}
1467
1468impl Zero for NoCalcLength {
1469 fn zero() -> Self {
1470 NoCalcLength::Absolute(AbsoluteLength::Px(0.))
1471 }
1472
1473 fn is_zero(&self) -> bool {
1474 NoCalcLength::is_zero(self)
1475 }
1476}
1477
1478#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1483pub enum Length {
1484 NoCalc(NoCalcLength),
1486 Calc(Box<CalcLengthPercentage>),
1490}
1491
1492impl From<NoCalcLength> for Length {
1493 #[inline]
1494 fn from(len: NoCalcLength) -> Self {
1495 Length::NoCalc(len)
1496 }
1497}
1498
1499impl PartialOrd for FontRelativeLength {
1500 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1501 use self::FontRelativeLength::*;
1502
1503 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1504 return None;
1505 }
1506
1507 match (self, other) {
1508 (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
1509 (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
1510 (&Rex(ref one), &Rex(ref other)) => one.partial_cmp(other),
1511 (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
1512 (&Rch(ref one), &Rch(ref other)) => one.partial_cmp(other),
1513 (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
1514 (&Rcap(ref one), &Rcap(ref other)) => one.partial_cmp(other),
1515 (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
1516 (&Ric(ref one), &Ric(ref other)) => one.partial_cmp(other),
1517 (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
1518 (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other),
1519 (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other),
1520 _ => unsafe {
1523 match *self {
1524 Em(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) | Ic(..)
1525 | Ric(..) | Rem(..) | Lh(..) | Rlh(..) => {},
1526 }
1527 debug_unreachable!("Forgot an arm in partial_cmp?")
1528 },
1529 }
1530 }
1531}
1532
1533impl PartialOrd for ContainerRelativeLength {
1534 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1535 use self::ContainerRelativeLength::*;
1536
1537 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1538 return None;
1539 }
1540
1541 match (self, other) {
1542 (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),
1543 (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),
1544 (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),
1545 (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),
1546 (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),
1547 (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),
1548
1549 _ => unsafe {
1553 match *self {
1554 Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1555 }
1556 debug_unreachable!("Forgot to handle unit in partial_cmp()")
1557 },
1558 }
1559 }
1560}
1561
1562impl PartialOrd for ViewportPercentageLength {
1563 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1564 use self::ViewportPercentageLength::*;
1565
1566 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1567 return None;
1568 }
1569
1570 match (self, other) {
1571 (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
1572 (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),
1573 (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),
1574 (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),
1575 (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
1576 (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),
1577 (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),
1578 (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),
1579 (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
1580 (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),
1581 (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),
1582 (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),
1583 (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
1584 (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),
1585 (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),
1586 (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),
1587 (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),
1588 (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),
1589 (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),
1590 (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),
1591 (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),
1592 (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),
1593 (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),
1594 (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),
1595 _ => unsafe {
1598 match *self {
1599 Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
1600 | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
1601 | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
1602 | Svi(..) | Lvi(..) | Dvi(..) => {},
1603 }
1604 debug_unreachable!("Forgot an arm in partial_cmp?")
1605 },
1606 }
1607 }
1608}
1609
1610impl Length {
1611 #[inline]
1612 fn parse_internal<'i, 't>(
1613 context: &ParserContext,
1614 input: &mut Parser<'i, 't>,
1615 num_context: AllowedNumericType,
1616 allow_quirks: AllowQuirks,
1617 ) -> Result<Self, ParseError<'i>> {
1618 let location = input.current_source_location();
1619 let token = input.next()?;
1620 match *token {
1621 Token::Dimension {
1622 value, ref unit, ..
1623 } if num_context.is_ok(context.parsing_mode, value) => {
1624 NoCalcLength::parse_dimension_with_context(context, value, unit)
1625 .map(Length::NoCalc)
1626 .map_err(|()| location.new_unexpected_token_error(token.clone()))
1627 },
1628 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1629 if value != 0.
1630 && !context.parsing_mode.allows_unitless_lengths()
1631 && !allow_quirks.allowed(context.quirks_mode)
1632 {
1633 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1634 }
1635 Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
1636 value,
1637 ))))
1638 },
1639 Token::Function(ref name) => {
1640 let function = CalcNode::math_function(context, name, location)?;
1641 let calc = CalcNode::parse_length(context, input, num_context, function)?;
1642 Ok(Length::Calc(Box::new(calc)))
1643 },
1644 ref token => return Err(location.new_unexpected_token_error(token.clone())),
1645 }
1646 }
1647
1648 #[inline]
1650 pub fn parse_non_negative<'i, 't>(
1651 context: &ParserContext,
1652 input: &mut Parser<'i, 't>,
1653 ) -> Result<Self, ParseError<'i>> {
1654 Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1655 }
1656
1657 #[inline]
1659 pub fn parse_non_negative_quirky<'i, 't>(
1660 context: &ParserContext,
1661 input: &mut Parser<'i, 't>,
1662 allow_quirks: AllowQuirks,
1663 ) -> Result<Self, ParseError<'i>> {
1664 Self::parse_internal(
1665 context,
1666 input,
1667 AllowedNumericType::NonNegative,
1668 allow_quirks,
1669 )
1670 }
1671
1672 #[inline]
1674 pub fn from_px(px_value: CSSFloat) -> Length {
1675 Length::NoCalc(NoCalcLength::from_px(px_value))
1676 }
1677
1678 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1680 match *self {
1681 Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),
1682 Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
1683 }
1684 }
1685
1686 #[cfg(feature = "gecko")]
1688 pub fn to_computed_pixel_length_with_font_metrics(
1689 &self,
1690 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1691 ) -> Result<CSSFloat, ()> {
1692 match *self {
1693 Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1694 Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1695 }
1696 }
1697}
1698
1699impl Parse for Length {
1700 fn parse<'i, 't>(
1701 context: &ParserContext,
1702 input: &mut Parser<'i, 't>,
1703 ) -> Result<Self, ParseError<'i>> {
1704 Self::parse_quirky(context, input, AllowQuirks::No)
1705 }
1706}
1707
1708impl Zero for Length {
1709 fn zero() -> Self {
1710 Length::NoCalc(NoCalcLength::zero())
1711 }
1712
1713 fn is_zero(&self) -> bool {
1714 match *self {
1717 Length::NoCalc(ref l) => l.is_zero(),
1718 Length::Calc(..) => false,
1719 }
1720 }
1721}
1722
1723impl Length {
1724 pub fn parse_quirky<'i, 't>(
1726 context: &ParserContext,
1727 input: &mut Parser<'i, 't>,
1728 allow_quirks: AllowQuirks,
1729 ) -> Result<Self, ParseError<'i>> {
1730 Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1731 }
1732}
1733
1734pub type NonNegativeLength = NonNegative<Length>;
1736
1737impl Parse for NonNegativeLength {
1738 #[inline]
1739 fn parse<'i, 't>(
1740 context: &ParserContext,
1741 input: &mut Parser<'i, 't>,
1742 ) -> Result<Self, ParseError<'i>> {
1743 Ok(NonNegative(Length::parse_non_negative(context, input)?))
1744 }
1745}
1746
1747impl From<NoCalcLength> for NonNegativeLength {
1748 #[inline]
1749 fn from(len: NoCalcLength) -> Self {
1750 NonNegative(Length::NoCalc(len))
1751 }
1752}
1753
1754impl From<Length> for NonNegativeLength {
1755 #[inline]
1756 fn from(len: Length) -> Self {
1757 NonNegative(len)
1758 }
1759}
1760
1761impl NonNegativeLength {
1762 #[inline]
1764 pub fn from_px(px_value: CSSFloat) -> Self {
1765 Length::from_px(px_value.max(0.)).into()
1766 }
1767
1768 #[inline]
1770 pub fn parse_quirky<'i, 't>(
1771 context: &ParserContext,
1772 input: &mut Parser<'i, 't>,
1773 allow_quirks: AllowQuirks,
1774 ) -> Result<Self, ParseError<'i>> {
1775 Ok(NonNegative(Length::parse_non_negative_quirky(
1776 context,
1777 input,
1778 allow_quirks,
1779 )?))
1780 }
1781}
1782
1783#[allow(missing_docs)]
1788#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1789pub enum LengthPercentage {
1790 Length(NoCalcLength),
1791 Percentage(computed::Percentage),
1792 Calc(Box<CalcLengthPercentage>),
1793}
1794
1795impl From<Length> for LengthPercentage {
1796 fn from(len: Length) -> LengthPercentage {
1797 match len {
1798 Length::NoCalc(l) => LengthPercentage::Length(l),
1799 Length::Calc(l) => LengthPercentage::Calc(l),
1800 }
1801 }
1802}
1803
1804impl From<NoCalcLength> for LengthPercentage {
1805 #[inline]
1806 fn from(len: NoCalcLength) -> Self {
1807 LengthPercentage::Length(len)
1808 }
1809}
1810
1811impl From<Percentage> for LengthPercentage {
1812 #[inline]
1813 fn from(pc: Percentage) -> Self {
1814 if let Some(clamping_mode) = pc.calc_clamping_mode() {
1815 LengthPercentage::Calc(Box::new(CalcLengthPercentage {
1816 clamping_mode,
1817 node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
1818 }))
1819 } else {
1820 LengthPercentage::Percentage(computed::Percentage(pc.get()))
1821 }
1822 }
1823}
1824
1825impl From<computed::Percentage> for LengthPercentage {
1826 #[inline]
1827 fn from(pc: computed::Percentage) -> Self {
1828 LengthPercentage::Percentage(pc)
1829 }
1830}
1831
1832impl Parse for LengthPercentage {
1833 #[inline]
1834 fn parse<'i, 't>(
1835 context: &ParserContext,
1836 input: &mut Parser<'i, 't>,
1837 ) -> Result<Self, ParseError<'i>> {
1838 Self::parse_quirky(context, input, AllowQuirks::No)
1839 }
1840}
1841
1842impl LengthPercentage {
1843 #[inline]
1844 pub fn zero_percent() -> LengthPercentage {
1846 LengthPercentage::Percentage(computed::Percentage::zero())
1847 }
1848
1849 #[inline]
1850 pub fn hundred_percent() -> LengthPercentage {
1852 LengthPercentage::Percentage(computed::Percentage::hundred())
1853 }
1854
1855 fn parse_internal<'i, 't>(
1856 context: &ParserContext,
1857 input: &mut Parser<'i, 't>,
1858 num_context: AllowedNumericType,
1859 allow_quirks: AllowQuirks,
1860 allow_anchor: AllowAnchorPositioningFunctions,
1861 ) -> Result<Self, ParseError<'i>> {
1862 let location = input.current_source_location();
1863 let token = input.next()?;
1864 match *token {
1865 Token::Dimension {
1866 value, ref unit, ..
1867 } if num_context.is_ok(context.parsing_mode, value) => {
1868 return NoCalcLength::parse_dimension_with_context(context, value, unit)
1869 .map(LengthPercentage::Length)
1870 .map_err(|()| location.new_unexpected_token_error(token.clone()));
1871 },
1872 Token::Percentage { unit_value, .. }
1873 if num_context.is_ok(context.parsing_mode, unit_value) =>
1874 {
1875 return Ok(LengthPercentage::Percentage(computed::Percentage(
1876 unit_value,
1877 )));
1878 },
1879 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1880 if value != 0.
1881 && !context.parsing_mode.allows_unitless_lengths()
1882 && !allow_quirks.allowed(context.quirks_mode)
1883 {
1884 return Err(location.new_unexpected_token_error(token.clone()));
1885 } else {
1886 return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
1887 }
1888 },
1889 Token::Function(ref name) => {
1890 let function = CalcNode::math_function(context, name, location)?;
1891 let calc = CalcNode::parse_length_or_percentage(
1892 context,
1893 input,
1894 num_context,
1895 function,
1896 allow_anchor,
1897 )?;
1898 Ok(LengthPercentage::Calc(Box::new(calc)))
1899 },
1900 _ => return Err(location.new_unexpected_token_error(token.clone())),
1901 }
1902 }
1903
1904 #[inline]
1907 pub fn parse_quirky<'i, 't>(
1908 context: &ParserContext,
1909 input: &mut Parser<'i, 't>,
1910 allow_quirks: AllowQuirks,
1911 ) -> Result<Self, ParseError<'i>> {
1912 Self::parse_internal(
1913 context,
1914 input,
1915 AllowedNumericType::All,
1916 allow_quirks,
1917 AllowAnchorPositioningFunctions::No,
1918 )
1919 }
1920
1921 #[inline]
1924 fn parse_quirky_with_anchor_size_function<'i, 't>(
1925 context: &ParserContext,
1926 input: &mut Parser<'i, 't>,
1927 allow_quirks: AllowQuirks,
1928 ) -> Result<Self, ParseError<'i>> {
1929 Self::parse_internal(
1930 context,
1931 input,
1932 AllowedNumericType::All,
1933 allow_quirks,
1934 AllowAnchorPositioningFunctions::AllowAnchorSize,
1935 )
1936 }
1937
1938 #[inline]
1941 pub fn parse_quirky_with_anchor_functions<'i, 't>(
1942 context: &ParserContext,
1943 input: &mut Parser<'i, 't>,
1944 allow_quirks: AllowQuirks,
1945 ) -> Result<Self, ParseError<'i>> {
1946 Self::parse_internal(
1947 context,
1948 input,
1949 AllowedNumericType::All,
1950 allow_quirks,
1951 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1952 )
1953 }
1954
1955 pub fn parse_non_negative_with_anchor_size<'i, 't>(
1958 context: &ParserContext,
1959 input: &mut Parser<'i, 't>,
1960 allow_quirks: AllowQuirks,
1961 ) -> Result<Self, ParseError<'i>> {
1962 Self::parse_internal(
1963 context,
1964 input,
1965 AllowedNumericType::NonNegative,
1966 allow_quirks,
1967 AllowAnchorPositioningFunctions::AllowAnchorSize,
1968 )
1969 }
1970
1971 #[inline]
1976 pub fn parse_non_negative<'i, 't>(
1977 context: &ParserContext,
1978 input: &mut Parser<'i, 't>,
1979 ) -> Result<Self, ParseError<'i>> {
1980 Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1981 }
1982
1983 #[inline]
1985 pub fn parse_non_negative_quirky<'i, 't>(
1986 context: &ParserContext,
1987 input: &mut Parser<'i, 't>,
1988 allow_quirks: AllowQuirks,
1989 ) -> Result<Self, ParseError<'i>> {
1990 Self::parse_internal(
1991 context,
1992 input,
1993 AllowedNumericType::NonNegative,
1994 allow_quirks,
1995 AllowAnchorPositioningFunctions::No,
1996 )
1997 }
1998}
1999
2000impl Zero for LengthPercentage {
2001 fn zero() -> Self {
2002 LengthPercentage::Length(NoCalcLength::zero())
2003 }
2004
2005 fn is_zero(&self) -> bool {
2006 match *self {
2007 LengthPercentage::Length(l) => l.is_zero(),
2008 LengthPercentage::Percentage(p) => p.0 == 0.0,
2009 LengthPercentage::Calc(_) => false,
2010 }
2011 }
2012}
2013
2014impl ZeroNoPercent for LengthPercentage {
2015 fn is_zero_no_percent(&self) -> bool {
2016 match *self {
2017 LengthPercentage::Percentage(_) => false,
2018 _ => self.is_zero(),
2019 }
2020 }
2021}
2022
2023pub trait EqualsPercentage {
2025 fn equals_percentage(&self, v: CSSFloat) -> bool;
2028}
2029
2030impl EqualsPercentage for LengthPercentage {
2031 fn equals_percentage(&self, v: CSSFloat) -> bool {
2032 match *self {
2033 LengthPercentage::Percentage(p) => p.0 == v,
2034 _ => false,
2035 }
2036 }
2037}
2038
2039pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
2041
2042impl LengthPercentageOrAuto {
2043 #[inline]
2045 pub fn zero_percent() -> Self {
2046 generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
2047 }
2048
2049 #[inline]
2052 pub fn parse_quirky<'i, 't>(
2053 context: &ParserContext,
2054 input: &mut Parser<'i, 't>,
2055 allow_quirks: AllowQuirks,
2056 ) -> Result<Self, ParseError<'i>> {
2057 Self::parse_with(context, input, |context, input| {
2058 LengthPercentage::parse_quirky(context, input, allow_quirks)
2059 })
2060 }
2061}
2062
2063pub type NonNegativeLengthPercentageOrAuto =
2065 generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
2066
2067impl NonNegativeLengthPercentageOrAuto {
2068 #[inline]
2070 pub fn zero_percent() -> Self {
2071 generics::LengthPercentageOrAuto::LengthPercentage(
2072 NonNegativeLengthPercentage::zero_percent(),
2073 )
2074 }
2075
2076 #[inline]
2079 pub fn parse_quirky<'i, 't>(
2080 context: &ParserContext,
2081 input: &mut Parser<'i, 't>,
2082 allow_quirks: AllowQuirks,
2083 ) -> Result<Self, ParseError<'i>> {
2084 Self::parse_with(context, input, |context, input| {
2085 NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
2086 })
2087 }
2088}
2089
2090pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
2092
2093pub type NonNegativeLengthPercentageOrNormal =
2095 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
2096
2097impl From<NoCalcLength> for NonNegativeLengthPercentage {
2098 #[inline]
2099 fn from(len: NoCalcLength) -> Self {
2100 NonNegative(LengthPercentage::from(len))
2101 }
2102}
2103
2104impl Parse for NonNegativeLengthPercentage {
2105 #[inline]
2106 fn parse<'i, 't>(
2107 context: &ParserContext,
2108 input: &mut Parser<'i, 't>,
2109 ) -> Result<Self, ParseError<'i>> {
2110 Self::parse_quirky(context, input, AllowQuirks::No)
2111 }
2112}
2113
2114impl NonNegativeLengthPercentage {
2115 #[inline]
2116 pub fn zero_percent() -> Self {
2118 NonNegative(LengthPercentage::zero_percent())
2119 }
2120
2121 #[inline]
2124 pub fn parse_quirky<'i, 't>(
2125 context: &ParserContext,
2126 input: &mut Parser<'i, 't>,
2127 allow_quirks: AllowQuirks,
2128 ) -> Result<Self, ParseError<'i>> {
2129 LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
2130 }
2131
2132 #[inline]
2135 pub fn parse_non_negative_with_anchor_size<'i, 't>(
2136 context: &ParserContext,
2137 input: &mut Parser<'i, 't>,
2138 allow_quirks: AllowQuirks,
2139 ) -> Result<Self, ParseError<'i>> {
2140 LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
2141 .map(NonNegative)
2142 }
2143}
2144
2145pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
2151
2152impl LengthOrAuto {
2153 #[inline]
2156 pub fn parse_quirky<'i, 't>(
2157 context: &ParserContext,
2158 input: &mut Parser<'i, 't>,
2159 allow_quirks: AllowQuirks,
2160 ) -> Result<Self, ParseError<'i>> {
2161 Self::parse_with(context, input, |context, input| {
2162 Length::parse_quirky(context, input, allow_quirks)
2163 })
2164 }
2165}
2166
2167pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
2169
2170pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
2172
2173pub type Size = GenericSize<NonNegativeLengthPercentage>;
2175
2176impl Parse for Size {
2177 fn parse<'i, 't>(
2178 context: &ParserContext,
2179 input: &mut Parser<'i, 't>,
2180 ) -> Result<Self, ParseError<'i>> {
2181 Size::parse_quirky(context, input, AllowQuirks::No)
2182 }
2183}
2184
2185macro_rules! parse_size_non_length {
2186 ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
2187 $auto_or_none:expr => $auto_or_none_ident:ident) => {{
2188 let size = $input.try_parse(|input| {
2189 Ok(try_match_ident_ignore_ascii_case! { input,
2190 "min-content" | "-moz-min-content" => $size::MinContent,
2191 "max-content" | "-moz-max-content" => $size::MaxContent,
2192 "fit-content" | "-moz-fit-content" => $size::FitContent,
2193 #[cfg(feature = "gecko")]
2194 "-moz-available" => $size::MozAvailable,
2195 "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
2196 "stretch" if is_stretch_enabled() => $size::Stretch,
2197 $auto_or_none => $size::$auto_or_none_ident,
2198 })
2199 });
2200 if size.is_ok() {
2201 return size;
2202 }
2203 }};
2204}
2205
2206fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
2207 static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2208}
2209
2210fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
2211 static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2215 && static_prefs::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
2216}
2217
2218fn is_stretch_enabled() -> bool {
2219 static_prefs::pref!("layout.css.stretch-size-keyword.enabled")
2220}
2221
2222fn is_fit_content_function_enabled() -> bool {
2223 static_prefs::pref!("layout.css.fit-content-function.enabled")
2224}
2225
2226macro_rules! parse_fit_content_function {
2227 ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
2228 if is_fit_content_function_enabled() {
2229 if let Ok(length) = $input.try_parse(|input| {
2230 input.expect_function_matching("fit-content")?;
2231 input.parse_nested_block(|i| {
2232 NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
2233 })
2234 }) {
2235 return Ok($size::FitContentFunction(length));
2236 }
2237 }
2238 };
2239}
2240
2241#[derive(Clone, Copy, PartialEq, Eq)]
2242enum ParseAnchorFunctions {
2243 Yes,
2244 No,
2245}
2246
2247impl Size {
2248 pub fn parse_quirky<'i, 't>(
2250 context: &ParserContext,
2251 input: &mut Parser<'i, 't>,
2252 allow_quirks: AllowQuirks,
2253 ) -> Result<Self, ParseError<'i>> {
2254 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
2255 Self::parse_quirky_internal(
2256 context,
2257 input,
2258 allow_quirks,
2259 allow_webkit_fill_available,
2260 ParseAnchorFunctions::Yes,
2261 )
2262 }
2263
2264 pub fn parse_size_for_flex_basis_width<'i, 't>(
2266 context: &ParserContext,
2267 input: &mut Parser<'i, 't>,
2268 ) -> Result<Self, ParseError<'i>> {
2269 Self::parse_quirky_internal(
2270 context,
2271 input,
2272 AllowQuirks::No,
2273 true,
2274 ParseAnchorFunctions::No,
2275 )
2276 }
2277
2278 fn parse_quirky_internal<'i, 't>(
2283 context: &ParserContext,
2284 input: &mut Parser<'i, 't>,
2285 allow_quirks: AllowQuirks,
2286 allow_webkit_fill_available: bool,
2287 allow_anchor_functions: ParseAnchorFunctions,
2288 ) -> Result<Self, ParseError<'i>> {
2289 parse_size_non_length!(Size, input, allow_webkit_fill_available,
2290 "auto" => Auto);
2291 parse_fit_content_function!(Size, input, context, allow_quirks);
2292
2293 let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
2294 && static_prefs::pref!("layout.css.anchor-positioning.enabled");
2295 match input
2296 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2297 {
2298 Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
2299 Err(e) if !allow_anchor => return Err(e.into()),
2300 Err(_) => (),
2301 };
2302 if let Ok(length) = input.try_parse(|i| {
2303 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2304 context,
2305 i,
2306 allow_quirks,
2307 )
2308 }) {
2309 return Ok(GenericSize::AnchorContainingCalcFunction(length));
2310 }
2311 Ok(Self::AnchorSizeFunction(Box::new(
2312 GenericAnchorSizeFunction::parse(context, input)?,
2313 )))
2314 }
2315
2316 pub fn parse_size_for_width_or_height_quirky<'i, 't>(
2322 context: &ParserContext,
2323 input: &mut Parser<'i, 't>,
2324 allow_quirks: AllowQuirks,
2325 ) -> Result<Self, ParseError<'i>> {
2326 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
2327 Self::parse_quirky_internal(
2328 context,
2329 input,
2330 allow_quirks,
2331 allow_webkit_fill_available,
2332 ParseAnchorFunctions::Yes,
2333 )
2334 }
2335
2336 pub fn parse_size_for_width_or_height<'i, 't>(
2342 context: &ParserContext,
2343 input: &mut Parser<'i, 't>,
2344 ) -> Result<Self, ParseError<'i>> {
2345 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
2346 Self::parse_quirky_internal(
2347 context,
2348 input,
2349 AllowQuirks::No,
2350 allow_webkit_fill_available,
2351 ParseAnchorFunctions::Yes,
2352 )
2353 }
2354
2355 #[inline]
2357 pub fn zero_percent() -> Self {
2358 GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
2359 }
2360}
2361
2362pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
2364
2365impl Parse for MaxSize {
2366 fn parse<'i, 't>(
2367 context: &ParserContext,
2368 input: &mut Parser<'i, 't>,
2369 ) -> Result<Self, ParseError<'i>> {
2370 MaxSize::parse_quirky(context, input, AllowQuirks::No)
2371 }
2372}
2373
2374impl MaxSize {
2375 pub fn parse_quirky<'i, 't>(
2377 context: &ParserContext,
2378 input: &mut Parser<'i, 't>,
2379 allow_quirks: AllowQuirks,
2380 ) -> Result<Self, ParseError<'i>> {
2381 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
2382 parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
2383 "none" => None);
2384 parse_fit_content_function!(MaxSize, input, context, allow_quirks);
2385
2386 match input
2387 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2388 {
2389 Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
2390 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2391 return Err(e.into())
2392 },
2393 Err(_) => (),
2394 };
2395 if let Ok(length) = input.try_parse(|i| {
2396 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2397 context,
2398 i,
2399 allow_quirks,
2400 )
2401 }) {
2402 return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
2403 }
2404 Ok(Self::AnchorSizeFunction(Box::new(
2405 GenericAnchorSizeFunction::parse(context, input)?,
2406 )))
2407 }
2408}
2409
2410pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
2412
2413pub type Margin = GenericMargin<LengthPercentage>;
2415
2416impl Margin {
2417 #[inline]
2420 pub fn parse_quirky<'i, 't>(
2421 context: &ParserContext,
2422 input: &mut Parser<'i, 't>,
2423 allow_quirks: AllowQuirks,
2424 ) -> Result<Self, ParseError<'i>> {
2425 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2426 {
2427 return Ok(Self::LengthPercentage(l));
2428 }
2429 match input.try_parse(|i| i.expect_ident_matching("auto")) {
2430 Ok(_) => return Ok(Self::Auto),
2431 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2432 return Err(e.into())
2433 },
2434 Err(_) => (),
2435 };
2436 if let Ok(l) = input.try_parse(|i| {
2437 LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2438 }) {
2439 return Ok(Self::AnchorContainingCalcFunction(l));
2440 }
2441 let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
2442 Ok(Self::AnchorSizeFunction(Box::new(inner)))
2443 }
2444}
2445
2446impl Parse for Margin {
2447 fn parse<'i, 't>(
2448 context: &ParserContext,
2449 input: &mut Parser<'i, 't>,
2450 ) -> Result<Self, ParseError<'i>> {
2451 Self::parse_quirky(context, input, AllowQuirks::No)
2452 }
2453}