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