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 http://mozilla.org/MPL/2.0/. */
4 
5 //! [Length values][length].
6 //!
7 //! [length]: https://drafts.csswg.org/css-values/#lengths
8 
9 use app_units::Au;
10 use cssparser::{Parser, Token};
11 use euclid::Size2D;
12 use font_metrics::FontMetricsQueryResult;
13 use parser::{Parse, ParserContext};
14 use std::cmp;
15 use std::ops::{Add, Mul};
16 use style_traits::{ParseError, StyleParseErrorKind};
17 use style_traits::values::specified::AllowedNumericType;
18 use super::{AllowQuirks, Number, ToComputedValue, Percentage};
19 use values::{Auto, CSSFloat, Either, Normal};
20 use values::computed::{self, CSSPixelLength, Context, ExtremumLength};
21 use values::generics::NonNegative;
22 use values::specified::calc::CalcNode;
23 
24 pub use values::specified::calc::CalcLengthOrPercentage;
25 pub use super::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
26 pub use super::image::{GradientKind, Image};
27 
28 /// Number of app units per pixel
29 pub const AU_PER_PX: CSSFloat = 60.;
30 /// Number of app units per inch
31 pub const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
32 /// Number of app units per centimeter
33 pub const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
34 /// Number of app units per millimeter
35 pub const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4;
36 /// Number of app units per quarter
37 pub const AU_PER_Q: CSSFloat = AU_PER_MM / 4.;
38 /// Number of app units per point
39 pub const AU_PER_PT: CSSFloat = AU_PER_IN / 72.;
40 /// Number of app units per pica
41 pub const AU_PER_PC: CSSFloat = AU_PER_PT * 12.;
42 
43 /// Same as Gecko's AppUnitsToIntCSSPixels
44 ///
45 /// Converts app units to integer pixel values,
46 /// rounding during the conversion
au_to_int_px(au: f32) -> i3247 pub fn au_to_int_px(au: f32) -> i32 {
48     (au / AU_PER_PX).round() as i32
49 }
50 
51 /// A font relative length.
52 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
53 pub enum FontRelativeLength {
54     /// A "em" value: https://drafts.csswg.org/css-values/#em
55     #[css(dimension)]
56     Em(CSSFloat),
57     /// A "ex" value: https://drafts.csswg.org/css-values/#ex
58     #[css(dimension)]
59     Ex(CSSFloat),
60     /// A "ch" value: https://drafts.csswg.org/css-values/#ch
61     #[css(dimension)]
62     Ch(CSSFloat),
63     /// A "rem" value: https://drafts.csswg.org/css-values/#rem
64     #[css(dimension)]
65     Rem(CSSFloat)
66 }
67 
68 /// A source to resolve font-relative units against
69 #[derive(Clone, Copy, Debug, PartialEq)]
70 pub enum FontBaseSize {
71     /// Use the font-size of the current element.
72     CurrentStyle,
73     /// Use the inherited font-size.
74     InheritedStyle,
75     /// Use the inherited font-size, but strip em units.
76     ///
77     /// FIXME(emilio): This is very complex, and should go away.
78     InheritedStyleButStripEmUnits,
79     /// Use a custom base size.
80     ///
81     /// FIXME(emilio): This is very dubious, and only used for MathML.
82     Custom(Au),
83 }
84 
85 impl FontBaseSize {
86     /// Calculate the actual size for a given context
resolve(&self, context: &Context) -> Au87     pub fn resolve(&self, context: &Context) -> Au {
88         match *self {
89             FontBaseSize::Custom(size) => size,
90             FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().size(),
91             FontBaseSize::InheritedStyleButStripEmUnits |
92             FontBaseSize::InheritedStyle => context.style().get_parent_font().clone_font_size().size(),
93         }
94     }
95 }
96 
97 impl FontRelativeLength {
98     /// Computes the font-relative length.
to_computed_value(&self, context: &Context, base_size: FontBaseSize) -> CSSPixelLength99     pub fn to_computed_value(&self, context: &Context, base_size: FontBaseSize) -> CSSPixelLength {
100         use std::f32;
101         let (reference_size, length) = self.reference_font_size_and_length(context, base_size);
102         let pixel = (length * reference_size.to_f32_px()).min(f32::MAX).max(f32::MIN);
103         CSSPixelLength::new(pixel)
104     }
105 
106     /// Return reference font size.
107     ///
108     /// We use the base_size flag to pass a different size for computing
109     /// font-size and unconstrained font-size.
110     ///
111     /// This returns a pair, the first one is the reference font size, and the
112     /// second one is the unpacked relative length.
reference_font_size_and_length( &self, context: &Context, base_size: FontBaseSize, ) -> (Au, CSSFloat)113     fn reference_font_size_and_length(
114         &self,
115         context: &Context,
116         base_size: FontBaseSize,
117     ) -> (Au, CSSFloat) {
118         fn query_font_metrics(
119             context: &Context,
120             reference_font_size: Au,
121         ) -> FontMetricsQueryResult {
122             context.font_metrics_provider.query(
123                 context.style().get_font(),
124                 reference_font_size,
125                 context.style().writing_mode,
126                 context.in_media_query,
127                 context.device(),
128             )
129         }
130 
131         let reference_font_size = base_size.resolve(context);
132         match *self {
133             FontRelativeLength::Em(length) => {
134                 if context.for_non_inherited_property.is_some() {
135                     if base_size == FontBaseSize::CurrentStyle {
136                         context.rule_cache_conditions.borrow_mut()
137                             .set_font_size_dependency(
138                                 reference_font_size.into()
139                             );
140                     }
141                 }
142 
143                 if base_size == FontBaseSize::InheritedStyleButStripEmUnits {
144                     (Au(0), length)
145                 } else {
146                     (reference_font_size, length)
147                 }
148             },
149             FontRelativeLength::Ex(length) => {
150                 if context.for_non_inherited_property.is_some() {
151                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
152                 }
153                 let reference_size = match query_font_metrics(context, reference_font_size) {
154                     FontMetricsQueryResult::Available(metrics) => {
155                         metrics.x_height
156                     },
157                     // https://drafts.csswg.org/css-values/#ex
158                     //
159                     //     In the cases where it is impossible or impractical to
160                     //     determine the x-height, a value of 0.5em must be
161                     //     assumed.
162                     //
163                     FontMetricsQueryResult::NotAvailable => {
164                         reference_font_size.scale_by(0.5)
165                     },
166                 };
167                 (reference_size, length)
168             },
169             FontRelativeLength::Ch(length) => {
170                 if context.for_non_inherited_property.is_some() {
171                     context.rule_cache_conditions.borrow_mut().set_uncacheable();
172                 }
173                 let reference_size = match query_font_metrics(context, reference_font_size) {
174                     FontMetricsQueryResult::Available(metrics) => {
175                         metrics.zero_advance_measure
176                     },
177                     // https://drafts.csswg.org/css-values/#ch
178                     //
179                     //     In the cases where it is impossible or impractical to
180                     //     determine the measure of the “0” glyph, it must be
181                     //     assumed to be 0.5em wide by 1em tall. Thus, the ch
182                     //     unit falls back to 0.5em in the general case, and to
183                     //     1em when it would be typeset upright (i.e.
184                     //     writing-mode is vertical-rl or vertical-lr and
185                     //     text-orientation is upright).
186                     //
187                     FontMetricsQueryResult::NotAvailable => {
188                         if context.style().writing_mode.is_vertical() {
189                             reference_font_size
190                         } else {
191                             reference_font_size.scale_by(0.5)
192                         }
193                     }
194                 };
195                 (reference_size, length)
196             }
197             FontRelativeLength::Rem(length) => {
198                 // https://drafts.csswg.org/css-values/#rem:
199                 //
200                 //     When specified on the font-size property of the root
201                 //     element, the rem units refer to the property's initial
202                 //     value.
203                 //
204                 let reference_size = if context.is_root_element || context.in_media_query {
205                     reference_font_size
206                 } else {
207                     context.device().root_font_size()
208                 };
209                 (reference_size, length)
210             }
211         }
212     }
213 }
214 
215 /// A viewport-relative length.
216 ///
217 /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
218 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
219 pub enum ViewportPercentageLength {
220     /// A vw unit: https://drafts.csswg.org/css-values/#vw
221     #[css(dimension)]
222     Vw(CSSFloat),
223     /// A vh unit: https://drafts.csswg.org/css-values/#vh
224     #[css(dimension)]
225     Vh(CSSFloat),
226     /// <https://drafts.csswg.org/css-values/#vmin>
227     #[css(dimension)]
228     Vmin(CSSFloat),
229     /// <https://drafts.csswg.org/css-values/#vmax>
230     #[css(dimension)]
231     Vmax(CSSFloat)
232 }
233 
234 impl ViewportPercentageLength {
235     /// Computes the given viewport-relative length for the given viewport size.
to_computed_value(&self, viewport_size: Size2D<Au>) -> CSSPixelLength236     pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> CSSPixelLength {
237         let (factor, length) = match *self {
238             ViewportPercentageLength::Vw(length) =>
239                 (length, viewport_size.width),
240             ViewportPercentageLength::Vh(length) =>
241                 (length, viewport_size.height),
242             ViewportPercentageLength::Vmin(length) =>
243                 (length, cmp::min(viewport_size.width, viewport_size.height)),
244             ViewportPercentageLength::Vmax(length) =>
245                 (length, cmp::max(viewport_size.width, viewport_size.height)),
246         };
247 
248         // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.
249         // See bug 989802. We truncate so that adding multiple viewport units
250         // that add up to 100 does not overflow due to rounding differences
251         let trunc_scaled = ((length.0 as f64) * factor as f64 / 100.).trunc();
252         Au::from_f64_au(trunc_scaled).into()
253     }
254 }
255 
256 /// HTML5 "character width", as defined in HTML5 § 14.5.4.
257 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
258 pub struct CharacterWidth(pub i32);
259 
260 impl CharacterWidth {
261     /// Computes the given character width.
to_computed_value(&self, reference_font_size: Au) -> CSSPixelLength262     pub fn to_computed_value(&self, reference_font_size: Au) -> CSSPixelLength {
263         // This applies the *converting a character width to pixels* algorithm as specified
264         // in HTML5 § 14.5.4.
265         //
266         // TODO(pcwalton): Find these from the font.
267         let average_advance = reference_font_size.scale_by(0.5);
268         let max_advance = reference_font_size;
269         let au = average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance;
270         au.into()
271     }
272 }
273 
274 /// Represents an absolute length with its unit
275 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
276 pub enum AbsoluteLength {
277     /// An absolute length in pixels (px)
278     #[css(dimension)]
279     Px(CSSFloat),
280     /// An absolute length in inches (in)
281     #[css(dimension)]
282     In(CSSFloat),
283     /// An absolute length in centimeters (cm)
284     #[css(dimension)]
285     Cm(CSSFloat),
286     /// An absolute length in millimeters (mm)
287     #[css(dimension)]
288     Mm(CSSFloat),
289     /// An absolute length in quarter-millimeters (q)
290     #[css(dimension)]
291     Q(CSSFloat),
292     /// An absolute length in points (pt)
293     #[css(dimension)]
294     Pt(CSSFloat),
295     /// An absolute length in pica (pc)
296     #[css(dimension)]
297     Pc(CSSFloat),
298 }
299 
300 impl AbsoluteLength {
is_zero(&self) -> bool301     fn is_zero(&self) -> bool {
302         match *self {
303             AbsoluteLength::Px(v)
304             | AbsoluteLength::In(v)
305             | AbsoluteLength::Cm(v)
306             | AbsoluteLength::Mm(v)
307             | AbsoluteLength::Q(v)
308             | AbsoluteLength::Pt(v)
309             | AbsoluteLength::Pc(v) => v == 0.,
310         }
311     }
312 
313     /// Convert this into a pixel value.
314     #[inline]
to_px(&self) -> CSSFloat315     pub fn to_px(&self) -> CSSFloat {
316         use std::f32;
317 
318         let pixel = match *self {
319             AbsoluteLength::Px(value) => value,
320             AbsoluteLength::In(value) => value * (AU_PER_IN / AU_PER_PX),
321             AbsoluteLength::Cm(value) => value * (AU_PER_CM / AU_PER_PX),
322             AbsoluteLength::Mm(value) => value * (AU_PER_MM / AU_PER_PX),
323             AbsoluteLength::Q(value) => value * (AU_PER_Q / AU_PER_PX),
324             AbsoluteLength::Pt(value) => value * (AU_PER_PT / AU_PER_PX),
325             AbsoluteLength::Pc(value) => value * (AU_PER_PC / AU_PER_PX),
326         };
327         pixel.min(f32::MAX).max(f32::MIN)
328     }
329 }
330 
331 impl ToComputedValue for AbsoluteLength {
332     type ComputedValue = CSSPixelLength;
333 
to_computed_value(&self, _: &Context) -> Self::ComputedValue334     fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
335         CSSPixelLength::new(self.to_px())
336     }
337 
from_computed_value(computed: &Self::ComputedValue) -> Self338     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
339         AbsoluteLength::Px(computed.px())
340     }
341 }
342 
343 impl Mul<CSSFloat> for AbsoluteLength {
344     type Output = AbsoluteLength;
345 
346     #[inline]
mul(self, scalar: CSSFloat) -> AbsoluteLength347     fn mul(self, scalar: CSSFloat) -> AbsoluteLength {
348         match self {
349             AbsoluteLength::Px(v) => AbsoluteLength::Px(v * scalar),
350             AbsoluteLength::In(v) => AbsoluteLength::In(v * scalar),
351             AbsoluteLength::Cm(v) => AbsoluteLength::Cm(v * scalar),
352             AbsoluteLength::Mm(v) => AbsoluteLength::Mm(v * scalar),
353             AbsoluteLength::Q(v) => AbsoluteLength::Q(v * scalar),
354             AbsoluteLength::Pt(v) => AbsoluteLength::Pt(v * scalar),
355             AbsoluteLength::Pc(v) => AbsoluteLength::Pc(v * scalar),
356         }
357     }
358 }
359 
360 impl Add<AbsoluteLength> for AbsoluteLength {
361     type Output = Self;
362 
363     #[inline]
add(self, rhs: Self) -> Self364     fn add(self, rhs: Self) -> Self {
365         match (self, rhs) {
366             (AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(x + y),
367             (AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(x + y),
368             (AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(x + y),
369             (AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(x + y),
370             (AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(x + y),
371             (AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(x + y),
372             (AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(x + y),
373             _ => AbsoluteLength::Px(self.to_px() + rhs.to_px()),
374         }
375     }
376 }
377 
378 /// A `<length>` without taking `calc` expressions into account
379 ///
380 /// <https://drafts.csswg.org/css-values/#lengths>
381 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
382 pub enum NoCalcLength {
383     /// An absolute length
384     ///
385     /// <https://drafts.csswg.org/css-values/#absolute-length>
386     Absolute(AbsoluteLength),
387 
388     /// A font-relative length:
389     ///
390     /// <https://drafts.csswg.org/css-values/#font-relative-lengths>
391     FontRelative(FontRelativeLength),
392 
393     /// A viewport-relative length.
394     ///
395     /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
396     ViewportPercentage(ViewportPercentageLength),
397 
398     /// HTML5 "character width", as defined in HTML5 § 14.5.4.
399     ///
400     /// This cannot be specified by the user directly and is only generated by
401     /// `Stylist::synthesize_rules_for_legacy_attributes()`.
402     #[css(function)]
403     ServoCharacterWidth(CharacterWidth),
404 }
405 
406 impl Mul<CSSFloat> for NoCalcLength {
407     type Output = NoCalcLength;
408 
409     #[inline]
mul(self, scalar: CSSFloat) -> NoCalcLength410     fn mul(self, scalar: CSSFloat) -> NoCalcLength {
411         match self {
412             NoCalcLength::Absolute(v) => NoCalcLength::Absolute(v * scalar),
413             NoCalcLength::FontRelative(v) => NoCalcLength::FontRelative(v * scalar),
414             NoCalcLength::ViewportPercentage(v) => NoCalcLength::ViewportPercentage(v * scalar),
415             NoCalcLength::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"),
416         }
417     }
418 }
419 
420 impl NoCalcLength {
421     /// Parse a given absolute or relative dimension.
parse_dimension( context: &ParserContext, value: CSSFloat, unit: &str, ) -> Result<Self, ()>422     pub fn parse_dimension(
423         context: &ParserContext,
424         value: CSSFloat,
425         unit: &str,
426     ) -> Result<Self, ()> {
427         match_ignore_ascii_case! { unit,
428             "px" => Ok(NoCalcLength::Absolute(AbsoluteLength::Px(value))),
429             "in" => Ok(NoCalcLength::Absolute(AbsoluteLength::In(value))),
430             "cm" => Ok(NoCalcLength::Absolute(AbsoluteLength::Cm(value))),
431             "mm" => Ok(NoCalcLength::Absolute(AbsoluteLength::Mm(value))),
432             "q" => Ok(NoCalcLength::Absolute(AbsoluteLength::Q(value))),
433             "pt" => Ok(NoCalcLength::Absolute(AbsoluteLength::Pt(value))),
434             "pc" => Ok(NoCalcLength::Absolute(AbsoluteLength::Pc(value))),
435             // font-relative
436             "em" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Em(value))),
437             "ex" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Ex(value))),
438             "ch" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Ch(value))),
439             "rem" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Rem(value))),
440             // viewport percentages
441             "vw" => {
442                 if context.in_page_rule() {
443                     return Err(())
444                 }
445                 Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value)))
446             },
447             "vh" => {
448                 if context.in_page_rule() {
449                     return Err(())
450                 }
451                 Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value)))
452             },
453             "vmin" => {
454                 if context.in_page_rule() {
455                     return Err(())
456                 }
457                 Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value)))
458             },
459             "vmax" => {
460                 if context.in_page_rule() {
461                     return Err(())
462                 }
463                 Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value)))
464             },
465             _ => Err(())
466         }
467     }
468 
469     #[inline]
470     /// Returns a `zero` length.
zero() -> NoCalcLength471     pub fn zero() -> NoCalcLength {
472         NoCalcLength::Absolute(AbsoluteLength::Px(0.))
473     }
474 
475     #[inline]
476     /// Checks whether the length value is zero.
is_zero(&self) -> bool477     pub fn is_zero(&self) -> bool {
478         match *self {
479             NoCalcLength::Absolute(length) => length.is_zero(),
480             _ => false
481         }
482     }
483 
484     /// Get a px value without context.
485     #[inline]
to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()>486     pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
487         match *self {
488             NoCalcLength::Absolute(len) => Ok(len.to_px()),
489             _ => Err(()),
490         }
491     }
492 
493     /// Get an absolute length from a px value.
494     #[inline]
from_px(px_value: CSSFloat) -> NoCalcLength495     pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
496         NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
497     }
498 }
499 
500 /// An extension to `NoCalcLength` to parse `calc` expressions.
501 /// This is commonly used for the `<length>` values.
502 ///
503 /// <https://drafts.csswg.org/css-values/#lengths>
504 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
505 pub enum Length {
506     /// The internal length type that cannot parse `calc`
507     NoCalc(NoCalcLength),
508     /// A calc expression.
509     ///
510     /// <https://drafts.csswg.org/css-values/#calc-notation>
511     Calc(Box<CalcLengthOrPercentage>),
512 }
513 
514 impl From<NoCalcLength> for Length {
515     #[inline]
from(len: NoCalcLength) -> Self516     fn from(len: NoCalcLength) -> Self {
517         Length::NoCalc(len)
518     }
519 }
520 
521 impl Mul<CSSFloat> for Length {
522     type Output = Length;
523 
524     #[inline]
mul(self, scalar: CSSFloat) -> Length525     fn mul(self, scalar: CSSFloat) -> Length {
526         match self {
527             Length::NoCalc(inner) => Length::NoCalc(inner * scalar),
528             Length::Calc(..) => panic!("Can't multiply Calc!"),
529         }
530     }
531 }
532 
533 impl Mul<CSSFloat> for FontRelativeLength {
534     type Output = FontRelativeLength;
535 
536     #[inline]
mul(self, scalar: CSSFloat) -> FontRelativeLength537     fn mul(self, scalar: CSSFloat) -> FontRelativeLength {
538         match self {
539             FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar),
540             FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar),
541             FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar),
542             FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar),
543         }
544     }
545 }
546 
547 impl Mul<CSSFloat> for ViewportPercentageLength {
548     type Output = ViewportPercentageLength;
549 
550     #[inline]
mul(self, scalar: CSSFloat) -> ViewportPercentageLength551     fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength {
552         match self {
553             ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar),
554             ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar),
555             ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar),
556             ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar),
557         }
558     }
559 }
560 
561 impl Length {
562     #[inline]
563     /// Returns a `zero` length.
zero() -> Length564     pub fn zero() -> Length {
565         Length::NoCalc(NoCalcLength::zero())
566     }
567 
568     #[inline]
parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>569     fn parse_internal<'i, 't>(
570         context: &ParserContext,
571         input: &mut Parser<'i, 't>,
572         num_context: AllowedNumericType,
573         allow_quirks: AllowQuirks,
574     ) -> Result<Self, ParseError<'i>> {
575         // FIXME: remove early returns when lifetimes are non-lexical
576         {
577             let location = input.current_source_location();
578             let token = input.next()?;
579             match *token {
580                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
581                     return NoCalcLength::parse_dimension(context, value, unit)
582                         .map(Length::NoCalc)
583                         .map_err(|()| location.new_unexpected_token_error(token.clone()))
584                 }
585                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
586                     if value != 0. &&
587                        !context.parsing_mode.allows_unitless_lengths() &&
588                        !allow_quirks.allowed(context.quirks_mode) {
589                         return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
590                     }
591                     return Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(value))))
592                 },
593                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
594                 ref token => return Err(location.new_unexpected_token_error(token.clone()))
595             }
596         }
597         input.parse_nested_block(|input| {
598             CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc)))
599         })
600     }
601 
602     /// Parse a non-negative length
603     #[inline]
parse_non_negative<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Length, ParseError<'i>>604     pub fn parse_non_negative<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
605                                       -> Result<Length, ParseError<'i>> {
606         Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
607     }
608 
609     /// Parse a non-negative length, allowing quirks.
610     #[inline]
parse_non_negative_quirky<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks) -> Result<Length, ParseError<'i>>611     pub fn parse_non_negative_quirky<'i, 't>(context: &ParserContext,
612                                              input: &mut Parser<'i, 't>,
613                                              allow_quirks: AllowQuirks)
614                                              -> Result<Length, ParseError<'i>> {
615         Self::parse_internal(context, input, AllowedNumericType::NonNegative, allow_quirks)
616     }
617 
618     /// Get an absolute length from a px value.
619     #[inline]
from_px(px_value: CSSFloat) -> Length620     pub fn from_px(px_value: CSSFloat) -> Length {
621         Length::NoCalc(NoCalcLength::from_px(px_value))
622     }
623 }
624 
625 impl Parse for Length {
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>626     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
627         Self::parse_quirky(context, input, AllowQuirks::No)
628     }
629 }
630 
631 impl Length {
632     /// Parses a length, with quirks.
parse_quirky<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks) -> Result<Self, ParseError<'i>>633     pub fn parse_quirky<'i, 't>(context: &ParserContext,
634                                 input: &mut Parser<'i, 't>,
635                                 allow_quirks: AllowQuirks)
636                                 -> Result<Self, ParseError<'i>> {
637         Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
638     }
639 }
640 
641 /// A wrapper of Length, whose value must be >= 0.
642 pub type NonNegativeLength = NonNegative<Length>;
643 
644 impl Parse for NonNegativeLength {
645     #[inline]
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>646     fn parse<'i, 't>(
647         context: &ParserContext,
648         input: &mut Parser<'i, 't>,
649     ) -> Result<Self, ParseError<'i>> {
650         Ok(NonNegative(Length::parse_non_negative(context, input)?))
651     }
652 }
653 
654 impl From<NoCalcLength> for NonNegativeLength {
655     #[inline]
from(len: NoCalcLength) -> Self656     fn from(len: NoCalcLength) -> Self {
657         NonNegative::<Length>(Length::NoCalc(len))
658     }
659 }
660 
661 impl From<Length> for NonNegativeLength {
662     #[inline]
from(len: Length) -> Self663     fn from(len: Length) -> Self {
664         NonNegative::<Length>(len)
665     }
666 }
667 
668 impl NonNegativeLength {
669     /// Returns a `zero` length.
670     #[inline]
zero() -> Self671     pub fn zero() -> Self {
672         Length::zero().into()
673     }
674 
675     /// Get an absolute length from a px value.
676     #[inline]
from_px(px_value: CSSFloat) -> Self677     pub fn from_px(px_value: CSSFloat) -> Self {
678         Length::from_px(px_value.max(0.)).into()
679     }
680 }
681 
682 /// Either a NonNegativeLength or the `normal` keyword.
683 pub type NonNegativeLengthOrNormal = Either<NonNegativeLength, Normal>;
684 
685 /// Either a NonNegativeLength or the `auto` keyword.
686 pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;
687 
688 /// A length or a percentage value.
689 #[allow(missing_docs)]
690 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
691 pub enum LengthOrPercentage {
692     Length(NoCalcLength),
693     Percentage(computed::Percentage),
694     Calc(Box<CalcLengthOrPercentage>),
695 }
696 
697 impl From<Length> for LengthOrPercentage {
from(len: Length) -> LengthOrPercentage698     fn from(len: Length) -> LengthOrPercentage {
699         match len {
700             Length::NoCalc(l) => LengthOrPercentage::Length(l),
701             Length::Calc(l) => LengthOrPercentage::Calc(l),
702         }
703     }
704 }
705 
706 impl From<NoCalcLength> for LengthOrPercentage {
707     #[inline]
from(len: NoCalcLength) -> Self708     fn from(len: NoCalcLength) -> Self {
709         LengthOrPercentage::Length(len)
710     }
711 }
712 
713 impl From<Percentage> for LengthOrPercentage {
714     #[inline]
from(pc: Percentage) -> Self715     fn from(pc: Percentage) -> Self {
716         if pc.is_calc() {
717             LengthOrPercentage::Calc(Box::new(CalcLengthOrPercentage {
718                 percentage: Some(computed::Percentage(pc.get())),
719                 .. Default::default()
720             }))
721         } else {
722             LengthOrPercentage::Percentage(computed::Percentage(pc.get()))
723         }
724     }
725 }
726 
727 impl From<computed::Percentage> for LengthOrPercentage {
728     #[inline]
from(pc: computed::Percentage) -> Self729     fn from(pc: computed::Percentage) -> Self {
730         LengthOrPercentage::Percentage(pc)
731     }
732 }
733 
734 impl LengthOrPercentage {
735     #[inline]
736     /// Returns a `zero` length.
zero() -> LengthOrPercentage737     pub fn zero() -> LengthOrPercentage {
738         LengthOrPercentage::Length(NoCalcLength::zero())
739     }
740 
parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>741     fn parse_internal<'i, 't>(
742         context: &ParserContext,
743         input: &mut Parser<'i, 't>,
744         num_context: AllowedNumericType,
745         allow_quirks: AllowQuirks,
746     ) -> Result<Self, ParseError<'i>> {
747         // FIXME: remove early returns when lifetimes are non-lexical
748         {
749             let location = input.current_source_location();
750             let token = input.next()?;
751             match *token {
752                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
753                     return NoCalcLength::parse_dimension(context, value, unit)
754                         .map(LengthOrPercentage::Length)
755                         .map_err(|()| location.new_unexpected_token_error(token.clone()))
756                 }
757                 Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
758                     return Ok(LengthOrPercentage::Percentage(computed::Percentage(unit_value)))
759                 }
760                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
761                     if value != 0. &&
762                        !context.parsing_mode.allows_unitless_lengths() &&
763                        !allow_quirks.allowed(context.quirks_mode) {
764                         return Err(location.new_unexpected_token_error(token.clone()))
765                     } else {
766                         return Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value)))
767                     }
768                 }
769                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
770                 _ => return Err(location.new_unexpected_token_error(token.clone()))
771             }
772         }
773 
774         let calc = input.parse_nested_block(|i| {
775             CalcNode::parse_length_or_percentage(context, i, num_context)
776         })?;
777         Ok(LengthOrPercentage::Calc(Box::new(calc)))
778     }
779 
780     /// Parse a non-negative length.
781     #[inline]
parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<LengthOrPercentage, ParseError<'i>>782     pub fn parse_non_negative<'i, 't>(
783         context: &ParserContext,
784         input: &mut Parser<'i, 't>,
785     ) -> Result<LengthOrPercentage, ParseError<'i>> {
786         Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
787     }
788 
789     /// Parse a non-negative length, with quirks.
790     #[inline]
parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<LengthOrPercentage, ParseError<'i>>791     pub fn parse_non_negative_quirky<'i, 't>(
792         context: &ParserContext,
793         input: &mut Parser<'i, 't>,
794         allow_quirks: AllowQuirks,
795     ) -> Result<LengthOrPercentage, ParseError<'i>> {
796         Self::parse_internal(context, input, AllowedNumericType::NonNegative, allow_quirks)
797     }
798 }
799 
800 impl Parse for LengthOrPercentage {
801     #[inline]
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>802     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
803         Self::parse_quirky(context, input, AllowQuirks::No)
804     }
805 }
806 
807 impl LengthOrPercentage {
808     /// Parses a length or a percentage, allowing the unitless length quirk.
809     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
810     #[inline]
parse_quirky<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks) -> Result<Self, ParseError<'i>>811     pub fn parse_quirky<'i, 't>(context: &ParserContext,
812                                 input: &mut Parser<'i, 't>,
813                                 allow_quirks: AllowQuirks) -> Result<Self, ParseError<'i>> {
814         Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
815     }
816 }
817 
818 /// Either a `<length>`, a `<percentage>`, or the `auto` keyword.
819 #[allow(missing_docs)]
820 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
821 pub enum LengthOrPercentageOrAuto {
822     Length(NoCalcLength),
823     Percentage(computed::Percentage),
824     Auto,
825     Calc(Box<CalcLengthOrPercentage>),
826 }
827 
828 impl From<NoCalcLength> for LengthOrPercentageOrAuto {
829     #[inline]
from(len: NoCalcLength) -> Self830     fn from(len: NoCalcLength) -> Self {
831         LengthOrPercentageOrAuto::Length(len)
832     }
833 }
834 
835 impl From<computed::Percentage> for LengthOrPercentageOrAuto {
836     #[inline]
from(pc: computed::Percentage) -> Self837     fn from(pc: computed::Percentage) -> Self {
838         LengthOrPercentageOrAuto::Percentage(pc)
839     }
840 }
841 
842 impl LengthOrPercentageOrAuto {
parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>843     fn parse_internal<'i, 't>(
844         context: &ParserContext,
845         input: &mut Parser<'i, 't>,
846         num_context: AllowedNumericType,
847         allow_quirks: AllowQuirks,
848     ) -> Result<Self, ParseError<'i>> {
849         // FIXME: remove early returns when lifetimes are non-lexical
850         {
851             let location = input.current_source_location();
852             let token = input.next()?;
853             match *token {
854                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
855                     return NoCalcLength::parse_dimension(context, value, unit)
856                         .map(LengthOrPercentageOrAuto::Length)
857                         .map_err(|()| location.new_unexpected_token_error(token.clone()))
858                 }
859                 Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
860                     return Ok(LengthOrPercentageOrAuto::Percentage(computed::Percentage(unit_value)))
861                 }
862                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
863                     if value != 0. &&
864                        !context.parsing_mode.allows_unitless_lengths() &&
865                        !allow_quirks.allowed(context.quirks_mode) {
866                         return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
867                     }
868                     return Ok(LengthOrPercentageOrAuto::Length(
869                         NoCalcLength::Absolute(AbsoluteLength::Px(value))
870                     ))
871                 }
872                 Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => {
873                     return Ok(LengthOrPercentageOrAuto::Auto)
874                 }
875                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
876                 _ => return Err(location.new_unexpected_token_error(token.clone()))
877             }
878         }
879 
880         let calc = input.parse_nested_block(|i| {
881             CalcNode::parse_length_or_percentage(context, i, num_context)
882         })?;
883         Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc)))
884     }
885 
886     /// Parse a non-negative length, percentage, or auto.
887     #[inline]
parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<LengthOrPercentageOrAuto, ParseError<'i>>888     pub fn parse_non_negative<'i, 't>(
889         context: &ParserContext,
890         input: &mut Parser<'i, 't>,
891     ) -> Result<LengthOrPercentageOrAuto, ParseError<'i>> {
892         Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
893     }
894 
895     /// Parse a non-negative length, percentage, or auto.
896     #[inline]
parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>897     pub fn parse_non_negative_quirky<'i, 't>(
898         context: &ParserContext,
899         input: &mut Parser<'i, 't>,
900         allow_quirks: AllowQuirks,
901     ) -> Result<Self, ParseError<'i>> {
902         Self::parse_internal(context, input, AllowedNumericType::NonNegative, allow_quirks)
903     }
904 
905     /// Returns the `auto` value.
auto() -> Self906     pub fn auto() -> Self {
907         LengthOrPercentageOrAuto::Auto
908     }
909 
910     /// Returns a value representing a `0` length.
zero() -> Self911     pub fn zero() -> Self {
912         LengthOrPercentageOrAuto::Length(NoCalcLength::zero())
913     }
914 
915     /// Returns a value representing `0%`.
916     #[inline]
zero_percent() -> Self917     pub fn zero_percent() -> Self {
918         LengthOrPercentageOrAuto::Percentage(computed::Percentage::zero())
919     }
920 }
921 
922 impl Parse for LengthOrPercentageOrAuto {
923     #[inline]
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>924     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
925         Self::parse_quirky(context, input, AllowQuirks::No)
926     }
927 }
928 
929 impl LengthOrPercentageOrAuto {
930     /// Parses, with quirks.
931     #[inline]
parse_quirky<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks) -> Result<Self, ParseError<'i>>932     pub fn parse_quirky<'i, 't>(context: &ParserContext,
933                                 input: &mut Parser<'i, 't>,
934                                 allow_quirks: AllowQuirks)
935                                 -> Result<Self, ParseError<'i>> {
936         Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
937     }
938 }
939 
940 /// Either a `<length>`, a `<percentage>`, or the `none` keyword.
941 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
942 #[allow(missing_docs)]
943 pub enum LengthOrPercentageOrNone {
944     Length(NoCalcLength),
945     Percentage(computed::Percentage),
946     Calc(Box<CalcLengthOrPercentage>),
947     None,
948 }
949 
950 impl LengthOrPercentageOrNone {
parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>951     fn parse_internal<'i, 't>(
952         context: &ParserContext,
953         input: &mut Parser<'i, 't>,
954         num_context: AllowedNumericType,
955         allow_quirks: AllowQuirks,
956     ) -> Result<Self, ParseError<'i>> {
957         // FIXME: remove early returns when lifetimes are non-lexical
958         {
959             let location = input.current_source_location();
960             let token = input.next()?;
961             match *token {
962                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
963                     return NoCalcLength::parse_dimension(context, value, unit)
964                         .map(LengthOrPercentageOrNone::Length)
965                         .map_err(|()| location.new_unexpected_token_error(token.clone()))
966                 }
967                 Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
968                     return Ok(LengthOrPercentageOrNone::Percentage(computed::Percentage(unit_value)))
969                 }
970                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
971                     if value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
972                        !allow_quirks.allowed(context.quirks_mode) {
973                         return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
974                     }
975                     return Ok(LengthOrPercentageOrNone::Length(
976                         NoCalcLength::Absolute(AbsoluteLength::Px(value))
977                     ))
978                 }
979                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
980                 Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => {
981                     return Ok(LengthOrPercentageOrNone::None)
982                 }
983                 _ => return Err(location.new_unexpected_token_error(token.clone()))
984             }
985         }
986 
987         let calc = input.parse_nested_block(|i| {
988             CalcNode::parse_length_or_percentage(context, i, num_context)
989         })?;
990         Ok(LengthOrPercentageOrNone::Calc(Box::new(calc)))
991     }
992 
993     /// Parse a non-negative LengthOrPercentageOrNone.
994     #[inline]
parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>995     pub fn parse_non_negative<'i, 't>(
996         context: &ParserContext,
997         input: &mut Parser<'i, 't>,
998     ) -> Result<Self, ParseError<'i>> {
999         Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
1000     }
1001 
1002     /// Parse a non-negative LengthOrPercentageOrNone, with quirks.
1003     #[inline]
parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1004     pub fn parse_non_negative_quirky<'i, 't>(
1005         context: &ParserContext,
1006         input: &mut Parser<'i, 't>,
1007         allow_quirks: AllowQuirks,
1008     ) -> Result<Self, ParseError<'i>> {
1009         Self::parse_internal(context, input, AllowedNumericType::NonNegative, allow_quirks)
1010     }
1011 }
1012 
1013 impl Parse for LengthOrPercentageOrNone {
1014     #[inline]
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>1015     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
1016         Self::parse_internal(context, input, AllowedNumericType::All, AllowQuirks::No)
1017     }
1018 }
1019 
1020 /// A wrapper of LengthOrPercentage, whose value must be >= 0.
1021 pub type NonNegativeLengthOrPercentage = NonNegative<LengthOrPercentage>;
1022 
1023 impl From<NoCalcLength> for NonNegativeLengthOrPercentage {
1024     #[inline]
from(len: NoCalcLength) -> Self1025     fn from(len: NoCalcLength) -> Self {
1026         NonNegative::<LengthOrPercentage>(LengthOrPercentage::from(len))
1027     }
1028 }
1029 
1030 impl Parse for NonNegativeLengthOrPercentage {
1031     #[inline]
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>1032     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
1033         LengthOrPercentage::parse_non_negative(context, input).map(NonNegative::<LengthOrPercentage>)
1034     }
1035 }
1036 
1037 impl NonNegativeLengthOrPercentage {
1038     #[inline]
1039     /// Returns a `zero` length.
zero() -> Self1040     pub fn zero() -> Self {
1041         NonNegative::<LengthOrPercentage>(LengthOrPercentage::zero())
1042     }
1043 
1044     /// Parses a length or a percentage, allowing the unitless length quirk.
1045     /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
1046     #[inline]
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1047     pub fn parse_quirky<'i, 't>(
1048         context: &ParserContext,
1049         input: &mut Parser<'i, 't>,
1050         allow_quirks: AllowQuirks,
1051     ) -> Result<Self, ParseError<'i>> {
1052         LengthOrPercentage::parse_non_negative_quirky(context, input, allow_quirks)
1053             .map(NonNegative::<LengthOrPercentage>)
1054     }
1055 }
1056 
1057 /// Either a `<length>` or the `normal` keyword.
1058 pub type LengthOrNormal = Either<Length, Normal>;
1059 
1060 /// Either a `<length>` or the `auto` keyword.
1061 pub type LengthOrAuto = Either<Length, Auto>;
1062 
1063 /// Either a `<length>` or a `<number>`.
1064 pub type LengthOrNumber = Either<Length, Number>;
1065 
1066 impl LengthOrNumber {
1067     /// Parse a non-negative LengthOrNumber.
parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1068     pub fn parse_non_negative<'i, 't>(
1069         context: &ParserContext,
1070         input: &mut Parser<'i, 't>,
1071     ) -> Result<Self, ParseError<'i>> {
1072         // We try to parse as a Number first because, for cases like
1073         // LengthOrNumber, we want "0" to be parsed as a plain Number rather
1074         // than a Length (0px); this matches the behaviour of all major browsers
1075         if let Ok(v) = input.try(|i| Number::parse_non_negative(context, i)) {
1076             return Ok(Either::Second(v))
1077         }
1078 
1079         Length::parse_non_negative(context, input).map(Either::First)
1080     }
1081 
1082     /// Returns `0`.
1083     #[inline]
zero() -> Self1084     pub fn zero() -> Self {
1085         Either::Second(Number::new(0.))
1086     }
1087 }
1088 
1089 /// A value suitable for a `min-width` or `min-height` property.
1090 /// Unlike `max-width` or `max-height` properties, a MozLength can be
1091 /// `auto`, and cannot be `none`.
1092 #[allow(missing_docs)]
1093 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
1094 pub enum MozLength {
1095     LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
1096     ExtremumLength(ExtremumLength),
1097 }
1098 
1099 impl Parse for MozLength {
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>1100     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
1101         MozLength::parse_quirky(context, input, AllowQuirks::No)
1102     }
1103 }
1104 
1105 impl MozLength {
1106     /// Parses, without quirks, and disallowing ExtremumLength values.
1107     ///
1108     /// Used for logical props in the block direction.
parse_disallow_keyword<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1109     pub fn parse_disallow_keyword<'i, 't>(
1110         context: &ParserContext,
1111         input: &mut Parser<'i, 't>,
1112     ) -> Result<Self, ParseError<'i>> {
1113         let length = LengthOrPercentageOrAuto::parse_non_negative(context, input)?;
1114         Ok(MozLength::LengthOrPercentageOrAuto(length))
1115     }
1116 
1117     /// Parses, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1118     pub fn parse_quirky<'i, 't>(
1119         context: &ParserContext,
1120         input: &mut Parser<'i, 't>,
1121         allow_quirks: AllowQuirks,
1122     ) -> Result<Self, ParseError<'i>> {
1123         if let Ok(l) = input.try(ExtremumLength::parse) {
1124             return Ok(MozLength::ExtremumLength(l));
1125         }
1126 
1127         let length = LengthOrPercentageOrAuto::parse_non_negative_quirky(
1128             context,
1129             input,
1130             allow_quirks,
1131         )?;
1132         Ok(MozLength::LengthOrPercentageOrAuto(length))
1133     }
1134 
1135     /// Returns `auto`.
1136     #[inline]
auto() -> Self1137     pub fn auto() -> Self {
1138         MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::auto())
1139     }
1140 
1141     /// Returns `0%`.
1142     #[inline]
zero_percent() -> Self1143     pub fn zero_percent() -> Self {
1144         MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::zero_percent())
1145     }
1146 }
1147 
1148 /// A value suitable for a `max-width` or `max-height` property.
1149 #[allow(missing_docs)]
1150 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
1151 pub enum MaxLength {
1152     LengthOrPercentageOrNone(LengthOrPercentageOrNone),
1153     ExtremumLength(ExtremumLength),
1154 }
1155 
1156 impl Parse for MaxLength {
parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>1157     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
1158         MaxLength::parse_quirky(context, input, AllowQuirks::No)
1159     }
1160 }
1161 
1162 impl MaxLength {
1163     /// Parses, without quirks, and disallowing ExtremumLength values.
1164     ///
1165     /// Used for logical props in the block direction.
parse_disallow_keyword<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>1166     pub fn parse_disallow_keyword<'i, 't>(
1167         context: &ParserContext,
1168         input: &mut Parser<'i, 't>,
1169     ) -> Result<Self, ParseError<'i>> {
1170         let length = LengthOrPercentageOrNone::parse_non_negative(context, input)?;
1171         Ok(MaxLength::LengthOrPercentageOrNone(length))
1172     }
1173 
1174     /// Parses, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>>1175     pub fn parse_quirky<'i, 't>(
1176         context: &ParserContext,
1177         input: &mut Parser<'i, 't>,
1178         allow_quirks: AllowQuirks,
1179     ) -> Result<Self, ParseError<'i>> {
1180         if let Ok(l) = input.try(ExtremumLength::parse) {
1181             return Ok(MaxLength::ExtremumLength(l));
1182         }
1183 
1184         let length = LengthOrPercentageOrNone::parse_non_negative_quirky(
1185             context,
1186             input,
1187             allow_quirks,
1188         )?;
1189         Ok(MaxLength::LengthOrPercentageOrNone(length))
1190     }
1191 }
1192