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