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