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