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