1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 //! [Length values][length].
6 //!
7 //! [length]: https://drafts.csswg.org/css-values/#lengths
8 
9 use super::{AllowQuirks, Number, Percentage, ToComputedValue};
10 use crate::computed_value_flags::ComputedValueFlags;
11 use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
12 use crate::parser::{Parse, ParserContext};
13 use crate::values::computed::{self, CSSPixelLength, Context};
14 use crate::values::generics::length as generics;
15 use crate::values::generics::length::{
16     GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
17 };
18 use crate::values::generics::NonNegative;
19 use crate::values::specified::calc::{self, CalcNode};
20 use crate::values::specified::NonNegativeNumber;
21 use crate::values::CSSFloat;
22 use crate::Zero;
23 use app_units::Au;
24 use cssparser::{Parser, Token};
25 use euclid::default::Size2D;
26 use std::cmp;
27 use std::ops::{Add, Mul};
28 use style_traits::values::specified::AllowedNumericType;
29 use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
30 
31 pub use super::image::Image;
32 pub use super::image::{EndingShape as GradientEndingShape, Gradient};
33 pub use crate::values::specified::calc::CalcLengthPercentage;
34 
35 /// Number of app units per pixel
36 pub const AU_PER_PX: CSSFloat = 60.;
37 /// Number of app units per inch
38 pub const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
39 /// Number of app units per centimeter
40 pub const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
41 /// Number of app units per millimeter
42 pub const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4;
43 /// Number of app units per quarter
44 pub const AU_PER_Q: CSSFloat = AU_PER_MM / 4.;
45 /// Number of app units per point
46 pub const AU_PER_PT: CSSFloat = AU_PER_IN / 72.;
47 /// Number of app units per pica
48 pub const AU_PER_PC: CSSFloat = AU_PER_PT * 12.;
49 
50 /// A font relative length.
51 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
52 pub enum FontRelativeLength {
53     /// A "em" value: https://drafts.csswg.org/css-values/#em
54     #[css(dimension)]
55     Em(CSSFloat),
56     /// A "ex" value: https://drafts.csswg.org/css-values/#ex
57     #[css(dimension)]
58     Ex(CSSFloat),
59     /// A "ch" value: https://drafts.csswg.org/css-values/#ch
60     #[css(dimension)]
61     Ch(CSSFloat),
62     /// A "cap" value: https://drafts.csswg.org/css-values/#cap
63     #[css(dimension)]
64     Cap(CSSFloat),
65     /// An "ic" value: https://drafts.csswg.org/css-values/#ic
66     #[css(dimension)]
67     Ic(CSSFloat),
68     /// A "rem" value: https://drafts.csswg.org/css-values/#rem
69     #[css(dimension)]
70     Rem(CSSFloat),
71 }
72 
73 /// A source to resolve font-relative units against
74 #[derive(Clone, Copy, Debug, PartialEq)]
75 pub enum FontBaseSize {
76     /// Use the font-size of the current element.
77     CurrentStyle,
78     /// Use the inherited font-size.
79     InheritedStyle,
80 }
81 
82 impl FontBaseSize {
83     /// Calculate the actual size for a given context
resolve(&self, context: &Context) -> computed::Length84     pub fn resolve(&self, context: &Context) -> computed::Length {
85         match *self {
86             FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().size(),
87             FontBaseSize::InheritedStyle => {
88                 context.style().get_parent_font().clone_font_size().size()
89             },
90         }
91     }
92 }
93 
94 impl FontRelativeLength {
95     /// Return true if this is a zero value.
is_zero(&self) -> bool96     fn is_zero(&self) -> bool {
97         match *self {
98             FontRelativeLength::Em(v) |
99             FontRelativeLength::Ex(v) |
100             FontRelativeLength::Ch(v) |
101             FontRelativeLength::Cap(v) |
102             FontRelativeLength::Ic(v) |
103             FontRelativeLength::Rem(v) => v == 0.,
104         }
105     }
106 
is_negative(&self) -> bool107     fn is_negative(&self) -> bool {
108         match *self {
109             FontRelativeLength::Em(v) |
110             FontRelativeLength::Ex(v) |
111             FontRelativeLength::Ch(v) |
112             FontRelativeLength::Cap(v) |
113             FontRelativeLength::Ic(v) |
114             FontRelativeLength::Rem(v) => v < 0.,
115         }
116     }
117 
try_sum(&self, other: &Self) -> Result<Self, ()>118     fn try_sum(&self, other: &Self) -> Result<Self, ()> {
119         use self::FontRelativeLength::*;
120 
121         if std::mem::discriminant(self) != std::mem::discriminant(other) {
122             return Err(());
123         }
124 
125         Ok(match (self, other) {
126             (&Em(one), &Em(other)) => Em(one + other),
127             (&Ex(one), &Ex(other)) => Ex(one + other),
128             (&Ch(one), &Ch(other)) => Ch(one + other),
129             (&Cap(one), &Cap(other)) => Cap(one + other),
130             (&Ic(one), &Ic(other)) => Ic(one + other),
131             (&Rem(one), &Rem(other)) => Rem(one + other),
132             // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
133             // able to figure it own on its own so we help.
134             _ => unsafe {
135                 match *self {
136                     Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {},
137                 }
138                 debug_unreachable!("Forgot to handle unit in try_sum()")
139             },
140         })
141     }
142 
143     /// Computes the font-relative length.
to_computed_value( &self, context: &Context, base_size: FontBaseSize, ) -> computed::Length144     pub fn to_computed_value(
145         &self,
146         context: &Context,
147         base_size: FontBaseSize,
148     ) -> computed::Length {
149         let (reference_size, length) = self.reference_font_size_and_length(context, base_size);
150         (reference_size * length).normalized()
151     }
152 
153     /// Return reference font size.
154     ///
155     /// We use the base_size flag to pass a different size for computing
156     /// font-size and unconstrained font-size.
157     ///
158     /// This returns a pair, the first one is the reference font size, and the
159     /// second one is the unpacked relative length.
reference_font_size_and_length( &self, context: &Context, base_size: FontBaseSize, ) -> (computed::Length, CSSFloat)160     fn reference_font_size_and_length(
161         &self,
162         context: &Context,
163         base_size: FontBaseSize,
164     ) -> (computed::Length, CSSFloat) {
165         fn query_font_metrics(
166             context: &Context,
167             base_size: FontBaseSize,
168             orientation: FontMetricsOrientation,
169         ) -> FontMetrics {
170             context
171                 .font_metrics_provider
172                 .query(context, base_size, orientation)
173         }
174 
175         let reference_font_size = base_size.resolve(context);
176         let font_metrics_flag = match base_size {
177             FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,
178             FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,
179         };
180         match *self {
181             FontRelativeLength::Em(length) => {
182                 if context.for_non_inherited_property.is_some() {
183                     if base_size == FontBaseSize::CurrentStyle {
184                         context
185                             .rule_cache_conditions
186                             .borrow_mut()
187                             .set_font_size_dependency(reference_font_size.into());
188                     }
189                 }
190 
191                 (reference_font_size, length)
192             },
193             FontRelativeLength::Ex(length) => {
194                 if context.for_non_inherited_property.is_some() {
195                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
196                 }
197                 context.builder.add_flags(font_metrics_flag);
198                 // The x-height is an intrinsically horizontal metric.
199                 let metrics =
200                     query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
201                 let reference_size = metrics.x_height.unwrap_or_else(|| {
202                     // https://drafts.csswg.org/css-values/#ex
203                     //
204                     //     In the cases where it is impossible or impractical to
205                     //     determine the x-height, a value of 0.5em must be
206                     //     assumed.
207                     //
208                     reference_font_size * 0.5
209                 });
210                 (reference_size, length)
211             },
212             FontRelativeLength::Ch(length) => {
213                 if context.for_non_inherited_property.is_some() {
214                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
215                 }
216                 context.builder.add_flags(font_metrics_flag);
217                 // https://drafts.csswg.org/css-values/#ch:
218                 //
219                 //     Equal to the used advance measure of the “0” (ZERO,
220                 //     U+0030) glyph in the font used to render it. (The advance
221                 //     measure of a glyph is its advance width or height,
222                 //     whichever is in the inline axis of the element.)
223                 //
224                 let metrics = query_font_metrics(
225                     context,
226                     base_size,
227                     FontMetricsOrientation::MatchContextPreferHorizontal,
228                 );
229                 let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| {
230                     // https://drafts.csswg.org/css-values/#ch
231                     //
232                     //     In the cases where it is impossible or impractical to
233                     //     determine the measure of the “0” glyph, it must be
234                     //     assumed to be 0.5em wide by 1em tall. Thus, the ch
235                     //     unit falls back to 0.5em in the general case, and to
236                     //     1em when it would be typeset upright (i.e.
237                     //     writing-mode is vertical-rl or vertical-lr and
238                     //     text-orientation is upright).
239                     //
240                     let wm = context.style().writing_mode;
241                     if wm.is_vertical() && wm.is_upright() {
242                         reference_font_size
243                     } else {
244                         reference_font_size * 0.5
245                     }
246                 });
247                 (reference_size, length)
248             },
249             FontRelativeLength::Cap(length) => {
250                 if context.for_non_inherited_property.is_some() {
251                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
252                 }
253                 context.builder.add_flags(font_metrics_flag);
254                 let metrics =
255                     query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
256                 let reference_size = metrics.cap_height.unwrap_or_else(|| {
257                     // https://drafts.csswg.org/css-values/#cap
258                     //
259                     //     In the cases where it is impossible or impractical to
260                     //     determine the cap-height, the font’s ascent must be used.
261                     //
262                     metrics.ascent
263                 });
264                 (reference_size, length)
265             },
266             FontRelativeLength::Ic(length) => {
267                 if context.for_non_inherited_property.is_some() {
268                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
269                 }
270                 context.builder.add_flags(font_metrics_flag);
271                 let metrics = query_font_metrics(
272                     context,
273                     base_size,
274                     FontMetricsOrientation::MatchContextPreferVertical,
275                 );
276                 let reference_size = metrics.ic_width.unwrap_or_else(|| {
277                     // https://drafts.csswg.org/css-values/#ic
278                     //
279                     //     In the cases where it is impossible or impractical to
280                     //     determine the ideographic advance measure, it must be
281                     //     assumed to be 1em.
282                     //
283                     reference_font_size
284                 });
285                 (reference_size, length)
286             },
287             FontRelativeLength::Rem(length) => {
288                 // https://drafts.csswg.org/css-values/#rem:
289                 //
290                 //     When specified on the font-size property of the root
291                 //     element, the rem units refer to the property's initial
292                 //     value.
293                 //
294                 let reference_size = if context.builder.is_root_element || context.in_media_query {
295                     reference_font_size
296                 } else {
297                     context.device().root_font_size()
298                 };
299                 (reference_size, length)
300             },
301         }
302     }
303 }
304 
305 /// A viewport-relative length.
306 ///
307 /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
308 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
309 pub enum ViewportPercentageLength {
310     /// A vw unit: https://drafts.csswg.org/css-values/#vw
311     #[css(dimension)]
312     Vw(CSSFloat),
313     /// A vh unit: https://drafts.csswg.org/css-values/#vh
314     #[css(dimension)]
315     Vh(CSSFloat),
316     /// <https://drafts.csswg.org/css-values/#vmin>
317     #[css(dimension)]
318     Vmin(CSSFloat),
319     /// <https://drafts.csswg.org/css-values/#vmax>
320     #[css(dimension)]
321     Vmax(CSSFloat),
322 }
323 
324 impl ViewportPercentageLength {
325     /// Return true if this is a zero value.
is_zero(&self) -> bool326     fn is_zero(&self) -> bool {
327         match *self {
328             ViewportPercentageLength::Vw(v) |
329             ViewportPercentageLength::Vh(v) |
330             ViewportPercentageLength::Vmin(v) |
331             ViewportPercentageLength::Vmax(v) => v == 0.,
332         }
333     }
334 
is_negative(&self) -> bool335     fn is_negative(&self) -> bool {
336         match *self {
337             ViewportPercentageLength::Vw(v) |
338             ViewportPercentageLength::Vh(v) |
339             ViewportPercentageLength::Vmin(v) |
340             ViewportPercentageLength::Vmax(v) => v < 0.,
341         }
342     }
343 
try_sum(&self, other: &Self) -> Result<Self, ()>344     fn try_sum(&self, other: &Self) -> Result<Self, ()> {
345         use self::ViewportPercentageLength::*;
346 
347         if std::mem::discriminant(self) != std::mem::discriminant(other) {
348             return Err(());
349         }
350 
351         Ok(match (self, other) {
352             (&Vw(one), &Vw(other)) => Vw(one + other),
353             (&Vh(one), &Vh(other)) => Vh(one + other),
354             (&Vmin(one), &Vmin(other)) => Vmin(one + other),
355             (&Vmax(one), &Vmax(other)) => Vmax(one + other),
356             // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
357             // able to figure it own on its own so we help.
358             _ => unsafe {
359                 match *self {
360                     Vw(..) | Vh(..) | Vmin(..) | Vmax(..) => {},
361                 }
362                 debug_unreachable!("Forgot to handle unit in try_sum()")
363             },
364         })
365     }
366 
367     /// Computes the given viewport-relative length for the given viewport size.
to_computed_value(&self, viewport_size: Size2D<Au>) -> CSSPixelLength368     pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> CSSPixelLength {
369         let (factor, length) = match *self {
370             ViewportPercentageLength::Vw(length) => (length, viewport_size.width),
371             ViewportPercentageLength::Vh(length) => (length, viewport_size.height),
372             ViewportPercentageLength::Vmin(length) => {
373                 (length, cmp::min(viewport_size.width, viewport_size.height))
374             },
375             ViewportPercentageLength::Vmax(length) => {
376                 (length, cmp::max(viewport_size.width, viewport_size.height))
377             },
378         };
379 
380         // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.
381         // See bug 989802. We truncate so that adding multiple viewport units
382         // that add up to 100 does not overflow due to rounding differences
383         let trunc_scaled = ((length.0 as f64) * factor as f64 / 100.).trunc();
384         Au::from_f64_au(trunc_scaled).into()
385     }
386 }
387 
388 /// HTML5 "character width", as defined in HTML5 § 14.5.4.
389 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
390 pub struct CharacterWidth(pub i32);
391 
392 impl CharacterWidth {
393     /// Computes the given character width.
to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length394     pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {
395         // This applies the *converting a character width to pixels* algorithm
396         // as specified in HTML5 § 14.5.4.
397         //
398         // TODO(pcwalton): Find these from the font.
399         let average_advance = reference_font_size * 0.5;
400         let max_advance = reference_font_size;
401         average_advance * (self.0 as CSSFloat - 1.0) + max_advance
402     }
403 }
404 
405 /// Represents an absolute length with its unit
406 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
407 pub enum AbsoluteLength {
408     /// An absolute length in pixels (px)
409     #[css(dimension)]
410     Px(CSSFloat),
411     /// An absolute length in inches (in)
412     #[css(dimension)]
413     In(CSSFloat),
414     /// An absolute length in centimeters (cm)
415     #[css(dimension)]
416     Cm(CSSFloat),
417     /// An absolute length in millimeters (mm)
418     #[css(dimension)]
419     Mm(CSSFloat),
420     /// An absolute length in quarter-millimeters (q)
421     #[css(dimension)]
422     Q(CSSFloat),
423     /// An absolute length in points (pt)
424     #[css(dimension)]
425     Pt(CSSFloat),
426     /// An absolute length in pica (pc)
427     #[css(dimension)]
428     Pc(CSSFloat),
429 }
430 
431 impl AbsoluteLength {
is_zero(&self) -> bool432     fn is_zero(&self) -> bool {
433         match *self {
434             AbsoluteLength::Px(v) |
435             AbsoluteLength::In(v) |
436             AbsoluteLength::Cm(v) |
437             AbsoluteLength::Mm(v) |
438             AbsoluteLength::Q(v) |
439             AbsoluteLength::Pt(v) |
440             AbsoluteLength::Pc(v) => v == 0.,
441         }
442     }
443 
is_negative(&self) -> bool444     fn is_negative(&self) -> bool {
445         match *self {
446             AbsoluteLength::Px(v) |
447             AbsoluteLength::In(v) |
448             AbsoluteLength::Cm(v) |
449             AbsoluteLength::Mm(v) |
450             AbsoluteLength::Q(v) |
451             AbsoluteLength::Pt(v) |
452             AbsoluteLength::Pc(v) => v < 0.,
453         }
454     }
455 
456     /// Convert this into a pixel value.
457     #[inline]
to_px(&self) -> CSSFloat458     pub fn to_px(&self) -> CSSFloat {
459         use std::f32;
460 
461         let pixel = match *self {
462             AbsoluteLength::Px(value) => value,
463             AbsoluteLength::In(value) => value * (AU_PER_IN / AU_PER_PX),
464             AbsoluteLength::Cm(value) => value * (AU_PER_CM / AU_PER_PX),
465             AbsoluteLength::Mm(value) => value * (AU_PER_MM / AU_PER_PX),
466             AbsoluteLength::Q(value) => value * (AU_PER_Q / AU_PER_PX),
467             AbsoluteLength::Pt(value) => value * (AU_PER_PT / AU_PER_PX),
468             AbsoluteLength::Pc(value) => value * (AU_PER_PC / AU_PER_PX),
469         };
470         pixel.min(f32::MAX).max(f32::MIN)
471     }
472 }
473 
474 impl ToComputedValue for AbsoluteLength {
475     type ComputedValue = CSSPixelLength;
476 
to_computed_value(&self, _: &Context) -> Self::ComputedValue477     fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
478         CSSPixelLength::new(self.to_px())
479     }
480 
from_computed_value(computed: &Self::ComputedValue) -> Self481     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
482         AbsoluteLength::Px(computed.px())
483     }
484 }
485 
486 impl PartialOrd for AbsoluteLength {
partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>487     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
488         self.to_px().partial_cmp(&other.to_px())
489     }
490 }
491 
492 impl Mul<CSSFloat> for AbsoluteLength {
493     type Output = AbsoluteLength;
494 
495     #[inline]
mul(self, scalar: CSSFloat) -> AbsoluteLength496     fn mul(self, scalar: CSSFloat) -> AbsoluteLength {
497         match self {
498             AbsoluteLength::Px(v) => AbsoluteLength::Px(v * scalar),
499             AbsoluteLength::In(v) => AbsoluteLength::In(v * scalar),
500             AbsoluteLength::Cm(v) => AbsoluteLength::Cm(v * scalar),
501             AbsoluteLength::Mm(v) => AbsoluteLength::Mm(v * scalar),
502             AbsoluteLength::Q(v) => AbsoluteLength::Q(v * scalar),
503             AbsoluteLength::Pt(v) => AbsoluteLength::Pt(v * scalar),
504             AbsoluteLength::Pc(v) => AbsoluteLength::Pc(v * scalar),
505         }
506     }
507 }
508 
509 impl Add<AbsoluteLength> for AbsoluteLength {
510     type Output = Self;
511 
512     #[inline]
add(self, rhs: Self) -> Self513     fn add(self, rhs: Self) -> Self {
514         match (self, rhs) {
515             (AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(x + y),
516             (AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(x + y),
517             (AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(x + y),
518             (AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(x + y),
519             (AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(x + y),
520             (AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(x + y),
521             (AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(x + y),
522             _ => AbsoluteLength::Px(self.to_px() + rhs.to_px()),
523         }
524     }
525 }
526 
527 /// A `<length>` without taking `calc` expressions into account
528 ///
529 /// <https://drafts.csswg.org/css-values/#lengths>
530 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
531 pub enum NoCalcLength {
532     /// An absolute length
533     ///
534     /// <https://drafts.csswg.org/css-values/#absolute-length>
535     Absolute(AbsoluteLength),
536 
537     /// A font-relative length:
538     ///
539     /// <https://drafts.csswg.org/css-values/#font-relative-lengths>
540     FontRelative(FontRelativeLength),
541 
542     /// A viewport-relative length.
543     ///
544     /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
545     ViewportPercentage(ViewportPercentageLength),
546 
547     /// HTML5 "character width", as defined in HTML5 § 14.5.4.
548     ///
549     /// This cannot be specified by the user directly and is only generated by
550     /// `Stylist::synthesize_rules_for_legacy_attributes()`.
551     #[css(function)]
552     ServoCharacterWidth(CharacterWidth),
553 }
554 
555 impl Mul<CSSFloat> for NoCalcLength {
556     type Output = NoCalcLength;
557 
558     #[inline]
mul(self, scalar: CSSFloat) -> NoCalcLength559     fn mul(self, scalar: CSSFloat) -> NoCalcLength {
560         match self {
561             NoCalcLength::Absolute(v) => NoCalcLength::Absolute(v * scalar),
562             NoCalcLength::FontRelative(v) => NoCalcLength::FontRelative(v * scalar),
563             NoCalcLength::ViewportPercentage(v) => NoCalcLength::ViewportPercentage(v * scalar),
564             NoCalcLength::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"),
565         }
566     }
567 }
568 
569 impl NoCalcLength {
570     /// Returns whether the value of this length without unit is less than zero.
is_negative(&self) -> bool571     pub fn is_negative(&self) -> bool {
572         match *self {
573             NoCalcLength::Absolute(v) => v.is_negative(),
574             NoCalcLength::FontRelative(v) => v.is_negative(),
575             NoCalcLength::ViewportPercentage(v) => v.is_negative(),
576             NoCalcLength::ServoCharacterWidth(c) => c.0 < 0,
577         }
578     }
579 
580     /// Parse a given absolute or relative dimension.
parse_dimension( context: &ParserContext, value: CSSFloat, unit: &str, ) -> Result<Self, ()>581     pub fn parse_dimension(
582         context: &ParserContext,
583         value: CSSFloat,
584         unit: &str,
585     ) -> Result<Self, ()> {
586         Ok(match_ignore_ascii_case! { unit,
587             "px" => NoCalcLength::Absolute(AbsoluteLength::Px(value)),
588             "in" => NoCalcLength::Absolute(AbsoluteLength::In(value)),
589             "cm" => NoCalcLength::Absolute(AbsoluteLength::Cm(value)),
590             "mm" => NoCalcLength::Absolute(AbsoluteLength::Mm(value)),
591             "q" => NoCalcLength::Absolute(AbsoluteLength::Q(value)),
592             "pt" => NoCalcLength::Absolute(AbsoluteLength::Pt(value)),
593             "pc" => NoCalcLength::Absolute(AbsoluteLength::Pc(value)),
594             // font-relative
595             "em" => NoCalcLength::FontRelative(FontRelativeLength::Em(value)),
596             "ex" => NoCalcLength::FontRelative(FontRelativeLength::Ex(value)),
597             "ch" => NoCalcLength::FontRelative(FontRelativeLength::Ch(value)),
598             "cap" => NoCalcLength::FontRelative(FontRelativeLength::Cap(value)),
599             "ic" => NoCalcLength::FontRelative(FontRelativeLength::Ic(value)),
600             "rem" => NoCalcLength::FontRelative(FontRelativeLength::Rem(value)),
601             // viewport percentages
602             "vw" if !context.in_page_rule() => {
603                 NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value))
604             },
605             "vh" if !context.in_page_rule() => {
606                 NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value))
607             },
608             "vmin" if !context.in_page_rule() => {
609                 NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value))
610             },
611             "vmax" if !context.in_page_rule() => {
612                 NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))
613             },
614             _ => return Err(()),
615         })
616     }
617 
618     /// Try to sume two lengths if compatible into the left hand side.
try_sum(&self, other: &Self) -> Result<Self, ()>619     pub(crate) fn try_sum(&self, other: &Self) -> Result<Self, ()> {
620         use self::NoCalcLength::*;
621 
622         if std::mem::discriminant(self) != std::mem::discriminant(other) {
623             return Err(());
624         }
625 
626         Ok(match (self, other) {
627             (&Absolute(ref one), &Absolute(ref other)) => Absolute(*one + *other),
628             (&FontRelative(ref one), &FontRelative(ref other)) => FontRelative(one.try_sum(other)?),
629             (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
630                 ViewportPercentage(one.try_sum(other)?)
631             },
632             (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
633                 ServoCharacterWidth(CharacterWidth(one.0 + other.0))
634             },
635             // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
636             // able to figure it own on its own so we help.
637             _ => unsafe {
638                 match *self {
639                     Absolute(..) |
640                     FontRelative(..) |
641                     ViewportPercentage(..) |
642                     ServoCharacterWidth(..) => {},
643                 }
644                 debug_unreachable!("Forgot to handle unit in try_sum()")
645             },
646         })
647     }
648 
649     /// Get a px value without context.
650     #[inline]
to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()>651     pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
652         match *self {
653             NoCalcLength::Absolute(len) => Ok(len.to_px()),
654             _ => Err(()),
655         }
656     }
657 
658     /// Get an absolute length from a px value.
659     #[inline]
from_px(px_value: CSSFloat) -> NoCalcLength660     pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
661         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
662     }
663 }
664 
665 impl SpecifiedValueInfo for NoCalcLength {}
666 
667 impl PartialOrd for NoCalcLength {
partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>668     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
669         use self::NoCalcLength::*;
670 
671         if std::mem::discriminant(self) != std::mem::discriminant(other) {
672             return None;
673         }
674 
675         match (self, other) {
676             (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
677             (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
678             (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
679                 one.partial_cmp(other)
680             },
681             (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
682                 one.0.partial_cmp(&other.0)
683             },
684             // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
685             // able to figure it own on its own so we help.
686             _ => unsafe {
687                 match *self {
688                     Absolute(..) |
689                     FontRelative(..) |
690                     ViewportPercentage(..) |
691                     ServoCharacterWidth(..) => {},
692                 }
693                 debug_unreachable!("Forgot an arm in partial_cmp?")
694             },
695         }
696     }
697 }
698 
699 impl Zero for NoCalcLength {
zero() -> Self700     fn zero() -> Self {
701         NoCalcLength::Absolute(AbsoluteLength::Px(0.))
702     }
703 
is_zero(&self) -> bool704     fn is_zero(&self) -> bool {
705         match *self {
706             NoCalcLength::Absolute(v) => v.is_zero(),
707             NoCalcLength::FontRelative(v) => v.is_zero(),
708             NoCalcLength::ViewportPercentage(v) => v.is_zero(),
709             NoCalcLength::ServoCharacterWidth(v) => v.0 == 0,
710         }
711     }
712 }
713 
714 /// An extension to `NoCalcLength` to parse `calc` expressions.
715 /// This is commonly used for the `<length>` values.
716 ///
717 /// <https://drafts.csswg.org/css-values/#lengths>
718 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
719 pub enum Length {
720     /// The internal length type that cannot parse `calc`
721     NoCalc(NoCalcLength),
722     /// A calc expression.
723     ///
724     /// <https://drafts.csswg.org/css-values/#calc-notation>
725     Calc(Box<CalcLengthPercentage>),
726 }
727 
728 impl From<NoCalcLength> for Length {
729     #[inline]
from(len: NoCalcLength) -> Self730     fn from(len: NoCalcLength) -> Self {
731         Length::NoCalc(len)
732     }
733 }
734 
735 impl Mul<CSSFloat> for Length {
736     type Output = Length;
737 
738     #[inline]
mul(self, scalar: CSSFloat) -> Length739     fn mul(self, scalar: CSSFloat) -> Length {
740         match self {
741             Length::NoCalc(inner) => Length::NoCalc(inner * scalar),
742             Length::Calc(..) => panic!("Can't multiply Calc!"),
743         }
744     }
745 }
746 
747 impl PartialOrd for FontRelativeLength {
partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>748     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
749         use self::FontRelativeLength::*;
750 
751         if std::mem::discriminant(self) != std::mem::discriminant(other) {
752             return None;
753         }
754 
755         match (self, other) {
756             (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
757             (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
758             (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
759             (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
760             (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
761             (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
762             // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
763             // able to figure it own on its own so we help.
764             _ => unsafe {
765                 match *self {
766                     Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {},
767                 }
768                 debug_unreachable!("Forgot an arm in partial_cmp?")
769             },
770         }
771     }
772 }
773 
774 impl Mul<CSSFloat> for FontRelativeLength {
775     type Output = FontRelativeLength;
776 
777     #[inline]
mul(self, scalar: CSSFloat) -> FontRelativeLength778     fn mul(self, scalar: CSSFloat) -> FontRelativeLength {
779         match self {
780             FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar),
781             FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar),
782             FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar),
783             FontRelativeLength::Cap(v) => FontRelativeLength::Cap(v * scalar),
784             FontRelativeLength::Ic(v) => FontRelativeLength::Ic(v * scalar),
785             FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar),
786         }
787     }
788 }
789 
790 impl Mul<CSSFloat> for ViewportPercentageLength {
791     type Output = ViewportPercentageLength;
792 
793     #[inline]
mul(self, scalar: CSSFloat) -> ViewportPercentageLength794     fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength {
795         match self {
796             ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar),
797             ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar),
798             ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar),
799             ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar),
800         }
801     }
802 }
803 
804 impl PartialOrd for ViewportPercentageLength {
partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>805     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
806         use self::ViewportPercentageLength::*;
807 
808         if std::mem::discriminant(self) != std::mem::discriminant(other) {
809             return None;
810         }
811 
812         match (self, other) {
813             (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
814             (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
815             (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
816             (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
817             // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
818             // able to figure it own on its own so we help.
819             _ => unsafe {
820                 match *self {
821                     Vw(..) | Vh(..) | Vmin(..) | Vmax(..) => {},
822                 }
823                 debug_unreachable!("Forgot an arm in partial_cmp?")
824             },
825         }
826     }
827 }
828 
829 impl Length {
830     #[inline]
parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>831     fn parse_internal<'i, 't>(
832         context: &ParserContext,
833         input: &mut Parser<'i, 't>,
834         num_context: AllowedNumericType,
835         allow_quirks: AllowQuirks,
836     ) -> Result<Self, ParseError<'i>> {
837         let location = input.current_source_location();
838         let token = input.next()?;
839         match *token {
840             Token::Dimension {
841                 value, ref unit, ..
842             } if num_context.is_ok(context.parsing_mode, value) => {
843                 NoCalcLength::parse_dimension(context, value, unit)
844                     .map(Length::NoCalc)
845                     .map_err(|()| location.new_unexpected_token_error(token.clone()))
846             },
847             Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
848                 if value != 0. &&
849                     !context.parsing_mode.allows_unitless_lengths() &&
850                     !allow_quirks.allowed(context.quirks_mode)
851                 {
852                     return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
853                 }
854                 Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
855                     value,
856                 ))))
857             },
858             Token::Function(ref name) => {
859                 let function = CalcNode::math_function(name, location)?;
860                 let calc = CalcNode::parse_length(context, input, num_context, function)?;
861                 Ok(Length::Calc(Box::new(calc)))
862             },
863             ref token => return Err(location.new_unexpected_token_error(token.clone())),
864         }
865     }
866 
867     /// Parse a non-negative length
868     #[inline]
parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>869     pub fn parse_non_negative<'i, 't>(
870         context: &ParserContext,
871         input: &mut Parser<'i, 't>,
872     ) -> Result<Self, ParseError<'i>> {
873         Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
874     }
875 
876     /// Parse a non-negative length, allowing quirks.
877     #[inline]
parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>878     pub fn parse_non_negative_quirky<'i, 't>(
879         context: &ParserContext,
880         input: &mut Parser<'i, 't>,
881         allow_quirks: AllowQuirks,
882     ) -> Result<Self, ParseError<'i>> {
883         Self::parse_internal(
884             context,
885             input,
886             AllowedNumericType::NonNegative,
887             allow_quirks,
888         )
889     }
890 
891     /// Get an absolute length from a px value.
892     #[inline]
from_px(px_value: CSSFloat) -> Length893     pub fn from_px(px_value: CSSFloat) -> Length {
894         Length::NoCalc(NoCalcLength::from_px(px_value))
895     }
896 }
897 
898 impl Parse for Length {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>899     fn parse<'i, 't>(
900         context: &ParserContext,
901         input: &mut Parser<'i, 't>,
902     ) -> Result<Self, ParseError<'i>> {
903         Self::parse_quirky(context, input, AllowQuirks::No)
904     }
905 }
906 
907 impl Zero for Length {
zero() -> Self908     fn zero() -> Self {
909         Length::NoCalc(NoCalcLength::zero())
910     }
911 
is_zero(&self) -> bool912     fn is_zero(&self) -> bool {
913         // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
914         // non-zero here?
915         match *self {
916             Length::NoCalc(ref l) => l.is_zero(),
917             Length::Calc(..) => false,
918         }
919     }
920 }
921 
922 impl Length {
923     /// Parses a length, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>924     pub fn parse_quirky<'i, 't>(
925         context: &ParserContext,
926         input: &mut Parser<'i, 't>,
927         allow_quirks: AllowQuirks,
928     ) -> Result<Self, ParseError<'i>> {
929         Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
930     }
931 }
932 
933 /// A wrapper of Length, whose value must be >= 0.
934 pub type NonNegativeLength = NonNegative<Length>;
935 
936 impl Parse for NonNegativeLength {
937     #[inline]
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>938     fn parse<'i, 't>(
939         context: &ParserContext,
940         input: &mut Parser<'i, 't>,
941     ) -> Result<Self, ParseError<'i>> {
942         Ok(NonNegative(Length::parse_non_negative(context, input)?))
943     }
944 }
945 
946 impl From<NoCalcLength> for NonNegativeLength {
947     #[inline]
from(len: NoCalcLength) -> Self948     fn from(len: NoCalcLength) -> Self {
949         NonNegative(Length::NoCalc(len))
950     }
951 }
952 
953 impl From<Length> for NonNegativeLength {
954     #[inline]
from(len: Length) -> Self955     fn from(len: Length) -> Self {
956         NonNegative(len)
957     }
958 }
959 
960 impl NonNegativeLength {
961     /// Get an absolute length from a px value.
962     #[inline]
from_px(px_value: CSSFloat) -> Self963     pub fn from_px(px_value: CSSFloat) -> Self {
964         Length::from_px(px_value.max(0.)).into()
965     }
966 
967     /// Parses a non-negative length, optionally with quirks.
968     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>969     pub fn parse_quirky<'i, 't>(
970         context: &ParserContext,
971         input: &mut Parser<'i, 't>,
972         allow_quirks: AllowQuirks,
973     ) -> Result<Self, ParseError<'i>> {
974         Ok(NonNegative(Length::parse_non_negative_quirky(
975             context,
976             input,
977             allow_quirks,
978         )?))
979     }
980 }
981 
982 /// A `<length-percentage>` value. This can be either a `<length>`, a
983 /// `<percentage>`, or a combination of both via `calc()`.
984 ///
985 /// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
986 #[allow(missing_docs)]
987 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
988 pub enum LengthPercentage {
989     Length(NoCalcLength),
990     Percentage(computed::Percentage),
991     Calc(Box<CalcLengthPercentage>),
992 }
993 
994 impl From<Length> for LengthPercentage {
from(len: Length) -> LengthPercentage995     fn from(len: Length) -> LengthPercentage {
996         match len {
997             Length::NoCalc(l) => LengthPercentage::Length(l),
998             Length::Calc(l) => LengthPercentage::Calc(l),
999         }
1000     }
1001 }
1002 
1003 impl From<NoCalcLength> for LengthPercentage {
1004     #[inline]
from(len: NoCalcLength) -> Self1005     fn from(len: NoCalcLength) -> Self {
1006         LengthPercentage::Length(len)
1007     }
1008 }
1009 
1010 impl From<Percentage> for LengthPercentage {
1011     #[inline]
from(pc: Percentage) -> Self1012     fn from(pc: Percentage) -> Self {
1013         if pc.is_calc() {
1014             // FIXME(emilio): Hard-coding the clamping mode is suspect.
1015             LengthPercentage::Calc(Box::new(CalcLengthPercentage {
1016                 clamping_mode: AllowedNumericType::All,
1017                 node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
1018             }))
1019         } else {
1020             LengthPercentage::Percentage(computed::Percentage(pc.get()))
1021         }
1022     }
1023 }
1024 
1025 impl From<computed::Percentage> for LengthPercentage {
1026     #[inline]
from(pc: computed::Percentage) -> Self1027     fn from(pc: computed::Percentage) -> Self {
1028         LengthPercentage::Percentage(pc)
1029     }
1030 }
1031 
1032 impl Parse for LengthPercentage {
1033     #[inline]
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1034     fn parse<'i, 't>(
1035         context: &ParserContext,
1036         input: &mut Parser<'i, 't>,
1037     ) -> Result<Self, ParseError<'i>> {
1038         Self::parse_quirky(context, input, AllowQuirks::No)
1039     }
1040 }
1041 
1042 impl LengthPercentage {
1043     #[inline]
1044     /// Returns a `0%` value.
zero_percent() -> LengthPercentage1045     pub fn zero_percent() -> LengthPercentage {
1046         LengthPercentage::Percentage(computed::Percentage::zero())
1047     }
1048 
parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1049     fn parse_internal<'i, 't>(
1050         context: &ParserContext,
1051         input: &mut Parser<'i, 't>,
1052         num_context: AllowedNumericType,
1053         allow_quirks: AllowQuirks,
1054     ) -> Result<Self, ParseError<'i>> {
1055         let location = input.current_source_location();
1056         let token = input.next()?;
1057         match *token {
1058             Token::Dimension {
1059                 value, ref unit, ..
1060             } if num_context.is_ok(context.parsing_mode, value) => {
1061                 return NoCalcLength::parse_dimension(context, value, unit)
1062                     .map(LengthPercentage::Length)
1063                     .map_err(|()| location.new_unexpected_token_error(token.clone()));
1064             },
1065             Token::Percentage { unit_value, .. }
1066                 if num_context.is_ok(context.parsing_mode, unit_value) =>
1067             {
1068                 return Ok(LengthPercentage::Percentage(computed::Percentage(
1069                     unit_value,
1070                 )));
1071             },
1072             Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
1073                 if value != 0. &&
1074                     !context.parsing_mode.allows_unitless_lengths() &&
1075                     !allow_quirks.allowed(context.quirks_mode)
1076                 {
1077                     return Err(location.new_unexpected_token_error(token.clone()));
1078                 } else {
1079                     return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
1080                 }
1081             },
1082             Token::Function(ref name) => {
1083                 let function = CalcNode::math_function(name, location)?;
1084                 let calc =
1085                     CalcNode::parse_length_or_percentage(context, input, num_context, function)?;
1086                 Ok(LengthPercentage::Calc(Box::new(calc)))
1087             },
1088             _ => return Err(location.new_unexpected_token_error(token.clone())),
1089         }
1090     }
1091 
1092     /// Parses allowing the unitless length quirk.
1093     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1094     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1095     pub fn parse_quirky<'i, 't>(
1096         context: &ParserContext,
1097         input: &mut Parser<'i, 't>,
1098         allow_quirks: AllowQuirks,
1099     ) -> Result<Self, ParseError<'i>> {
1100         Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
1101     }
1102 
1103     /// Parse a non-negative length.
1104     ///
1105     /// FIXME(emilio): This should be not public and we should use
1106     /// NonNegativeLengthPercentage instead.
1107     #[inline]
parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1108     pub fn parse_non_negative<'i, 't>(
1109         context: &ParserContext,
1110         input: &mut Parser<'i, 't>,
1111     ) -> Result<Self, ParseError<'i>> {
1112         Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1113     }
1114 
1115     /// Parse a non-negative length, with quirks.
1116     #[inline]
parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1117     pub fn parse_non_negative_quirky<'i, 't>(
1118         context: &ParserContext,
1119         input: &mut Parser<'i, 't>,
1120         allow_quirks: AllowQuirks,
1121     ) -> Result<Self, ParseError<'i>> {
1122         Self::parse_internal(
1123             context,
1124             input,
1125             AllowedNumericType::NonNegative,
1126             allow_quirks,
1127         )
1128     }
1129 }
1130 
1131 impl Zero for LengthPercentage {
zero() -> Self1132     fn zero() -> Self {
1133         LengthPercentage::Length(NoCalcLength::zero())
1134     }
1135 
is_zero(&self) -> bool1136     fn is_zero(&self) -> bool {
1137         match *self {
1138             LengthPercentage::Length(l) => l.is_zero(),
1139             LengthPercentage::Percentage(p) => p.0 == 0.0,
1140             LengthPercentage::Calc(_) => false,
1141         }
1142     }
1143 }
1144 
1145 /// A specified type for `<length-percentage> | auto`.
1146 pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
1147 
1148 impl LengthPercentageOrAuto {
1149     /// Returns a value representing `0%`.
1150     #[inline]
zero_percent() -> Self1151     pub fn zero_percent() -> Self {
1152         generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
1153     }
1154 
1155     /// Parses a length or a percentage, allowing the unitless length quirk.
1156     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1157     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1158     pub fn parse_quirky<'i, 't>(
1159         context: &ParserContext,
1160         input: &mut Parser<'i, 't>,
1161         allow_quirks: AllowQuirks,
1162     ) -> Result<Self, ParseError<'i>> {
1163         Self::parse_with(context, input, |context, input| {
1164             LengthPercentage::parse_quirky(context, input, allow_quirks)
1165         })
1166     }
1167 }
1168 
1169 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
1170 pub type NonNegativeLengthPercentageOrAuto =
1171     generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
1172 
1173 impl NonNegativeLengthPercentageOrAuto {
1174     /// Returns a value representing `0%`.
1175     #[inline]
zero_percent() -> Self1176     pub fn zero_percent() -> Self {
1177         generics::LengthPercentageOrAuto::LengthPercentage(
1178             NonNegativeLengthPercentage::zero_percent(),
1179         )
1180     }
1181 
1182     /// Parses a non-negative length-percentage, allowing the unitless length
1183     /// quirk.
1184     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1185     pub fn parse_quirky<'i, 't>(
1186         context: &ParserContext,
1187         input: &mut Parser<'i, 't>,
1188         allow_quirks: AllowQuirks,
1189     ) -> Result<Self, ParseError<'i>> {
1190         Self::parse_with(context, input, |context, input| {
1191             NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
1192         })
1193     }
1194 }
1195 
1196 /// A wrapper of LengthPercentage, whose value must be >= 0.
1197 pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
1198 
1199 /// Either a NonNegativeLengthPercentage or the `normal` keyword.
1200 pub type NonNegativeLengthPercentageOrNormal =
1201     GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
1202 
1203 impl From<NoCalcLength> for NonNegativeLengthPercentage {
1204     #[inline]
from(len: NoCalcLength) -> Self1205     fn from(len: NoCalcLength) -> Self {
1206         NonNegative(LengthPercentage::from(len))
1207     }
1208 }
1209 
1210 impl Parse for NonNegativeLengthPercentage {
1211     #[inline]
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1212     fn parse<'i, 't>(
1213         context: &ParserContext,
1214         input: &mut Parser<'i, 't>,
1215     ) -> Result<Self, ParseError<'i>> {
1216         Self::parse_quirky(context, input, AllowQuirks::No)
1217     }
1218 }
1219 
1220 impl NonNegativeLengthPercentage {
1221     #[inline]
1222     /// Returns a `0%` value.
zero_percent() -> Self1223     pub fn zero_percent() -> Self {
1224         NonNegative(LengthPercentage::zero_percent())
1225     }
1226 
1227     /// Parses a length or a percentage, allowing the unitless length quirk.
1228     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1229     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1230     pub fn parse_quirky<'i, 't>(
1231         context: &ParserContext,
1232         input: &mut Parser<'i, 't>,
1233         allow_quirks: AllowQuirks,
1234     ) -> Result<Self, ParseError<'i>> {
1235         LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
1236     }
1237 }
1238 
1239 /// Either a `<length>` or the `auto` keyword.
1240 ///
1241 /// Note that we use LengthPercentage just for convenience, since it pretty much
1242 /// is everything we care about, but we could just add a similar LengthOrAuto
1243 /// instead if we think getting rid of this weirdness is worth it.
1244 pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
1245 
1246 impl LengthOrAuto {
1247     /// Parses a length, allowing the unitless length quirk.
1248     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1249     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1250     pub fn parse_quirky<'i, 't>(
1251         context: &ParserContext,
1252         input: &mut Parser<'i, 't>,
1253         allow_quirks: AllowQuirks,
1254     ) -> Result<Self, ParseError<'i>> {
1255         Self::parse_with(context, input, |context, input| {
1256             Length::parse_quirky(context, input, allow_quirks)
1257         })
1258     }
1259 }
1260 
1261 /// Either a non-negative `<length>` or the `auto` keyword.
1262 pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
1263 
1264 /// Either a `<length>` or a `<number>`.
1265 pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
1266 
1267 /// A specified value for `min-width`, `min-height`, `width` or `height` property.
1268 pub type Size = GenericSize<NonNegativeLengthPercentage>;
1269 
1270 impl Parse for Size {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1271     fn parse<'i, 't>(
1272         context: &ParserContext,
1273         input: &mut Parser<'i, 't>,
1274     ) -> Result<Self, ParseError<'i>> {
1275         Size::parse_quirky(context, input, AllowQuirks::No)
1276     }
1277 }
1278 
1279 macro_rules! parse_size_non_length {
1280     ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{
1281         let size = $input.try_parse(|input| {
1282             Ok(try_match_ident_ignore_ascii_case! { input,
1283                 #[cfg(feature = "gecko")]
1284                 "min-content" | "-moz-min-content" => $size::MinContent,
1285                 #[cfg(feature = "gecko")]
1286                 "max-content" | "-moz-max-content" => $size::MaxContent,
1287                 #[cfg(feature = "gecko")]
1288                 "fit-content" | "-moz-fit-content" => $size::FitContent,
1289                 #[cfg(feature = "gecko")]
1290                 "-moz-available" => $size::MozAvailable,
1291                 $auto_or_none => $size::$auto_or_none_ident,
1292             })
1293         });
1294         if size.is_ok() {
1295             return size;
1296         }
1297     }};
1298 }
1299 
1300 #[cfg(feature = "gecko")]
is_fit_content_function_enabled() -> bool1301 fn is_fit_content_function_enabled() -> bool {
1302     static_prefs::pref!("layout.css.fit-content-function.enabled")
1303 }
1304 #[cfg(feature = "servo")]
is_fit_content_function_enabled() -> bool1305 fn is_fit_content_function_enabled() -> bool {
1306     false
1307 }
1308 
1309 macro_rules! parse_fit_content_function {
1310     ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
1311         if is_fit_content_function_enabled() {
1312             if let Ok(length) = $input.try_parse(|input| {
1313                 input.expect_function_matching("fit-content")?;
1314                 input.parse_nested_block(|i| {
1315                     NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
1316                 })
1317             }) {
1318                 return Ok($size::FitContentFunction(length));
1319             }
1320         }
1321     };
1322 }
1323 
1324 impl Size {
1325     /// Parses, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1326     pub fn parse_quirky<'i, 't>(
1327         context: &ParserContext,
1328         input: &mut Parser<'i, 't>,
1329         allow_quirks: AllowQuirks,
1330     ) -> Result<Self, ParseError<'i>> {
1331         parse_size_non_length!(Size, input, "auto" => Auto);
1332         parse_fit_content_function!(Size, input, context, allow_quirks);
1333 
1334         let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
1335         Ok(GenericSize::LengthPercentage(length))
1336     }
1337 
1338     /// Returns `0%`.
1339     #[inline]
zero_percent() -> Self1340     pub fn zero_percent() -> Self {
1341         GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
1342     }
1343 }
1344 
1345 /// A specified value for `max-width` or `max-height` property.
1346 pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
1347 
1348 impl Parse for MaxSize {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1349     fn parse<'i, 't>(
1350         context: &ParserContext,
1351         input: &mut Parser<'i, 't>,
1352     ) -> Result<Self, ParseError<'i>> {
1353         MaxSize::parse_quirky(context, input, AllowQuirks::No)
1354     }
1355 }
1356 
1357 impl MaxSize {
1358     /// Parses, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1359     pub fn parse_quirky<'i, 't>(
1360         context: &ParserContext,
1361         input: &mut Parser<'i, 't>,
1362         allow_quirks: AllowQuirks,
1363     ) -> Result<Self, ParseError<'i>> {
1364         parse_size_non_length!(MaxSize, input, "none" => None);
1365         parse_fit_content_function!(MaxSize, input, context, allow_quirks);
1366 
1367         let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
1368         Ok(GenericMaxSize::LengthPercentage(length))
1369     }
1370 }
1371 
1372 /// A specified non-negative `<length>` | `<number>`.
1373 pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
1374