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 let reference_font_size = base_size.resolve(context);
372 match *self {
373 Self::Em(length) => {
375 if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
376 context
377 .rule_cache_conditions
378 .borrow_mut()
379 .set_font_size_dependency(reference_font_size.computed_size);
380 }
381
382 (reference_font_size.computed_size(), length)
383 },
384 Self::Lh(length) => {
385 let reference_size = if context.in_media_query {
391 context
392 .device()
393 .calc_line_height(
394 &context.default_style().get_font(),
395 context.style().writing_mode,
396 None,
397 )
398 .0
399 } else {
400 let line_height = context.builder.calc_line_height(
401 context.device(),
402 line_height_base,
403 context.style().writing_mode,
404 );
405 if context.for_non_inherited_property
406 && line_height_base == LineHeightBase::CurrentStyle
407 {
408 context
409 .rule_cache_conditions
410 .borrow_mut()
411 .set_line_height_dependency(line_height)
412 }
413 line_height.0
414 };
415 (reference_size, length)
416 },
417 Self::Ex(length) => (ex_size(context, base_size, &reference_font_size), length),
418 Self::Ch(length) => (ch_size(context, base_size, &reference_font_size), length),
419 Self::Cap(length) => (cap_size(context, base_size), length),
420 Self::Ic(length) => (ic_size(context, base_size, &reference_font_size), length),
421
422 Self::Rex(length) => {
424 let reference_size = if context.builder.is_root_element || context.in_media_query {
425 ex_size(context, base_size, &reference_font_size)
426 } else {
427 context
428 .device()
429 .root_font_metrics_ex()
430 .zoom(context.builder.effective_zoom)
431 };
432 (reference_size, length)
433 },
434 Self::Rch(length) => {
435 let reference_size = if context.builder.is_root_element || context.in_media_query {
436 ch_size(context, base_size, &reference_font_size)
437 } else {
438 context
439 .device()
440 .root_font_metrics_ch()
441 .zoom(context.builder.effective_zoom)
442 };
443 (reference_size, length)
444 },
445 Self::Rcap(length) => {
446 let reference_size = if context.builder.is_root_element || context.in_media_query {
447 cap_size(context, base_size)
448 } else {
449 context
450 .device()
451 .root_font_metrics_cap()
452 .zoom(context.builder.effective_zoom)
453 };
454 (reference_size, length)
455 },
456 Self::Ric(length) => {
457 let reference_size = if context.builder.is_root_element || context.in_media_query {
458 ic_size(context, base_size, &reference_font_size)
459 } else {
460 context
461 .device()
462 .root_font_metrics_ic()
463 .zoom(context.builder.effective_zoom)
464 };
465 (reference_size, length)
466 },
467 Self::Rem(length) => {
468 let reference_size = if context.builder.is_root_element || context.in_media_query {
475 reference_font_size.computed_size()
476 } else {
477 context
478 .device()
479 .root_font_size()
480 .zoom(context.builder.effective_zoom)
481 };
482 (reference_size, length)
483 },
484 Self::Rlh(length) => {
485 let reference_size = if context.builder.is_root_element {
491 context
492 .builder
493 .calc_line_height(
494 context.device(),
495 line_height_base,
496 context.style().writing_mode,
497 )
498 .0
499 } else if context.in_media_query {
500 context
501 .device()
502 .calc_line_height(
503 &context.default_style().get_font(),
504 context.style().writing_mode,
505 None,
506 )
507 .0
508 } else {
509 context.device().root_line_height()
510 };
511 let reference_size = reference_size.zoom(context.builder.effective_zoom);
512 (reference_size, length)
513 },
514 }
515 }
516}
517
518pub enum ViewportVariant {
520 UADefault,
522 Small,
524 Large,
526 Dynamic,
528}
529
530#[derive(PartialEq)]
532enum ViewportUnit {
533 Vw,
535 Vh,
537 Vmin,
539 Vmax,
541 Vb,
543 Vi,
545}
546
547#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
551#[repr(u8)]
552pub enum ViewportPercentageLength {
553 #[css(dimension)]
555 Vw(CSSFloat),
556 #[css(dimension)]
558 Svw(CSSFloat),
559 #[css(dimension)]
561 Lvw(CSSFloat),
562 #[css(dimension)]
564 Dvw(CSSFloat),
565 #[css(dimension)]
567 Vh(CSSFloat),
568 #[css(dimension)]
570 Svh(CSSFloat),
571 #[css(dimension)]
573 Lvh(CSSFloat),
574 #[css(dimension)]
576 Dvh(CSSFloat),
577 #[css(dimension)]
579 Vmin(CSSFloat),
580 #[css(dimension)]
582 Svmin(CSSFloat),
583 #[css(dimension)]
585 Lvmin(CSSFloat),
586 #[css(dimension)]
588 Dvmin(CSSFloat),
589 #[css(dimension)]
591 Vmax(CSSFloat),
592 #[css(dimension)]
594 Svmax(CSSFloat),
595 #[css(dimension)]
597 Lvmax(CSSFloat),
598 #[css(dimension)]
600 Dvmax(CSSFloat),
601 #[css(dimension)]
603 Vb(CSSFloat),
604 #[css(dimension)]
606 Svb(CSSFloat),
607 #[css(dimension)]
609 Lvb(CSSFloat),
610 #[css(dimension)]
612 Dvb(CSSFloat),
613 #[css(dimension)]
615 Vi(CSSFloat),
616 #[css(dimension)]
618 Svi(CSSFloat),
619 #[css(dimension)]
621 Lvi(CSSFloat),
622 #[css(dimension)]
624 Dvi(CSSFloat),
625}
626
627impl ViewportPercentageLength {
628 fn unitless_value(&self) -> CSSFloat {
630 self.unpack().2
631 }
632
633 fn unit(&self) -> &'static str {
635 match *self {
636 Self::Vw(_) => "vw",
637 Self::Lvw(_) => "lvw",
638 Self::Svw(_) => "svw",
639 Self::Dvw(_) => "dvw",
640 Self::Vh(_) => "vh",
641 Self::Svh(_) => "svh",
642 Self::Lvh(_) => "lvh",
643 Self::Dvh(_) => "dvh",
644 Self::Vmin(_) => "vmin",
645 Self::Svmin(_) => "svmin",
646 Self::Lvmin(_) => "lvmin",
647 Self::Dvmin(_) => "dvmin",
648 Self::Vmax(_) => "vmax",
649 Self::Svmax(_) => "svmax",
650 Self::Lvmax(_) => "lvmax",
651 Self::Dvmax(_) => "dvmax",
652 Self::Vb(_) => "vb",
653 Self::Svb(_) => "svb",
654 Self::Lvb(_) => "lvb",
655 Self::Dvb(_) => "dvb",
656 Self::Vi(_) => "vi",
657 Self::Svi(_) => "svi",
658 Self::Lvi(_) => "lvi",
659 Self::Dvi(_) => "dvi",
660 }
661 }
662
663 fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
664 match *self {
665 Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v),
666 Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v),
667 Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v),
668 Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v),
669 Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v),
670 Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v),
671 Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v),
672 Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v),
673 Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v),
674 Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v),
675 Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v),
676 Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v),
677 Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v),
678 Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v),
679 Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v),
680 Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v),
681 Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v),
682 Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v),
683 Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v),
684 Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v),
685 Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v),
686 Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v),
687 Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v),
688 Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v),
689 }
690 }
691
692 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
693 where
694 O: Fn(f32, f32) -> f32,
695 {
696 use self::ViewportPercentageLength::*;
697
698 if std::mem::discriminant(self) != std::mem::discriminant(other) {
699 return Err(());
700 }
701
702 Ok(match (self, other) {
703 (&Vw(one), &Vw(other)) => Vw(op(one, other)),
704 (&Svw(one), &Svw(other)) => Svw(op(one, other)),
705 (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),
706 (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),
707 (&Vh(one), &Vh(other)) => Vh(op(one, other)),
708 (&Svh(one), &Svh(other)) => Svh(op(one, other)),
709 (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),
710 (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),
711 (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),
712 (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),
713 (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),
714 (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),
715 (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),
716 (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),
717 (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),
718 (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),
719 (&Vb(one), &Vb(other)) => Vb(op(one, other)),
720 (&Svb(one), &Svb(other)) => Svb(op(one, other)),
721 (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),
722 (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),
723 (&Vi(one), &Vi(other)) => Vi(op(one, other)),
724 (&Svi(one), &Svi(other)) => Svi(op(one, other)),
725 (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),
726 (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),
727 _ => unsafe {
730 match *self {
731 Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
732 | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
733 | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
734 | Svi(..) | Lvi(..) | Dvi(..) => {},
735 }
736 debug_unreachable!("Forgot to handle unit in try_op()")
737 },
738 })
739 }
740
741 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
742 match self {
743 Self::Vw(x) => Self::Vw(op(*x)),
744 Self::Svw(x) => Self::Svw(op(*x)),
745 Self::Lvw(x) => Self::Lvw(op(*x)),
746 Self::Dvw(x) => Self::Dvw(op(*x)),
747 Self::Vh(x) => Self::Vh(op(*x)),
748 Self::Svh(x) => Self::Svh(op(*x)),
749 Self::Lvh(x) => Self::Lvh(op(*x)),
750 Self::Dvh(x) => Self::Dvh(op(*x)),
751 Self::Vmin(x) => Self::Vmin(op(*x)),
752 Self::Svmin(x) => Self::Svmin(op(*x)),
753 Self::Lvmin(x) => Self::Lvmin(op(*x)),
754 Self::Dvmin(x) => Self::Dvmin(op(*x)),
755 Self::Vmax(x) => Self::Vmax(op(*x)),
756 Self::Svmax(x) => Self::Svmax(op(*x)),
757 Self::Lvmax(x) => Self::Lvmax(op(*x)),
758 Self::Dvmax(x) => Self::Dvmax(op(*x)),
759 Self::Vb(x) => Self::Vb(op(*x)),
760 Self::Svb(x) => Self::Svb(op(*x)),
761 Self::Lvb(x) => Self::Lvb(op(*x)),
762 Self::Dvb(x) => Self::Dvb(op(*x)),
763 Self::Vi(x) => Self::Vi(op(*x)),
764 Self::Svi(x) => Self::Svi(op(*x)),
765 Self::Lvi(x) => Self::Lvi(op(*x)),
766 Self::Dvi(x) => Self::Dvi(op(*x)),
767 }
768 }
769
770 pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
772 let (variant, unit, factor) = self.unpack();
773 let size = context.viewport_size_for_viewport_unit_resolution(variant);
774 let length: app_units::Au = match unit {
775 ViewportUnit::Vw => size.width,
776 ViewportUnit::Vh => size.height,
777 ViewportUnit::Vmin => cmp::min(size.width, size.height),
778 ViewportUnit::Vmax => cmp::max(size.width, size.height),
779 ViewportUnit::Vi | ViewportUnit::Vb => {
780 context
781 .rule_cache_conditions
782 .borrow_mut()
783 .set_writing_mode_dependency(context.builder.writing_mode);
784 if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
785 size.width
786 } else {
787 size.height
788 }
789 },
790 };
791
792 let length = context.builder.effective_zoom.zoom(length.0 as f32);
794
795 let trunc_scaled =
800 ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
801 CSSPixelLength::new(crate::values::normalize(trunc_scaled))
802 }
803}
804
805#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
807#[repr(C)]
808pub struct CharacterWidth(pub i32);
809
810impl CharacterWidth {
811 pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {
813 let average_advance = reference_font_size * 0.5;
818 let max_advance = reference_font_size;
819 (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite()
820 }
821}
822
823#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
825#[repr(u8)]
826pub enum AbsoluteLength {
827 #[css(dimension)]
829 Px(CSSFloat),
830 #[css(dimension)]
832 In(CSSFloat),
833 #[css(dimension)]
835 Cm(CSSFloat),
836 #[css(dimension)]
838 Mm(CSSFloat),
839 #[css(dimension)]
841 Q(CSSFloat),
842 #[css(dimension)]
844 Pt(CSSFloat),
845 #[css(dimension)]
847 Pc(CSSFloat),
848}
849
850impl AbsoluteLength {
851 fn unitless_value(&self) -> CSSFloat {
853 match *self {
854 Self::Px(v)
855 | Self::In(v)
856 | Self::Cm(v)
857 | Self::Mm(v)
858 | Self::Q(v)
859 | Self::Pt(v)
860 | Self::Pc(v) => v,
861 }
862 }
863
864 fn unit(&self) -> &'static str {
866 match *self {
867 Self::Px(_) => "px",
868 Self::In(_) => "in",
869 Self::Cm(_) => "cm",
870 Self::Mm(_) => "mm",
871 Self::Q(_) => "q",
872 Self::Pt(_) => "pt",
873 Self::Pc(_) => "pc",
874 }
875 }
876
877 fn canonical_unit(&self) -> Option<&'static str> {
879 Some("px")
880 }
881
882 fn to(&self, unit: &str) -> Result<Self, ()> {
884 let px = self.to_px();
885
886 Ok(match_ignore_ascii_case! { unit,
887 "px" => Self::Px(px),
888 "in" => Self::In(px / PX_PER_IN),
889 "cm" => Self::Cm(px / PX_PER_CM),
890 "mm" => Self::Mm(px / PX_PER_MM),
891 "q" => Self::Q(px / PX_PER_Q),
892 "pt" => Self::Pt(px / PX_PER_PT),
893 "pc" => Self::Pc(px / PX_PER_PC),
894 _ => return Err(()),
895 })
896 }
897
898 #[inline]
900 pub fn to_px(&self) -> CSSFloat {
901 match *self {
902 Self::Px(value) => value,
903 Self::In(value) => value * PX_PER_IN,
904 Self::Cm(value) => value * PX_PER_CM,
905 Self::Mm(value) => value * PX_PER_MM,
906 Self::Q(value) => value * PX_PER_Q,
907 Self::Pt(value) => value * PX_PER_PT,
908 Self::Pc(value) => value * PX_PER_PC,
909 }
910 }
911
912 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
913 where
914 O: Fn(f32, f32) -> f32,
915 {
916 Ok(Self::Px(op(self.to_px(), other.to_px())))
917 }
918
919 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
920 Self::Px(op(self.to_px()))
921 }
922}
923
924impl ToComputedValue for AbsoluteLength {
925 type ComputedValue = CSSPixelLength;
926
927 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
928 CSSPixelLength::new(self.to_px())
929 .zoom(context.builder.effective_zoom)
930 .finite()
931 }
932
933 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
934 Self::Px(computed.px())
935 }
936}
937
938impl PartialOrd for AbsoluteLength {
939 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
940 self.to_px().partial_cmp(&other.to_px())
941 }
942}
943
944#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
948#[repr(u8)]
949pub enum ContainerRelativeLength {
950 #[css(dimension)]
952 Cqw(CSSFloat),
953 #[css(dimension)]
955 Cqh(CSSFloat),
956 #[css(dimension)]
958 Cqi(CSSFloat),
959 #[css(dimension)]
961 Cqb(CSSFloat),
962 #[css(dimension)]
964 Cqmin(CSSFloat),
965 #[css(dimension)]
967 Cqmax(CSSFloat),
968}
969
970impl ContainerRelativeLength {
971 fn unitless_value(&self) -> CSSFloat {
972 match *self {
973 Self::Cqw(v)
974 | Self::Cqh(v)
975 | Self::Cqi(v)
976 | Self::Cqb(v)
977 | Self::Cqmin(v)
978 | Self::Cqmax(v) => v,
979 }
980 }
981
982 fn unit(&self) -> &'static str {
984 match *self {
985 Self::Cqw(_) => "cqw",
986 Self::Cqh(_) => "cqh",
987 Self::Cqi(_) => "cqi",
988 Self::Cqb(_) => "cqb",
989 Self::Cqmin(_) => "cqmin",
990 Self::Cqmax(_) => "cqmax",
991 }
992 }
993
994 pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
995 where
996 O: Fn(f32, f32) -> f32,
997 {
998 use self::ContainerRelativeLength::*;
999
1000 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1001 return Err(());
1002 }
1003
1004 Ok(match (self, other) {
1005 (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
1006 (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
1007 (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
1008 (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
1009 (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
1010 (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),
1011
1012 _ => unsafe {
1016 match *self {
1017 Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1018 }
1019 debug_unreachable!("Forgot to handle unit in try_op()")
1020 },
1021 })
1022 }
1023
1024 pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1025 match self {
1026 Self::Cqw(x) => Self::Cqw(op(*x)),
1027 Self::Cqh(x) => Self::Cqh(op(*x)),
1028 Self::Cqi(x) => Self::Cqi(op(*x)),
1029 Self::Cqb(x) => Self::Cqb(op(*x)),
1030 Self::Cqmin(x) => Self::Cqmin(op(*x)),
1031 Self::Cqmax(x) => Self::Cqmax(op(*x)),
1032 }
1033 }
1034
1035 pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
1037 if context.for_non_inherited_property {
1038 context.rule_cache_conditions.borrow_mut().set_uncacheable();
1039 }
1040 context
1041 .builder
1042 .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
1043
1044 let size = context.get_container_size_query();
1048 let (factor, container_length) = match *self {
1049 Self::Cqw(v) => (v, size.get_container_width(context)),
1050 Self::Cqh(v) => (v, size.get_container_height(context)),
1051 Self::Cqi(v) => (v, size.get_container_inline_size(context)),
1052 Self::Cqb(v) => (v, size.get_container_block_size(context)),
1053 Self::Cqmin(v) => (
1054 v,
1055 cmp::min(
1056 size.get_container_inline_size(context),
1057 size.get_container_block_size(context),
1058 ),
1059 ),
1060 Self::Cqmax(v) => (
1061 v,
1062 cmp::max(
1063 size.get_container_inline_size(context),
1064 size.get_container_block_size(context),
1065 ),
1066 ),
1067 };
1068 CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
1069 }
1070}
1071
1072#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
1076#[repr(u8)]
1077pub enum NoCalcLength {
1078 Absolute(AbsoluteLength),
1082
1083 FontRelative(FontRelativeLength),
1087
1088 ViewportPercentage(ViewportPercentageLength),
1092
1093 ContainerRelative(ContainerRelativeLength),
1097 ServoCharacterWidth(CharacterWidth),
1102}
1103
1104impl NoCalcLength {
1105 pub fn unitless_value(&self) -> CSSFloat {
1107 match *self {
1108 Self::Absolute(v) => v.unitless_value(),
1109 Self::FontRelative(v) => v.unitless_value(),
1110 Self::ViewportPercentage(v) => v.unitless_value(),
1111 Self::ContainerRelative(v) => v.unitless_value(),
1112 Self::ServoCharacterWidth(c) => c.0 as f32,
1113 }
1114 }
1115
1116 pub fn unit(&self) -> &'static str {
1118 match *self {
1119 Self::Absolute(v) => v.unit(),
1120 Self::FontRelative(v) => v.unit(),
1121 Self::ViewportPercentage(v) => v.unit(),
1122 Self::ContainerRelative(v) => v.unit(),
1123 Self::ServoCharacterWidth(_) => "",
1124 }
1125 }
1126
1127 pub fn canonical_unit(&self) -> Option<&'static str> {
1129 match *self {
1130 Self::Absolute(v) => v.canonical_unit(),
1131 _ => None,
1132 }
1133 }
1134
1135 pub fn to(&self, unit: &str) -> Result<Self, ()> {
1137 match self {
1138 Self::Absolute(v) => Ok(Self::Absolute(v.to(unit)?)),
1139 _ => Err(()),
1140 }
1141 }
1142
1143 pub fn is_negative(&self) -> bool {
1145 self.unitless_value().is_sign_negative()
1146 }
1147
1148 pub fn is_zero(&self) -> bool {
1150 self.unitless_value() == 0.0
1151 }
1152
1153 pub fn is_infinite(&self) -> bool {
1155 self.unitless_value().is_infinite()
1156 }
1157
1158 pub fn is_nan(&self) -> bool {
1160 self.unitless_value().is_nan()
1161 }
1162
1163 pub fn should_zoom_text(&self) -> bool {
1168 match *self {
1169 Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,
1170 Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,
1171 }
1172 }
1173
1174 pub fn parse_dimension_with_flags(
1176 parsing_mode: ParsingMode,
1177 in_page_rule: bool,
1178 value: CSSFloat,
1179 unit: &str,
1180 ) -> Result<Self, ()> {
1181 let allows_computational_dependence = parsing_mode.allows_computational_dependence();
1182
1183 Ok(match_ignore_ascii_case! { unit,
1184 "px" => Self::Absolute(AbsoluteLength::Px(value)),
1185 "in" => Self::Absolute(AbsoluteLength::In(value)),
1186 "cm" => Self::Absolute(AbsoluteLength::Cm(value)),
1187 "mm" => Self::Absolute(AbsoluteLength::Mm(value)),
1188 "q" => Self::Absolute(AbsoluteLength::Q(value)),
1189 "pt" => Self::Absolute(AbsoluteLength::Pt(value)),
1190 "pc" => Self::Absolute(AbsoluteLength::Pc(value)),
1191 "em" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Em(value)),
1193 "ex" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ex(value)),
1194 "rex" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rex(value)),
1195 "ch" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ch(value)),
1196 "rch" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rch(value)),
1197 "cap" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Cap(value)),
1198 "rcap" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rcap(value)),
1199 "ic" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ic(value)),
1200 "ric" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ric(value)),
1201 "rem" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rem(value)),
1202 "lh" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Lh(value)),
1203 "rlh" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rlh(value)),
1204 "vw" if !in_page_rule => {
1206 Self::ViewportPercentage(ViewportPercentageLength::Vw(value))
1207 },
1208 "svw" if !in_page_rule => {
1209 Self::ViewportPercentage(ViewportPercentageLength::Svw(value))
1210 },
1211 "lvw" if !in_page_rule => {
1212 Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))
1213 },
1214 "dvw" if !in_page_rule => {
1215 Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))
1216 },
1217 "vh" if !in_page_rule => {
1218 Self::ViewportPercentage(ViewportPercentageLength::Vh(value))
1219 },
1220 "svh" if !in_page_rule => {
1221 Self::ViewportPercentage(ViewportPercentageLength::Svh(value))
1222 },
1223 "lvh" if !in_page_rule => {
1224 Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))
1225 },
1226 "dvh" if !in_page_rule => {
1227 Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))
1228 },
1229 "vmin" if !in_page_rule => {
1230 Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))
1231 },
1232 "svmin" if !in_page_rule => {
1233 Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))
1234 },
1235 "lvmin" if !in_page_rule => {
1236 Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))
1237 },
1238 "dvmin" if !in_page_rule => {
1239 Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))
1240 },
1241 "vmax" if !in_page_rule => {
1242 Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))
1243 },
1244 "svmax" if !in_page_rule => {
1245 Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))
1246 },
1247 "lvmax" if !in_page_rule => {
1248 Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))
1249 },
1250 "dvmax" if !in_page_rule => {
1251 Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))
1252 },
1253 "vb" if !in_page_rule => {
1254 Self::ViewportPercentage(ViewportPercentageLength::Vb(value))
1255 },
1256 "svb" if !in_page_rule => {
1257 Self::ViewportPercentage(ViewportPercentageLength::Svb(value))
1258 },
1259 "lvb" if !in_page_rule => {
1260 Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))
1261 },
1262 "dvb" if !in_page_rule => {
1263 Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))
1264 },
1265 "vi" if !in_page_rule => {
1266 Self::ViewportPercentage(ViewportPercentageLength::Vi(value))
1267 },
1268 "svi" if !in_page_rule => {
1269 Self::ViewportPercentage(ViewportPercentageLength::Svi(value))
1270 },
1271 "lvi" if !in_page_rule => {
1272 Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))
1273 },
1274 "dvi" if !in_page_rule => {
1275 Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))
1276 },
1277 "cqw" if !in_page_rule && cfg!(feature = "gecko") => {
1280 Self::ContainerRelative(ContainerRelativeLength::Cqw(value))
1281 },
1282 "cqh" if !in_page_rule && cfg!(feature = "gecko") => {
1283 Self::ContainerRelative(ContainerRelativeLength::Cqh(value))
1284 },
1285 "cqi" if !in_page_rule && cfg!(feature = "gecko") => {
1286 Self::ContainerRelative(ContainerRelativeLength::Cqi(value))
1287 },
1288 "cqb" if !in_page_rule && cfg!(feature = "gecko") => {
1289 Self::ContainerRelative(ContainerRelativeLength::Cqb(value))
1290 },
1291 "cqmin" if !in_page_rule && cfg!(feature = "gecko") => {
1292 Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))
1293 },
1294 "cqmax" if !in_page_rule && cfg!(feature = "gecko") => {
1295 Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))
1296 },
1297 _ => return Err(()),
1298 })
1299 }
1300
1301 pub fn parse_dimension_with_context(
1303 context: &ParserContext,
1304 value: CSSFloat,
1305 unit: &str,
1306 ) -> Result<Self, ()> {
1307 Self::parse_dimension_with_flags(context.parsing_mode, context.in_page_rule(), value, unit)
1308 }
1309
1310 pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
1311 where
1312 O: Fn(f32, f32) -> f32,
1313 {
1314 use self::NoCalcLength::*;
1315
1316 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1317 return Err(());
1318 }
1319
1320 Ok(match (self, other) {
1321 (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
1322 (&FontRelative(ref one), &FontRelative(ref other)) => {
1323 FontRelative(one.try_op(other, op)?)
1324 },
1325 (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1326 ViewportPercentage(one.try_op(other, op)?)
1327 },
1328 (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
1329 ContainerRelative(one.try_op(other, op)?)
1330 },
1331 (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1332 ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
1333 },
1334 _ => unsafe {
1337 match *self {
1338 Absolute(..)
1339 | FontRelative(..)
1340 | ViewportPercentage(..)
1341 | ContainerRelative(..)
1342 | ServoCharacterWidth(..) => {},
1343 }
1344 debug_unreachable!("Forgot to handle unit in try_op()")
1345 },
1346 })
1347 }
1348
1349 pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
1350 use self::NoCalcLength::*;
1351
1352 match self {
1353 Absolute(ref one) => Absolute(one.map(op)),
1354 FontRelative(ref one) => FontRelative(one.map(op)),
1355 ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),
1356 ContainerRelative(ref one) => ContainerRelative(one.map(op)),
1357 ServoCharacterWidth(ref one) => {
1358 ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))
1359 },
1360 }
1361 }
1362
1363 #[inline]
1365 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1366 match *self {
1367 Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1368 _ => Err(()),
1369 }
1370 }
1371
1372 #[cfg(feature = "gecko")]
1375 #[inline]
1376 pub fn to_computed_pixel_length_with_font_metrics(
1377 &self,
1378 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1379 ) -> Result<CSSFloat, ()> {
1380 match *self {
1381 Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
1382 Self::FontRelative(fr) => {
1383 if let Some(getter) = get_font_metrics {
1384 fr.to_computed_pixel_length_with_font_metrics(getter)
1385 } else {
1386 Err(())
1387 }
1388 },
1389 _ => Err(()),
1390 }
1391 }
1392
1393 #[inline]
1395 pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
1396 NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
1397 }
1398}
1399
1400impl ToCss for NoCalcLength {
1401 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1402 where
1403 W: Write,
1404 {
1405 crate::values::serialize_specified_dimension(
1406 self.unitless_value(),
1407 self.unit(),
1408 false,
1409 dest,
1410 )
1411 }
1412}
1413
1414impl ToTyped for NoCalcLength {
1415 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1416 let value = self.unitless_value();
1417 let unit = CssString::from(self.unit());
1418 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
1419 value,
1420 unit,
1421 })));
1422 Ok(())
1423 }
1424}
1425
1426impl SpecifiedValueInfo for NoCalcLength {}
1427
1428impl PartialOrd for NoCalcLength {
1429 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1430 use self::NoCalcLength::*;
1431
1432 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1433 return None;
1434 }
1435
1436 match (self, other) {
1437 (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
1438 (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
1439 (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
1440 one.partial_cmp(other)
1441 },
1442 (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),
1443 (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
1444 one.0.partial_cmp(&other.0)
1445 },
1446 _ => unsafe {
1449 match *self {
1450 Absolute(..)
1451 | FontRelative(..)
1452 | ViewportPercentage(..)
1453 | ContainerRelative(..)
1454 | ServoCharacterWidth(..) => {},
1455 }
1456 debug_unreachable!("Forgot an arm in partial_cmp?")
1457 },
1458 }
1459 }
1460}
1461
1462impl Zero for NoCalcLength {
1463 fn zero() -> Self {
1464 NoCalcLength::Absolute(AbsoluteLength::Px(0.))
1465 }
1466
1467 fn is_zero(&self) -> bool {
1468 NoCalcLength::is_zero(self)
1469 }
1470}
1471
1472#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1477#[typed_value(derive_fields)]
1478pub enum Length {
1479 NoCalc(NoCalcLength),
1481 Calc(Box<CalcLengthPercentage>),
1485}
1486
1487impl From<NoCalcLength> for Length {
1488 #[inline]
1489 fn from(len: NoCalcLength) -> Self {
1490 Length::NoCalc(len)
1491 }
1492}
1493
1494impl PartialOrd for FontRelativeLength {
1495 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1496 use self::FontRelativeLength::*;
1497
1498 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1499 return None;
1500 }
1501
1502 match (self, other) {
1503 (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
1504 (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
1505 (&Rex(ref one), &Rex(ref other)) => one.partial_cmp(other),
1506 (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
1507 (&Rch(ref one), &Rch(ref other)) => one.partial_cmp(other),
1508 (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
1509 (&Rcap(ref one), &Rcap(ref other)) => one.partial_cmp(other),
1510 (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
1511 (&Ric(ref one), &Ric(ref other)) => one.partial_cmp(other),
1512 (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
1513 (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other),
1514 (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other),
1515 _ => unsafe {
1518 match *self {
1519 Em(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) | Ic(..)
1520 | Ric(..) | Rem(..) | Lh(..) | Rlh(..) => {},
1521 }
1522 debug_unreachable!("Forgot an arm in partial_cmp?")
1523 },
1524 }
1525 }
1526}
1527
1528impl PartialOrd for ContainerRelativeLength {
1529 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1530 use self::ContainerRelativeLength::*;
1531
1532 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1533 return None;
1534 }
1535
1536 match (self, other) {
1537 (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),
1538 (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),
1539 (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),
1540 (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),
1541 (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),
1542 (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),
1543
1544 _ => unsafe {
1548 match *self {
1549 Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
1550 }
1551 debug_unreachable!("Forgot to handle unit in partial_cmp()")
1552 },
1553 }
1554 }
1555}
1556
1557impl PartialOrd for ViewportPercentageLength {
1558 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1559 use self::ViewportPercentageLength::*;
1560
1561 if std::mem::discriminant(self) != std::mem::discriminant(other) {
1562 return None;
1563 }
1564
1565 match (self, other) {
1566 (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
1567 (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),
1568 (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),
1569 (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),
1570 (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
1571 (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),
1572 (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),
1573 (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),
1574 (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
1575 (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),
1576 (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),
1577 (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),
1578 (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
1579 (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),
1580 (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),
1581 (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),
1582 (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),
1583 (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),
1584 (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),
1585 (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),
1586 (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),
1587 (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),
1588 (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),
1589 (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),
1590 _ => unsafe {
1593 match *self {
1594 Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)
1595 | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)
1596 | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)
1597 | Svi(..) | Lvi(..) | Dvi(..) => {},
1598 }
1599 debug_unreachable!("Forgot an arm in partial_cmp?")
1600 },
1601 }
1602 }
1603}
1604
1605impl Length {
1606 #[inline]
1607 fn parse_internal<'i, 't>(
1608 context: &ParserContext,
1609 input: &mut Parser<'i, 't>,
1610 num_context: AllowedNumericType,
1611 allow_quirks: AllowQuirks,
1612 ) -> Result<Self, ParseError<'i>> {
1613 let location = input.current_source_location();
1614 let token = input.next()?;
1615 match *token {
1616 Token::Dimension {
1617 value, ref unit, ..
1618 } if num_context.is_ok(context.parsing_mode, value) => {
1619 NoCalcLength::parse_dimension_with_context(context, value, unit)
1620 .map(Length::NoCalc)
1621 .map_err(|()| location.new_unexpected_token_error(token.clone()))
1622 },
1623 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1624 if value != 0.
1625 && !context.parsing_mode.allows_unitless_lengths()
1626 && !allow_quirks.allowed(context.quirks_mode)
1627 {
1628 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1629 }
1630 Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
1631 value,
1632 ))))
1633 },
1634 Token::Function(ref name) => {
1635 let function = CalcNode::math_function(context, name, location)?;
1636 let calc = CalcNode::parse_length(context, input, num_context, function)?;
1637 Ok(Length::Calc(Box::new(calc)))
1638 },
1639 ref token => return Err(location.new_unexpected_token_error(token.clone())),
1640 }
1641 }
1642
1643 #[inline]
1645 pub fn parse_non_negative<'i, 't>(
1646 context: &ParserContext,
1647 input: &mut Parser<'i, 't>,
1648 ) -> Result<Self, ParseError<'i>> {
1649 Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1650 }
1651
1652 #[inline]
1654 pub fn parse_non_negative_quirky<'i, 't>(
1655 context: &ParserContext,
1656 input: &mut Parser<'i, 't>,
1657 allow_quirks: AllowQuirks,
1658 ) -> Result<Self, ParseError<'i>> {
1659 Self::parse_internal(
1660 context,
1661 input,
1662 AllowedNumericType::NonNegative,
1663 allow_quirks,
1664 )
1665 }
1666
1667 #[inline]
1669 pub fn from_px(px_value: CSSFloat) -> Length {
1670 Length::NoCalc(NoCalcLength::from_px(px_value))
1671 }
1672
1673 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
1675 match *self {
1676 Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),
1677 Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
1678 }
1679 }
1680
1681 #[cfg(feature = "gecko")]
1683 pub fn to_computed_pixel_length_with_font_metrics(
1684 &self,
1685 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
1686 ) -> Result<CSSFloat, ()> {
1687 match *self {
1688 Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1689 Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
1690 }
1691 }
1692}
1693
1694impl Parse for Length {
1695 fn parse<'i, 't>(
1696 context: &ParserContext,
1697 input: &mut Parser<'i, 't>,
1698 ) -> Result<Self, ParseError<'i>> {
1699 Self::parse_quirky(context, input, AllowQuirks::No)
1700 }
1701}
1702
1703impl Zero for Length {
1704 fn zero() -> Self {
1705 Length::NoCalc(NoCalcLength::zero())
1706 }
1707
1708 fn is_zero(&self) -> bool {
1709 match *self {
1712 Length::NoCalc(ref l) => l.is_zero(),
1713 Length::Calc(..) => false,
1714 }
1715 }
1716}
1717
1718impl Length {
1719 pub fn parse_quirky<'i, 't>(
1721 context: &ParserContext,
1722 input: &mut Parser<'i, 't>,
1723 allow_quirks: AllowQuirks,
1724 ) -> Result<Self, ParseError<'i>> {
1725 Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1726 }
1727}
1728
1729pub type NonNegativeLength = NonNegative<Length>;
1731
1732impl Parse for NonNegativeLength {
1733 #[inline]
1734 fn parse<'i, 't>(
1735 context: &ParserContext,
1736 input: &mut Parser<'i, 't>,
1737 ) -> Result<Self, ParseError<'i>> {
1738 Ok(NonNegative(Length::parse_non_negative(context, input)?))
1739 }
1740}
1741
1742impl From<NoCalcLength> for NonNegativeLength {
1743 #[inline]
1744 fn from(len: NoCalcLength) -> Self {
1745 NonNegative(Length::NoCalc(len))
1746 }
1747}
1748
1749impl From<Length> for NonNegativeLength {
1750 #[inline]
1751 fn from(len: Length) -> Self {
1752 NonNegative(len)
1753 }
1754}
1755
1756impl NonNegativeLength {
1757 #[inline]
1759 pub fn from_px(px_value: CSSFloat) -> Self {
1760 Length::from_px(px_value.max(0.)).into()
1761 }
1762
1763 #[inline]
1765 pub fn parse_quirky<'i, 't>(
1766 context: &ParserContext,
1767 input: &mut Parser<'i, 't>,
1768 allow_quirks: AllowQuirks,
1769 ) -> Result<Self, ParseError<'i>> {
1770 Ok(NonNegative(Length::parse_non_negative_quirky(
1771 context,
1772 input,
1773 allow_quirks,
1774 )?))
1775 }
1776}
1777
1778#[allow(missing_docs)]
1783#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1784#[typed_value(derive_fields)]
1785pub enum LengthPercentage {
1786 Length(NoCalcLength),
1787 Percentage(computed::Percentage),
1788 Calc(Box<CalcLengthPercentage>),
1789}
1790
1791impl From<Length> for LengthPercentage {
1792 fn from(len: Length) -> LengthPercentage {
1793 match len {
1794 Length::NoCalc(l) => LengthPercentage::Length(l),
1795 Length::Calc(l) => LengthPercentage::Calc(l),
1796 }
1797 }
1798}
1799
1800impl From<NoCalcLength> for LengthPercentage {
1801 #[inline]
1802 fn from(len: NoCalcLength) -> Self {
1803 LengthPercentage::Length(len)
1804 }
1805}
1806
1807impl From<Percentage> for LengthPercentage {
1808 #[inline]
1809 fn from(pc: Percentage) -> Self {
1810 if let Some(clamping_mode) = pc.calc_clamping_mode() {
1811 LengthPercentage::Calc(Box::new(CalcLengthPercentage {
1812 clamping_mode,
1813 node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
1814 }))
1815 } else {
1816 LengthPercentage::Percentage(computed::Percentage(pc.get()))
1817 }
1818 }
1819}
1820
1821impl From<computed::Percentage> for LengthPercentage {
1822 #[inline]
1823 fn from(pc: computed::Percentage) -> Self {
1824 LengthPercentage::Percentage(pc)
1825 }
1826}
1827
1828impl Parse for LengthPercentage {
1829 #[inline]
1830 fn parse<'i, 't>(
1831 context: &ParserContext,
1832 input: &mut Parser<'i, 't>,
1833 ) -> Result<Self, ParseError<'i>> {
1834 Self::parse_quirky(context, input, AllowQuirks::No)
1835 }
1836}
1837
1838impl LengthPercentage {
1839 #[inline]
1840 pub fn zero_percent() -> LengthPercentage {
1842 LengthPercentage::Percentage(computed::Percentage::zero())
1843 }
1844
1845 #[inline]
1846 pub fn hundred_percent() -> LengthPercentage {
1848 LengthPercentage::Percentage(computed::Percentage::hundred())
1849 }
1850
1851 fn parse_internal<'i, 't>(
1852 context: &ParserContext,
1853 input: &mut Parser<'i, 't>,
1854 num_context: AllowedNumericType,
1855 allow_quirks: AllowQuirks,
1856 allow_anchor: AllowAnchorPositioningFunctions,
1857 ) -> Result<Self, ParseError<'i>> {
1858 let location = input.current_source_location();
1859 let token = input.next()?;
1860 match *token {
1861 Token::Dimension {
1862 value, ref unit, ..
1863 } if num_context.is_ok(context.parsing_mode, value) => {
1864 return NoCalcLength::parse_dimension_with_context(context, value, unit)
1865 .map(LengthPercentage::Length)
1866 .map_err(|()| location.new_unexpected_token_error(token.clone()));
1867 },
1868 Token::Percentage { unit_value, .. }
1869 if num_context.is_ok(context.parsing_mode, unit_value) =>
1870 {
1871 return Ok(LengthPercentage::Percentage(computed::Percentage(
1872 unit_value,
1873 )));
1874 },
1875 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1876 if value != 0.
1877 && !context.parsing_mode.allows_unitless_lengths()
1878 && !allow_quirks.allowed(context.quirks_mode)
1879 {
1880 return Err(location.new_unexpected_token_error(token.clone()));
1881 } else {
1882 return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
1883 }
1884 },
1885 Token::Function(ref name) => {
1886 let function = CalcNode::math_function(context, name, location)?;
1887 let calc = CalcNode::parse_length_or_percentage(
1888 context,
1889 input,
1890 num_context,
1891 function,
1892 allow_anchor,
1893 )?;
1894 Ok(LengthPercentage::Calc(Box::new(calc)))
1895 },
1896 _ => return Err(location.new_unexpected_token_error(token.clone())),
1897 }
1898 }
1899
1900 #[inline]
1903 pub fn parse_quirky<'i, 't>(
1904 context: &ParserContext,
1905 input: &mut Parser<'i, 't>,
1906 allow_quirks: AllowQuirks,
1907 ) -> Result<Self, ParseError<'i>> {
1908 Self::parse_internal(
1909 context,
1910 input,
1911 AllowedNumericType::All,
1912 allow_quirks,
1913 AllowAnchorPositioningFunctions::No,
1914 )
1915 }
1916
1917 #[inline]
1920 fn parse_quirky_with_anchor_size_function<'i, 't>(
1921 context: &ParserContext,
1922 input: &mut Parser<'i, 't>,
1923 allow_quirks: AllowQuirks,
1924 ) -> Result<Self, ParseError<'i>> {
1925 Self::parse_internal(
1926 context,
1927 input,
1928 AllowedNumericType::All,
1929 allow_quirks,
1930 AllowAnchorPositioningFunctions::AllowAnchorSize,
1931 )
1932 }
1933
1934 #[inline]
1937 pub fn parse_quirky_with_anchor_functions<'i, 't>(
1938 context: &ParserContext,
1939 input: &mut Parser<'i, 't>,
1940 allow_quirks: AllowQuirks,
1941 ) -> Result<Self, ParseError<'i>> {
1942 Self::parse_internal(
1943 context,
1944 input,
1945 AllowedNumericType::All,
1946 allow_quirks,
1947 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,
1948 )
1949 }
1950
1951 pub fn parse_non_negative_with_anchor_size<'i, 't>(
1954 context: &ParserContext,
1955 input: &mut Parser<'i, 't>,
1956 allow_quirks: AllowQuirks,
1957 ) -> Result<Self, ParseError<'i>> {
1958 Self::parse_internal(
1959 context,
1960 input,
1961 AllowedNumericType::NonNegative,
1962 allow_quirks,
1963 AllowAnchorPositioningFunctions::AllowAnchorSize,
1964 )
1965 }
1966
1967 #[inline]
1972 pub fn parse_non_negative<'i, 't>(
1973 context: &ParserContext,
1974 input: &mut Parser<'i, 't>,
1975 ) -> Result<Self, ParseError<'i>> {
1976 Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1977 }
1978
1979 #[inline]
1981 pub fn parse_non_negative_quirky<'i, 't>(
1982 context: &ParserContext,
1983 input: &mut Parser<'i, 't>,
1984 allow_quirks: AllowQuirks,
1985 ) -> Result<Self, ParseError<'i>> {
1986 Self::parse_internal(
1987 context,
1988 input,
1989 AllowedNumericType::NonNegative,
1990 allow_quirks,
1991 AllowAnchorPositioningFunctions::No,
1992 )
1993 }
1994
1995 fn to_calc_node(self) -> CalcNode {
1999 match self {
2000 LengthPercentage::Length(l) => CalcNode::Leaf(calc::Leaf::Length(l)),
2001 LengthPercentage::Percentage(p) => CalcNode::Leaf(calc::Leaf::Percentage(p.0)),
2002 LengthPercentage::Calc(p) => p.node,
2003 }
2004 }
2005
2006 pub fn hundred_percent_minus(self, clamping_mode: AllowedNumericType) -> Self {
2008 let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new();
2009 sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0)));
2010
2011 let mut node = self.to_calc_node();
2012 node.negate();
2013 sum.push(node);
2014
2015 let calc = CalcNode::Sum(sum.into_boxed_slice().into());
2016 LengthPercentage::Calc(Box::new(
2017 calc.into_length_or_percentage(clamping_mode).unwrap(),
2018 ))
2019 }
2020}
2021
2022impl Zero for LengthPercentage {
2023 fn zero() -> Self {
2024 LengthPercentage::Length(NoCalcLength::zero())
2025 }
2026
2027 fn is_zero(&self) -> bool {
2028 match *self {
2029 LengthPercentage::Length(l) => l.is_zero(),
2030 LengthPercentage::Percentage(p) => p.0 == 0.0,
2031 LengthPercentage::Calc(_) => false,
2032 }
2033 }
2034}
2035
2036impl ZeroNoPercent for LengthPercentage {
2037 fn is_zero_no_percent(&self) -> bool {
2038 match *self {
2039 LengthPercentage::Percentage(_) => false,
2040 _ => self.is_zero(),
2041 }
2042 }
2043}
2044
2045pub trait EqualsPercentage {
2047 fn equals_percentage(&self, v: CSSFloat) -> bool;
2050}
2051
2052impl EqualsPercentage for LengthPercentage {
2053 fn equals_percentage(&self, v: CSSFloat) -> bool {
2054 match *self {
2055 LengthPercentage::Percentage(p) => p.0 == v,
2056 _ => false,
2057 }
2058 }
2059}
2060
2061pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
2063
2064impl LengthPercentageOrAuto {
2065 #[inline]
2067 pub fn zero_percent() -> Self {
2068 generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
2069 }
2070
2071 #[inline]
2074 pub fn parse_quirky<'i, 't>(
2075 context: &ParserContext,
2076 input: &mut Parser<'i, 't>,
2077 allow_quirks: AllowQuirks,
2078 ) -> Result<Self, ParseError<'i>> {
2079 Self::parse_with(context, input, |context, input| {
2080 LengthPercentage::parse_quirky(context, input, allow_quirks)
2081 })
2082 }
2083}
2084
2085pub type NonNegativeLengthPercentageOrAuto =
2087 generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
2088
2089impl NonNegativeLengthPercentageOrAuto {
2090 #[inline]
2092 pub fn zero_percent() -> Self {
2093 generics::LengthPercentageOrAuto::LengthPercentage(
2094 NonNegativeLengthPercentage::zero_percent(),
2095 )
2096 }
2097
2098 #[inline]
2101 pub fn parse_quirky<'i, 't>(
2102 context: &ParserContext,
2103 input: &mut Parser<'i, 't>,
2104 allow_quirks: AllowQuirks,
2105 ) -> Result<Self, ParseError<'i>> {
2106 Self::parse_with(context, input, |context, input| {
2107 NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
2108 })
2109 }
2110}
2111
2112pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
2114
2115pub type NonNegativeLengthPercentageOrNormal =
2117 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
2118
2119impl From<NoCalcLength> for NonNegativeLengthPercentage {
2120 #[inline]
2121 fn from(len: NoCalcLength) -> Self {
2122 NonNegative(LengthPercentage::from(len))
2123 }
2124}
2125
2126impl Parse for NonNegativeLengthPercentage {
2127 #[inline]
2128 fn parse<'i, 't>(
2129 context: &ParserContext,
2130 input: &mut Parser<'i, 't>,
2131 ) -> Result<Self, ParseError<'i>> {
2132 Self::parse_quirky(context, input, AllowQuirks::No)
2133 }
2134}
2135
2136impl NonNegativeLengthPercentage {
2137 #[inline]
2138 pub fn zero_percent() -> Self {
2140 NonNegative(LengthPercentage::zero_percent())
2141 }
2142
2143 #[inline]
2146 pub fn parse_quirky<'i, 't>(
2147 context: &ParserContext,
2148 input: &mut Parser<'i, 't>,
2149 allow_quirks: AllowQuirks,
2150 ) -> Result<Self, ParseError<'i>> {
2151 LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
2152 }
2153
2154 #[inline]
2157 pub fn parse_non_negative_with_anchor_size<'i, 't>(
2158 context: &ParserContext,
2159 input: &mut Parser<'i, 't>,
2160 allow_quirks: AllowQuirks,
2161 ) -> Result<Self, ParseError<'i>> {
2162 LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)
2163 .map(NonNegative)
2164 }
2165}
2166
2167pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
2173
2174impl LengthOrAuto {
2175 #[inline]
2178 pub fn parse_quirky<'i, 't>(
2179 context: &ParserContext,
2180 input: &mut Parser<'i, 't>,
2181 allow_quirks: AllowQuirks,
2182 ) -> Result<Self, ParseError<'i>> {
2183 Self::parse_with(context, input, |context, input| {
2184 Length::parse_quirky(context, input, allow_quirks)
2185 })
2186 }
2187}
2188
2189pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
2191
2192pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
2194
2195pub type Size = GenericSize<NonNegativeLengthPercentage>;
2197
2198impl Parse for Size {
2199 fn parse<'i, 't>(
2200 context: &ParserContext,
2201 input: &mut Parser<'i, 't>,
2202 ) -> Result<Self, ParseError<'i>> {
2203 Size::parse_quirky(context, input, AllowQuirks::No)
2204 }
2205}
2206
2207macro_rules! parse_size_non_length {
2208 ($size:ident, $input:expr, $allow_webkit_fill_available:expr,
2209 $auto_or_none:expr => $auto_or_none_ident:ident) => {{
2210 let size = $input.try_parse(|input| {
2211 Ok(try_match_ident_ignore_ascii_case! { input,
2212 "min-content" | "-moz-min-content" => $size::MinContent,
2213 "max-content" | "-moz-max-content" => $size::MaxContent,
2214 "fit-content" | "-moz-fit-content" => $size::FitContent,
2215 #[cfg(feature = "gecko")]
2216 "-moz-available" => $size::MozAvailable,
2217 "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable,
2218 "stretch" if is_stretch_enabled() => $size::Stretch,
2219 $auto_or_none => $size::$auto_or_none_ident,
2220 })
2221 });
2222 if size.is_ok() {
2223 return size;
2224 }
2225 }};
2226}
2227
2228fn is_webkit_fill_available_enabled_in_width_and_height() -> bool {
2229 static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2230}
2231
2232fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {
2233 static_prefs::pref!("layout.css.webkit-fill-available.enabled")
2237 && static_prefs::pref!("layout.css.webkit-fill-available.all-size-properties.enabled")
2238}
2239
2240fn is_stretch_enabled() -> bool {
2241 static_prefs::pref!("layout.css.stretch-size-keyword.enabled")
2242}
2243
2244fn is_fit_content_function_enabled() -> bool {
2245 static_prefs::pref!("layout.css.fit-content-function.enabled")
2246}
2247
2248macro_rules! parse_fit_content_function {
2249 ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
2250 if is_fit_content_function_enabled() {
2251 if let Ok(length) = $input.try_parse(|input| {
2252 input.expect_function_matching("fit-content")?;
2253 input.parse_nested_block(|i| {
2254 NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
2255 })
2256 }) {
2257 return Ok($size::FitContentFunction(length));
2258 }
2259 }
2260 };
2261}
2262
2263#[derive(Clone, Copy, PartialEq, Eq)]
2264enum ParseAnchorFunctions {
2265 Yes,
2266 No,
2267}
2268
2269impl Size {
2270 pub fn parse_quirky<'i, 't>(
2272 context: &ParserContext,
2273 input: &mut Parser<'i, 't>,
2274 allow_quirks: AllowQuirks,
2275 ) -> Result<Self, ParseError<'i>> {
2276 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
2277 Self::parse_quirky_internal(
2278 context,
2279 input,
2280 allow_quirks,
2281 allow_webkit_fill_available,
2282 ParseAnchorFunctions::Yes,
2283 )
2284 }
2285
2286 pub fn parse_size_for_flex_basis_width<'i, 't>(
2288 context: &ParserContext,
2289 input: &mut Parser<'i, 't>,
2290 ) -> Result<Self, ParseError<'i>> {
2291 Self::parse_quirky_internal(
2292 context,
2293 input,
2294 AllowQuirks::No,
2295 true,
2296 ParseAnchorFunctions::No,
2297 )
2298 }
2299
2300 fn parse_quirky_internal<'i, 't>(
2305 context: &ParserContext,
2306 input: &mut Parser<'i, 't>,
2307 allow_quirks: AllowQuirks,
2308 allow_webkit_fill_available: bool,
2309 allow_anchor_functions: ParseAnchorFunctions,
2310 ) -> Result<Self, ParseError<'i>> {
2311 parse_size_non_length!(Size, input, allow_webkit_fill_available,
2312 "auto" => Auto);
2313 parse_fit_content_function!(Size, input, context, allow_quirks);
2314
2315 let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes
2316 && static_prefs::pref!("layout.css.anchor-positioning.enabled");
2317 match input
2318 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2319 {
2320 Ok(length) => return Ok(GenericSize::LengthPercentage(length)),
2321 Err(e) if !allow_anchor => return Err(e.into()),
2322 Err(_) => (),
2323 };
2324 if let Ok(length) = input.try_parse(|i| {
2325 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2326 context,
2327 i,
2328 allow_quirks,
2329 )
2330 }) {
2331 return Ok(GenericSize::AnchorContainingCalcFunction(length));
2332 }
2333 Ok(Self::AnchorSizeFunction(Box::new(
2334 GenericAnchorSizeFunction::parse(context, input)?,
2335 )))
2336 }
2337
2338 pub fn parse_size_for_width_or_height_quirky<'i, 't>(
2344 context: &ParserContext,
2345 input: &mut Parser<'i, 't>,
2346 allow_quirks: AllowQuirks,
2347 ) -> Result<Self, ParseError<'i>> {
2348 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
2349 Self::parse_quirky_internal(
2350 context,
2351 input,
2352 allow_quirks,
2353 allow_webkit_fill_available,
2354 ParseAnchorFunctions::Yes,
2355 )
2356 }
2357
2358 pub fn parse_size_for_width_or_height<'i, 't>(
2364 context: &ParserContext,
2365 input: &mut Parser<'i, 't>,
2366 ) -> Result<Self, ParseError<'i>> {
2367 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();
2368 Self::parse_quirky_internal(
2369 context,
2370 input,
2371 AllowQuirks::No,
2372 allow_webkit_fill_available,
2373 ParseAnchorFunctions::Yes,
2374 )
2375 }
2376
2377 #[inline]
2379 pub fn zero_percent() -> Self {
2380 GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
2381 }
2382}
2383
2384pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
2386
2387impl Parse for MaxSize {
2388 fn parse<'i, 't>(
2389 context: &ParserContext,
2390 input: &mut Parser<'i, 't>,
2391 ) -> Result<Self, ParseError<'i>> {
2392 MaxSize::parse_quirky(context, input, AllowQuirks::No)
2393 }
2394}
2395
2396impl MaxSize {
2397 pub fn parse_quirky<'i, 't>(
2399 context: &ParserContext,
2400 input: &mut Parser<'i, 't>,
2401 allow_quirks: AllowQuirks,
2402 ) -> Result<Self, ParseError<'i>> {
2403 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();
2404 parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,
2405 "none" => None);
2406 parse_fit_content_function!(MaxSize, input, context, allow_quirks);
2407
2408 match input
2409 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))
2410 {
2411 Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),
2412 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2413 return Err(e.into())
2414 },
2415 Err(_) => (),
2416 };
2417 if let Ok(length) = input.try_parse(|i| {
2418 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(
2419 context,
2420 i,
2421 allow_quirks,
2422 )
2423 }) {
2424 return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));
2425 }
2426 Ok(Self::AnchorSizeFunction(Box::new(
2427 GenericAnchorSizeFunction::parse(context, input)?,
2428 )))
2429 }
2430}
2431
2432pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
2434
2435pub type Margin = GenericMargin<LengthPercentage>;
2437
2438impl Margin {
2439 #[inline]
2442 pub fn parse_quirky<'i, 't>(
2443 context: &ParserContext,
2444 input: &mut Parser<'i, 't>,
2445 allow_quirks: AllowQuirks,
2446 ) -> Result<Self, ParseError<'i>> {
2447 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2448 {
2449 return Ok(Self::LengthPercentage(l));
2450 }
2451 match input.try_parse(|i| i.expect_ident_matching("auto")) {
2452 Ok(_) => return Ok(Self::Auto),
2453 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2454 return Err(e.into())
2455 },
2456 Err(_) => (),
2457 };
2458 if let Ok(l) = input.try_parse(|i| {
2459 LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)
2460 }) {
2461 return Ok(Self::AnchorContainingCalcFunction(l));
2462 }
2463 let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;
2464 Ok(Self::AnchorSizeFunction(Box::new(inner)))
2465 }
2466}
2467
2468impl Parse for Margin {
2469 fn parse<'i, 't>(
2470 context: &ParserContext,
2471 input: &mut Parser<'i, 't>,
2472 ) -> Result<Self, ParseError<'i>> {
2473 Self::parse_quirky(context, input, AllowQuirks::No)
2474 }
2475}