/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! [Length values][length]. //! //! [length]: https://drafts.csswg.org/css-values/#lengths use super::{AllowQuirks, Number, Percentage, ToComputedValue}; use crate::font_metrics::{FontMetrics, FontMetricsOrientation}; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{self, CSSPixelLength, Context}; use crate::values::generics::length as generics; use crate::values::generics::length::{ GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, }; use crate::values::generics::NonNegative; use crate::values::specified::calc::{self, CalcNode}; use crate::values::specified::NonNegativeNumber; use crate::values::CSSFloat; use crate::{Zero, ZeroNoPercent}; use app_units::Au; use cssparser::{Parser, Token}; use std::cmp; use std::ops::{Add, Mul, Sub}; use style_traits::values::specified::AllowedNumericType; use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind}; pub use super::image::Image; pub use super::image::{EndingShape as GradientEndingShape, Gradient}; pub use crate::values::specified::calc::CalcLengthPercentage; /// Number of pixels per inch pub const PX_PER_IN: CSSFloat = 96.; /// Number of pixels per centimeter pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54; /// Number of pixels per millimeter pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4; /// Number of pixels per quarter pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.; /// Number of pixels per point pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.; /// Number of pixels per pica pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.; /// A font relative length. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pub enum FontRelativeLength { /// A "em" value: https://drafts.csswg.org/css-values/#em #[css(dimension)] Em(CSSFloat), /// A "ex" value: https://drafts.csswg.org/css-values/#ex #[css(dimension)] Ex(CSSFloat), /// A "ch" value: https://drafts.csswg.org/css-values/#ch #[css(dimension)] Ch(CSSFloat), /// A "cap" value: https://drafts.csswg.org/css-values/#cap #[css(dimension)] Cap(CSSFloat), /// An "ic" value: https://drafts.csswg.org/css-values/#ic #[css(dimension)] Ic(CSSFloat), /// A "rem" value: https://drafts.csswg.org/css-values/#rem #[css(dimension)] Rem(CSSFloat), } /// A source to resolve font-relative units against #[derive(Clone, Copy, Debug, PartialEq)] pub enum FontBaseSize { /// Use the font-size of the current element. CurrentStyle, /// Use the inherited font-size. InheritedStyle, } impl FontBaseSize { /// Calculate the actual size for a given context pub fn resolve(&self, context: &Context) -> computed::FontSize { match *self { FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size(), FontBaseSize::InheritedStyle => context.style().get_parent_font().clone_font_size(), } } } impl FontRelativeLength { /// Return the unitless, raw value. fn unitless_value(&self) -> CSSFloat { match *self { FontRelativeLength::Em(v) | FontRelativeLength::Ex(v) | FontRelativeLength::Ch(v) | FontRelativeLength::Cap(v) | FontRelativeLength::Ic(v) | FontRelativeLength::Rem(v) => v, } } fn try_op(&self, other: &Self, op: O) -> Result where O: Fn(f32, f32) -> f32, { use self::FontRelativeLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return Err(()); } Ok(match (self, other) { (&Em(one), &Em(other)) => Em(op(one, other)), (&Ex(one), &Ex(other)) => Ex(op(one, other)), (&Ch(one), &Ch(other)) => Ch(op(one, other)), (&Cap(one), &Cap(other)) => Cap(op(one, other)), (&Ic(one), &Ic(other)) => Ic(op(one, other)), (&Rem(one), &Rem(other)) => Rem(op(one, other)), // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {}, } debug_unreachable!("Forgot to handle unit in try_op()") }, }) } /// Computes the font-relative length. pub fn to_computed_value( &self, context: &Context, base_size: FontBaseSize, ) -> computed::Length { let (reference_size, length) = self.reference_font_size_and_length(context, base_size); (reference_size * length).normalized() } /// Return reference font size. /// /// We use the base_size flag to pass a different size for computing /// font-size and unconstrained font-size. /// /// This returns a pair, the first one is the reference font size, and the /// second one is the unpacked relative length. fn reference_font_size_and_length( &self, context: &Context, base_size: FontBaseSize, ) -> (computed::Length, CSSFloat) { fn query_font_metrics( context: &Context, base_size: FontBaseSize, orientation: FontMetricsOrientation, ) -> FontMetrics { let retrieve_math_scales = false; context.query_font_metrics(base_size, orientation, retrieve_math_scales) } let reference_font_size = base_size.resolve(context); match *self { FontRelativeLength::Em(length) => { if context.for_non_inherited_property.is_some() { if base_size == FontBaseSize::CurrentStyle { context .rule_cache_conditions .borrow_mut() .set_font_size_dependency(reference_font_size.computed_size); } } (reference_font_size.computed_size(), length) }, FontRelativeLength::Ex(length) => { // The x-height is an intrinsically horizontal metric. let metrics = query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal); let reference_size = metrics.x_height.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#ex // // In the cases where it is impossible or impractical to // determine the x-height, a value of 0.5em must be // assumed. // // (But note we use 0.5em of the used, not computed // font-size) reference_font_size.used_size() * 0.5 }); (reference_size, length) }, FontRelativeLength::Ch(length) => { // https://drafts.csswg.org/css-values/#ch: // // Equal to the used advance measure of the “0” (ZERO, // U+0030) glyph in the font used to render it. (The advance // measure of a glyph is its advance width or height, // whichever is in the inline axis of the element.) // let metrics = query_font_metrics( context, base_size, FontMetricsOrientation::MatchContextPreferHorizontal, ); let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#ch // // In the cases where it is impossible or impractical to // determine the measure of the “0” glyph, it must be // assumed to be 0.5em wide by 1em tall. Thus, the ch // unit falls back to 0.5em in the general case, and to // 1em when it would be typeset upright (i.e. // writing-mode is vertical-rl or vertical-lr and // text-orientation is upright). // // Same caveat about computed vs. used font-size applies // above. let wm = context.style().writing_mode; if wm.is_vertical() && wm.is_upright() { reference_font_size.used_size() } else { reference_font_size.used_size() * 0.5 } }); (reference_size, length) }, FontRelativeLength::Cap(length) => { let metrics = query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal); let reference_size = metrics.cap_height.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#cap // // In the cases where it is impossible or impractical to // determine the cap-height, the font’s ascent must be // used. // metrics.ascent }); (reference_size, length) }, FontRelativeLength::Ic(length) => { let metrics = query_font_metrics( context, base_size, FontMetricsOrientation::MatchContextPreferVertical, ); let reference_size = metrics.ic_width.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#ic // // In the cases where it is impossible or impractical to // determine the ideographic advance measure, it must be // assumed to be 1em. // // Same caveat about computed vs. used as for other // metric-dependent units. reference_font_size.used_size() }); (reference_size, length) }, FontRelativeLength::Rem(length) => { // https://drafts.csswg.org/css-values/#rem: // // When specified on the font-size property of the root // element, the rem units refer to the property's initial // value. // let reference_size = if context.builder.is_root_element || context.in_media_query { reference_font_size.computed_size() } else { context.device().root_font_size() }; (reference_size, length) }, } } } /// https://drafts.csswg.org/css-values/#viewport-variants pub enum ViewportVariant { /// https://drafts.csswg.org/css-values/#ua-default-viewport-size UADefault, /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units Small, /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units Large, /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units Dynamic, } /// https://drafts.csswg.org/css-values/#viewport-relative-units #[derive(PartialEq)] enum ViewportUnit { /// *vw units. Vw, /// *vh units. Vh, /// *vmin units. Vmin, /// *vmax units. Vmax, /// *vb units. Vb, /// *vi units. Vi, } /// A viewport-relative length. /// /// #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pub enum ViewportPercentageLength { /// #[css(dimension)] Vw(CSSFloat), /// #[css(dimension)] Svw(CSSFloat), /// #[css(dimension)] Lvw(CSSFloat), /// #[css(dimension)] Dvw(CSSFloat), /// #[css(dimension)] Vh(CSSFloat), /// #[css(dimension)] Svh(CSSFloat), /// #[css(dimension)] Lvh(CSSFloat), /// #[css(dimension)] Dvh(CSSFloat), /// #[css(dimension)] Vmin(CSSFloat), /// #[css(dimension)] Svmin(CSSFloat), /// #[css(dimension)] Lvmin(CSSFloat), /// #[css(dimension)] Dvmin(CSSFloat), /// #[css(dimension)] Vmax(CSSFloat), /// #[css(dimension)] Svmax(CSSFloat), /// #[css(dimension)] Lvmax(CSSFloat), /// #[css(dimension)] Dvmax(CSSFloat), /// #[css(dimension)] Vb(CSSFloat), /// #[css(dimension)] Svb(CSSFloat), /// #[css(dimension)] Lvb(CSSFloat), /// #[css(dimension)] Dvb(CSSFloat), /// #[css(dimension)] Vi(CSSFloat), /// #[css(dimension)] Svi(CSSFloat), /// #[css(dimension)] Lvi(CSSFloat), /// #[css(dimension)] Dvi(CSSFloat), } impl ViewportPercentageLength { /// Return the unitless, raw value. fn unitless_value(&self) -> CSSFloat { self.unpack().2 } fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) { match *self { ViewportPercentageLength::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v), ViewportPercentageLength::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v), ViewportPercentageLength::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v), ViewportPercentageLength::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v), ViewportPercentageLength::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v), ViewportPercentageLength::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v), ViewportPercentageLength::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v), ViewportPercentageLength::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v), ViewportPercentageLength::Vmin(v) => { (ViewportVariant::UADefault, ViewportUnit::Vmin, v) }, ViewportPercentageLength::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v), ViewportPercentageLength::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v), ViewportPercentageLength::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v), ViewportPercentageLength::Vmax(v) => { (ViewportVariant::UADefault, ViewportUnit::Vmax, v) }, ViewportPercentageLength::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v), ViewportPercentageLength::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v), ViewportPercentageLength::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v), ViewportPercentageLength::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v), ViewportPercentageLength::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v), ViewportPercentageLength::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v), ViewportPercentageLength::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v), ViewportPercentageLength::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v), ViewportPercentageLength::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v), ViewportPercentageLength::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v), ViewportPercentageLength::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v), } } fn try_op(&self, other: &Self, op: O) -> Result where O: Fn(f32, f32) -> f32, { use self::ViewportPercentageLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return Err(()); } Ok(match (self, other) { (&Vw(one), &Vw(other)) => Vw(op(one, other)), (&Svw(one), &Svw(other)) => Svw(op(one, other)), (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)), (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)), (&Vh(one), &Vh(other)) => Vh(op(one, other)), (&Svh(one), &Svh(other)) => Svh(op(one, other)), (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)), (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)), (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)), (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)), (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)), (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)), (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)), (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)), (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)), (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)), (&Vb(one), &Vb(other)) => Vb(op(one, other)), (&Svb(one), &Svb(other)) => Svb(op(one, other)), (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)), (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)), (&Vi(one), &Vi(other)) => Vi(op(one, other)), (&Svi(one), &Svi(other)) => Svi(op(one, other)), (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)), (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)), // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..) | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {}, } debug_unreachable!("Forgot to handle unit in try_op()") }, }) } /// Computes the given viewport-relative length for the given viewport size. pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength { let (variant, unit, factor) = self.unpack(); let size = context.viewport_size_for_viewport_unit_resolution(variant); let length = match unit { ViewportUnit::Vw => size.width, ViewportUnit::Vh => size.height, ViewportUnit::Vmin => cmp::min(size.width, size.height), ViewportUnit::Vmax => cmp::max(size.width, size.height), ViewportUnit::Vi | ViewportUnit::Vb => { context .rule_cache_conditions .borrow_mut() .set_writing_mode_dependency(context.builder.writing_mode); if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() { size.width } else { size.height } }, }; // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform. // See bug 989802. We truncate so that adding multiple viewport units // that add up to 100 does not overflow due to rounding differences let trunc_scaled = ((length.0 as f64) * factor as f64 / 100.).trunc(); Au::from_f64_au(trunc_scaled).into() } } /// HTML5 "character width", as defined in HTML5 § 14.5.4. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pub struct CharacterWidth(pub i32); impl CharacterWidth { /// Computes the given character width. pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length { // This applies the *converting a character width to pixels* algorithm // as specified in HTML5 § 14.5.4. // // TODO(pcwalton): Find these from the font. let average_advance = reference_font_size * 0.5; let max_advance = reference_font_size; average_advance * (self.0 as CSSFloat - 1.0) + max_advance } } /// Represents an absolute length with its unit #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pub enum AbsoluteLength { /// An absolute length in pixels (px) #[css(dimension)] Px(CSSFloat), /// An absolute length in inches (in) #[css(dimension)] In(CSSFloat), /// An absolute length in centimeters (cm) #[css(dimension)] Cm(CSSFloat), /// An absolute length in millimeters (mm) #[css(dimension)] Mm(CSSFloat), /// An absolute length in quarter-millimeters (q) #[css(dimension)] Q(CSSFloat), /// An absolute length in points (pt) #[css(dimension)] Pt(CSSFloat), /// An absolute length in pica (pc) #[css(dimension)] Pc(CSSFloat), } impl AbsoluteLength { /// Return the unitless, raw value. fn unitless_value(&self) -> CSSFloat { match *self { AbsoluteLength::Px(v) | AbsoluteLength::In(v) | AbsoluteLength::Cm(v) | AbsoluteLength::Mm(v) | AbsoluteLength::Q(v) | AbsoluteLength::Pt(v) | AbsoluteLength::Pc(v) => v, } } /// Convert this into a pixel value. #[inline] pub fn to_px(&self) -> CSSFloat { use std::f32; let pixel = match *self { AbsoluteLength::Px(value) => value, AbsoluteLength::In(value) => value * PX_PER_IN, AbsoluteLength::Cm(value) => value * PX_PER_CM, AbsoluteLength::Mm(value) => value * PX_PER_MM, AbsoluteLength::Q(value) => value * PX_PER_Q, AbsoluteLength::Pt(value) => value * PX_PER_PT, AbsoluteLength::Pc(value) => value * PX_PER_PC, }; pixel.min(f32::MAX).max(f32::MIN) } fn try_op(&self, other: &Self, op: O) -> Result where O: Fn(f32, f32) -> f32, { Ok(match (self, other) { (AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(op(*x, *y)), (AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(op(*x, *y)), (AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(op(*x, *y)), (AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(op(*x, *y)), (AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(op(*x, *y)), (AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(op(*x, *y)), (AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(op(*x, *y)), _ => AbsoluteLength::Px(op(self.to_px(), other.to_px())), }) } } impl ToComputedValue for AbsoluteLength { type ComputedValue = CSSPixelLength; fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { CSSPixelLength::new(self.to_px()) } fn from_computed_value(computed: &Self::ComputedValue) -> Self { AbsoluteLength::Px(computed.px()) } } impl PartialOrd for AbsoluteLength { fn partial_cmp(&self, other: &Self) -> Option { self.to_px().partial_cmp(&other.to_px()) } } impl Mul for AbsoluteLength { type Output = AbsoluteLength; #[inline] fn mul(self, scalar: CSSFloat) -> AbsoluteLength { match self { AbsoluteLength::Px(v) => AbsoluteLength::Px(v * scalar), AbsoluteLength::In(v) => AbsoluteLength::In(v * scalar), AbsoluteLength::Cm(v) => AbsoluteLength::Cm(v * scalar), AbsoluteLength::Mm(v) => AbsoluteLength::Mm(v * scalar), AbsoluteLength::Q(v) => AbsoluteLength::Q(v * scalar), AbsoluteLength::Pt(v) => AbsoluteLength::Pt(v * scalar), AbsoluteLength::Pc(v) => AbsoluteLength::Pc(v * scalar), } } } impl Add for AbsoluteLength { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { match (self, rhs) { (AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(x + y), (AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(x + y), (AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(x + y), (AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(x + y), (AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(x + y), (AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(x + y), (AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(x + y), _ => AbsoluteLength::Px(self.to_px() + rhs.to_px()), } } } /// A container query length. /// /// #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pub enum ContainerRelativeLength { /// 1% of query container's width #[css(dimension)] Cqw(CSSFloat), /// 1% of query container's height #[css(dimension)] Cqh(CSSFloat), /// 1% of query container's inline size #[css(dimension)] Cqi(CSSFloat), /// 1% of query container's block size #[css(dimension)] Cqb(CSSFloat), /// The smaller value of `cqi` or `cqb` #[css(dimension)] Cqmin(CSSFloat), /// The larger value of `cqi` or `cqb` #[css(dimension)] Cqmax(CSSFloat), } impl ContainerRelativeLength { fn unitless_value(&self) -> CSSFloat { match *self { ContainerRelativeLength::Cqw(v) | ContainerRelativeLength::Cqh(v) | ContainerRelativeLength::Cqi(v) | ContainerRelativeLength::Cqb(v) | ContainerRelativeLength::Cqmin(v) | ContainerRelativeLength::Cqmax(v) => v, } } pub(crate) fn try_op(&self, other: &Self, op: O) -> Result where O: Fn(f32, f32) -> f32, { use self::ContainerRelativeLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return Err(()); } Ok(match (self, other) { (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)), (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)), (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)), (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)), (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)), (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)), // See https://github.com/rust-lang/rust/issues/68867, then // https://github.com/rust-lang/rust/pull/95161. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {}, } debug_unreachable!("Forgot to handle unit in try_op()") }, }) } /// Computes the given container-relative length. pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength { let size = context.get_container_size_query(); let (factor, container_length) = match *self { ContainerRelativeLength::Cqw(v) => (v, size.get_container_width(context)), ContainerRelativeLength::Cqh(v) => (v, size.get_container_height(context)), ContainerRelativeLength::Cqi(v) => (v, size.get_container_inline_size(context)), ContainerRelativeLength::Cqb(v) => (v, size.get_container_block_size(context)), ContainerRelativeLength::Cqmin(v) => ( v, cmp::min( size.get_container_inline_size(context), size.get_container_block_size(context), ), ), ContainerRelativeLength::Cqmax(v) => ( v, cmp::max( size.get_container_inline_size(context), size.get_container_block_size(context), ), ), }; CSSPixelLength::new(((container_length.to_f64_px()) * factor as f64 / 100.0) as f32) } } #[cfg(feature = "gecko")] fn are_container_queries_enabled() -> bool { static_prefs::pref!("layout.css.container-queries.enabled") } #[cfg(feature = "servo")] fn are_container_queries_enabled() -> bool { false } impl Sub for AbsoluteLength { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self { match (self, rhs) { (AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(x - y), (AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(x - y), (AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(x - y), (AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(x - y), (AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(x - y), (AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(x - y), (AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(x - y), _ => AbsoluteLength::Px(self.to_px() - rhs.to_px()), } } } /// A `` without taking `calc` expressions into account /// /// #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pub enum NoCalcLength { /// An absolute length /// /// Absolute(AbsoluteLength), /// A font-relative length: /// /// FontRelative(FontRelativeLength), /// A viewport-relative length. /// /// ViewportPercentage(ViewportPercentageLength), /// A container query length. /// /// ContainerRelative(ContainerRelativeLength), /// HTML5 "character width", as defined in HTML5 § 14.5.4. /// /// This cannot be specified by the user directly and is only generated by /// `Stylist::synthesize_rules_for_legacy_attributes()`. #[css(function)] ServoCharacterWidth(CharacterWidth), } impl Mul for NoCalcLength { type Output = NoCalcLength; #[inline] fn mul(self, scalar: CSSFloat) -> NoCalcLength { match self { NoCalcLength::Absolute(v) => NoCalcLength::Absolute(v * scalar), NoCalcLength::FontRelative(v) => NoCalcLength::FontRelative(v * scalar), NoCalcLength::ViewportPercentage(v) => NoCalcLength::ViewportPercentage(v * scalar), NoCalcLength::ContainerRelative(v) => NoCalcLength::ContainerRelative(v * scalar), NoCalcLength::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"), } } } impl NoCalcLength { /// Return the unitless, raw value. pub fn unitless_value(&self) -> CSSFloat { match *self { NoCalcLength::Absolute(v) => v.unitless_value(), NoCalcLength::FontRelative(v) => v.unitless_value(), NoCalcLength::ViewportPercentage(v) => v.unitless_value(), NoCalcLength::ContainerRelative(v) => v.unitless_value(), NoCalcLength::ServoCharacterWidth(c) => c.0 as f32, } } /// Returns whether the value of this length without unit is less than zero. pub fn is_negative(&self) -> bool { self.unitless_value().is_sign_negative() } /// Returns whether the value of this length without unit is equal to zero. pub fn is_zero(&self) -> bool { self.unitless_value() == 0.0 } /// Returns whether the value of this length without unit is infinite. pub fn is_infinite(&self) -> bool { self.unitless_value().is_infinite() } /// Returns whether the value of this length without unit is NaN. pub fn is_nan(&self) -> bool { self.unitless_value().is_nan() } /// Whether text-only zoom should be applied to this length. /// /// Generally, font-dependent/relative units don't get text-only-zoomed, /// because the font they're relative to should be zoomed already. pub fn should_zoom_text(&self) -> bool { match *self { Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true, Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false, } } /// Parse a given absolute or relative dimension. pub fn parse_dimension( context: &ParserContext, value: CSSFloat, unit: &str, ) -> Result { Ok(match_ignore_ascii_case! { unit, "px" => NoCalcLength::Absolute(AbsoluteLength::Px(value)), "in" => NoCalcLength::Absolute(AbsoluteLength::In(value)), "cm" => NoCalcLength::Absolute(AbsoluteLength::Cm(value)), "mm" => NoCalcLength::Absolute(AbsoluteLength::Mm(value)), "q" => NoCalcLength::Absolute(AbsoluteLength::Q(value)), "pt" => NoCalcLength::Absolute(AbsoluteLength::Pt(value)), "pc" => NoCalcLength::Absolute(AbsoluteLength::Pc(value)), // font-relative "em" => NoCalcLength::FontRelative(FontRelativeLength::Em(value)), "ex" => NoCalcLength::FontRelative(FontRelativeLength::Ex(value)), "ch" => NoCalcLength::FontRelative(FontRelativeLength::Ch(value)), "cap" => NoCalcLength::FontRelative(FontRelativeLength::Cap(value)), "ic" => NoCalcLength::FontRelative(FontRelativeLength::Ic(value)), "rem" => NoCalcLength::FontRelative(FontRelativeLength::Rem(value)), // viewport percentages "vw" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value)) }, "svw" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Svw(value)) }, "lvw" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Lvw(value)) }, "dvw" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Dvw(value)) }, "vh" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value)) }, "svh" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Svh(value)) }, "lvh" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Lvh(value)) }, "dvh" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Dvh(value)) }, "vmin" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value)) }, "svmin" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Svmin(value)) }, "lvmin" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Lvmin(value)) }, "dvmin" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Dvmin(value)) }, "vmax" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value)) }, "svmax" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Svmax(value)) }, "lvmax" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Lvmax(value)) }, "dvmax" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Dvmax(value)) }, "vb" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vb(value)) }, "svb" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Svb(value)) }, "lvb" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Lvb(value)) }, "dvb" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Dvb(value)) }, "vi" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vi(value)) }, "svi" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Svi(value)) }, "lvi" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Lvi(value)) }, "dvi" if !context.in_page_rule() => { NoCalcLength::ViewportPercentage(ViewportPercentageLength::Dvi(value)) }, // Container query lengths. Inherit the limitation from viewport units since // we may fall back to them. "cqw" if !context.in_page_rule() && are_container_queries_enabled() => { NoCalcLength::ContainerRelative(ContainerRelativeLength::Cqw(value)) }, "cqh" if !context.in_page_rule() && are_container_queries_enabled() => { NoCalcLength::ContainerRelative(ContainerRelativeLength::Cqh(value)) }, "cqi" if !context.in_page_rule() && are_container_queries_enabled() => { NoCalcLength::ContainerRelative(ContainerRelativeLength::Cqi(value)) }, "cqb" if !context.in_page_rule() && are_container_queries_enabled() => { NoCalcLength::ContainerRelative(ContainerRelativeLength::Cqb(value)) }, "cqmin" if !context.in_page_rule() && are_container_queries_enabled() => { NoCalcLength::ContainerRelative(ContainerRelativeLength::Cqmin(value)) }, "cqmax" if !context.in_page_rule() && are_container_queries_enabled() => { NoCalcLength::ContainerRelative(ContainerRelativeLength::Cqmax(value)) }, _ => return Err(()), }) } pub(crate) fn try_op(&self, other: &Self, op: O) -> Result where O: Fn(f32, f32) -> f32, { use self::NoCalcLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return Err(()); } Ok(match (self, other) { (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?), (&FontRelative(ref one), &FontRelative(ref other)) => { FontRelative(one.try_op(other, op)?) }, (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => { ViewportPercentage(one.try_op(other, op)?) }, (&ContainerRelative(ref one), &ContainerRelative(ref other)) => { ContainerRelative(one.try_op(other, op)?) }, (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => { ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32)) }, // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Absolute(..) | FontRelative(..) | ViewportPercentage(..) | ContainerRelative(..) | ServoCharacterWidth(..) => {}, } debug_unreachable!("Forgot to handle unit in try_op()") }, }) } /// Get a px value without context. #[inline] pub fn to_computed_pixel_length_without_context(&self) -> Result { match *self { NoCalcLength::Absolute(len) => Ok(len.to_px()), _ => Err(()), } } /// Get an absolute length from a px value. #[inline] pub fn from_px(px_value: CSSFloat) -> NoCalcLength { NoCalcLength::Absolute(AbsoluteLength::Px(px_value)) } } impl SpecifiedValueInfo for NoCalcLength {} impl PartialOrd for NoCalcLength { fn partial_cmp(&self, other: &Self) -> Option { use self::NoCalcLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return None; } match (self, other) { (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()), (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other), (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => { one.partial_cmp(other) }, (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other), (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => { one.0.partial_cmp(&other.0) }, // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Absolute(..) | FontRelative(..) | ViewportPercentage(..) | ContainerRelative(..) | ServoCharacterWidth(..) => {}, } debug_unreachable!("Forgot an arm in partial_cmp?") }, } } } impl Zero for NoCalcLength { fn zero() -> Self { NoCalcLength::Absolute(AbsoluteLength::Px(0.)) } fn is_zero(&self) -> bool { NoCalcLength::is_zero(self) } } /// An extension to `NoCalcLength` to parse `calc` expressions. /// This is commonly used for the `` values. /// /// #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pub enum Length { /// The internal length type that cannot parse `calc` NoCalc(NoCalcLength), /// A calc expression. /// /// Calc(Box), } impl From for Length { #[inline] fn from(len: NoCalcLength) -> Self { Length::NoCalc(len) } } impl Mul for Length { type Output = Length; #[inline] fn mul(self, scalar: CSSFloat) -> Length { match self { Length::NoCalc(inner) => Length::NoCalc(inner * scalar), Length::Calc(..) => panic!("Can't multiply Calc!"), } } } impl PartialOrd for FontRelativeLength { fn partial_cmp(&self, other: &Self) -> Option { use self::FontRelativeLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return None; } match (self, other) { (&Em(ref one), &Em(ref other)) => one.partial_cmp(other), (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other), (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other), (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other), (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other), (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other), // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {}, } debug_unreachable!("Forgot an arm in partial_cmp?") }, } } } impl Mul for FontRelativeLength { type Output = FontRelativeLength; #[inline] fn mul(self, scalar: CSSFloat) -> FontRelativeLength { match self { FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar), FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar), FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar), FontRelativeLength::Cap(v) => FontRelativeLength::Cap(v * scalar), FontRelativeLength::Ic(v) => FontRelativeLength::Ic(v * scalar), FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar), } } } impl Mul for ContainerRelativeLength { type Output = ContainerRelativeLength; #[inline] fn mul(self, scalar: CSSFloat) -> ContainerRelativeLength { match self { ContainerRelativeLength::Cqw(v) => ContainerRelativeLength::Cqw(v * scalar), ContainerRelativeLength::Cqh(v) => ContainerRelativeLength::Cqh(v * scalar), ContainerRelativeLength::Cqi(v) => ContainerRelativeLength::Cqi(v * scalar), ContainerRelativeLength::Cqb(v) => ContainerRelativeLength::Cqb(v * scalar), ContainerRelativeLength::Cqmin(v) => ContainerRelativeLength::Cqmin(v * scalar), ContainerRelativeLength::Cqmax(v) => ContainerRelativeLength::Cqmax(v * scalar), } } } impl PartialOrd for ContainerRelativeLength { fn partial_cmp(&self, other: &Self) -> Option { use self::ContainerRelativeLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return None; } match (self, other) { (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other), (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other), (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other), (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other), (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other), (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other), // See https://github.com/rust-lang/rust/issues/68867, then // https://github.com/rust-lang/rust/pull/95161. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {}, } debug_unreachable!("Forgot to handle unit in partial_cmp()") }, } } } impl Mul for ViewportPercentageLength { type Output = ViewportPercentageLength; #[inline] fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength { match self { ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar), ViewportPercentageLength::Svw(v) => ViewportPercentageLength::Svw(v * scalar), ViewportPercentageLength::Lvw(v) => ViewportPercentageLength::Lvw(v * scalar), ViewportPercentageLength::Dvw(v) => ViewportPercentageLength::Dvw(v * scalar), ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar), ViewportPercentageLength::Svh(v) => ViewportPercentageLength::Svh(v * scalar), ViewportPercentageLength::Lvh(v) => ViewportPercentageLength::Lvh(v * scalar), ViewportPercentageLength::Dvh(v) => ViewportPercentageLength::Dvh(v * scalar), ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar), ViewportPercentageLength::Svmin(v) => ViewportPercentageLength::Svmin(v * scalar), ViewportPercentageLength::Lvmin(v) => ViewportPercentageLength::Lvmin(v * scalar), ViewportPercentageLength::Dvmin(v) => ViewportPercentageLength::Dvmin(v * scalar), ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar), ViewportPercentageLength::Svmax(v) => ViewportPercentageLength::Svmax(v * scalar), ViewportPercentageLength::Lvmax(v) => ViewportPercentageLength::Lvmax(v * scalar), ViewportPercentageLength::Dvmax(v) => ViewportPercentageLength::Dvmax(v * scalar), ViewportPercentageLength::Vb(v) => ViewportPercentageLength::Vb(v * scalar), ViewportPercentageLength::Svb(v) => ViewportPercentageLength::Svb(v * scalar), ViewportPercentageLength::Lvb(v) => ViewportPercentageLength::Lvb(v * scalar), ViewportPercentageLength::Dvb(v) => ViewportPercentageLength::Dvb(v * scalar), ViewportPercentageLength::Vi(v) => ViewportPercentageLength::Vi(v * scalar), ViewportPercentageLength::Svi(v) => ViewportPercentageLength::Svi(v * scalar), ViewportPercentageLength::Lvi(v) => ViewportPercentageLength::Lvi(v * scalar), ViewportPercentageLength::Dvi(v) => ViewportPercentageLength::Dvi(v * scalar), } } } impl PartialOrd for ViewportPercentageLength { fn partial_cmp(&self, other: &Self) -> Option { use self::ViewportPercentageLength::*; if std::mem::discriminant(self) != std::mem::discriminant(other) { return None; } match (self, other) { (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other), (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other), (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other), (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other), (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other), (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other), (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other), (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other), (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other), (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other), (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other), (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other), (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other), (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other), (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other), (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other), (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other), (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other), (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other), (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other), (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other), (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other), (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other), (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other), // See https://github.com/rust-lang/rust/issues/68867. rustc isn't // able to figure it own on its own so we help. _ => unsafe { match *self { Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..) | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {}, } debug_unreachable!("Forgot an arm in partial_cmp?") }, } } } impl Length { #[inline] fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result> { let location = input.current_source_location(); let token = input.next()?; match *token { Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => { NoCalcLength::parse_dimension(context, value, unit) .map(Length::NoCalc) .map_err(|()| location.new_unexpected_token_error(token.clone())) }, Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { if value != 0. && !context.parsing_mode.allows_unitless_lengths() && !allow_quirks.allowed(context.quirks_mode) { return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px( value, )))) }, Token::Function(ref name) => { let function = CalcNode::math_function(name, location)?; let calc = CalcNode::parse_length(context, input, num_context, function)?; Ok(Length::Calc(Box::new(calc))) }, ref token => return Err(location.new_unexpected_token_error(token.clone())), } } /// Parse a non-negative length #[inline] pub fn parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Self::parse_non_negative_quirky(context, input, AllowQuirks::No) } /// Parse a non-negative length, allowing quirks. #[inline] pub fn parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_internal( context, input, AllowedNumericType::NonNegative, allow_quirks, ) } /// Get an absolute length from a px value. #[inline] pub fn from_px(px_value: CSSFloat) -> Length { Length::NoCalc(NoCalcLength::from_px(px_value)) } } impl Parse for Length { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Self::parse_quirky(context, input, AllowQuirks::No) } } impl Zero for Length { fn zero() -> Self { Length::NoCalc(NoCalcLength::zero()) } fn is_zero(&self) -> bool { // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as // non-zero here? match *self { Length::NoCalc(ref l) => l.is_zero(), Length::Calc(..) => false, } } } impl Length { /// Parses a length, with quirks. pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks) } } /// A wrapper of Length, whose value must be >= 0. pub type NonNegativeLength = NonNegative; impl Parse for NonNegativeLength { #[inline] fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Ok(NonNegative(Length::parse_non_negative(context, input)?)) } } impl From for NonNegativeLength { #[inline] fn from(len: NoCalcLength) -> Self { NonNegative(Length::NoCalc(len)) } } impl From for NonNegativeLength { #[inline] fn from(len: Length) -> Self { NonNegative(len) } } impl NonNegativeLength { /// Get an absolute length from a px value. #[inline] pub fn from_px(px_value: CSSFloat) -> Self { Length::from_px(px_value.max(0.)).into() } /// Parses a non-negative length, optionally with quirks. #[inline] pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Ok(NonNegative(Length::parse_non_negative_quirky( context, input, allow_quirks, )?)) } } /// A `` value. This can be either a ``, a /// ``, or a combination of both via `calc()`. /// /// https://drafts.csswg.org/css-values-4/#typedef-length-percentage #[allow(missing_docs)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pub enum LengthPercentage { Length(NoCalcLength), Percentage(computed::Percentage), Calc(Box), } impl From for LengthPercentage { fn from(len: Length) -> LengthPercentage { match len { Length::NoCalc(l) => LengthPercentage::Length(l), Length::Calc(l) => LengthPercentage::Calc(l), } } } impl From for LengthPercentage { #[inline] fn from(len: NoCalcLength) -> Self { LengthPercentage::Length(len) } } impl From for LengthPercentage { #[inline] fn from(pc: Percentage) -> Self { if let Some(clamping_mode) = pc.calc_clamping_mode() { LengthPercentage::Calc(Box::new(CalcLengthPercentage { clamping_mode, node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())), })) } else { LengthPercentage::Percentage(computed::Percentage(pc.get())) } } } impl From for LengthPercentage { #[inline] fn from(pc: computed::Percentage) -> Self { LengthPercentage::Percentage(pc) } } impl Parse for LengthPercentage { #[inline] fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Self::parse_quirky(context, input, AllowQuirks::No) } } impl LengthPercentage { #[inline] /// Returns a `0%` value. pub fn zero_percent() -> LengthPercentage { LengthPercentage::Percentage(computed::Percentage::zero()) } #[inline] /// Returns a `100%` value. pub fn hundred_percent() -> LengthPercentage { LengthPercentage::Percentage(computed::Percentage::hundred()) } fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result> { let location = input.current_source_location(); let token = input.next()?; match *token { Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => { return NoCalcLength::parse_dimension(context, value, unit) .map(LengthPercentage::Length) .map_err(|()| location.new_unexpected_token_error(token.clone())); }, Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => { return Ok(LengthPercentage::Percentage(computed::Percentage( unit_value, ))); }, Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { if value != 0. && !context.parsing_mode.allows_unitless_lengths() && !allow_quirks.allowed(context.quirks_mode) { return Err(location.new_unexpected_token_error(token.clone())); } else { return Ok(LengthPercentage::Length(NoCalcLength::from_px(value))); } }, Token::Function(ref name) => { let function = CalcNode::math_function(name, location)?; let calc = CalcNode::parse_length_or_percentage(context, input, num_context, function)?; Ok(LengthPercentage::Calc(Box::new(calc))) }, _ => return Err(location.new_unexpected_token_error(token.clone())), } } /// Parses allowing the unitless length quirk. /// #[inline] pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks) } /// Parse a non-negative length. /// /// FIXME(emilio): This should be not public and we should use /// NonNegativeLengthPercentage instead. #[inline] pub fn parse_non_negative<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Self::parse_non_negative_quirky(context, input, AllowQuirks::No) } /// Parse a non-negative length, with quirks. #[inline] pub fn parse_non_negative_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_internal( context, input, AllowedNumericType::NonNegative, allow_quirks, ) } } impl Zero for LengthPercentage { fn zero() -> Self { LengthPercentage::Length(NoCalcLength::zero()) } fn is_zero(&self) -> bool { match *self { LengthPercentage::Length(l) => l.is_zero(), LengthPercentage::Percentage(p) => p.0 == 0.0, LengthPercentage::Calc(_) => false, } } } impl ZeroNoPercent for LengthPercentage { fn is_zero_no_percent(&self) -> bool { match *self { LengthPercentage::Percentage(_) => false, _ => self.is_zero(), } } } /// A specified type for ` | auto`. pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto; impl LengthPercentageOrAuto { /// Returns a value representing `0%`. #[inline] pub fn zero_percent() -> Self { generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent()) } /// Parses a length or a percentage, allowing the unitless length quirk. /// #[inline] pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_with(context, input, |context, input| { LengthPercentage::parse_quirky(context, input, allow_quirks) }) } } /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0. pub type NonNegativeLengthPercentageOrAuto = generics::LengthPercentageOrAuto; impl NonNegativeLengthPercentageOrAuto { /// Returns a value representing `0%`. #[inline] pub fn zero_percent() -> Self { generics::LengthPercentageOrAuto::LengthPercentage( NonNegativeLengthPercentage::zero_percent(), ) } /// Parses a non-negative length-percentage, allowing the unitless length /// quirk. #[inline] pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_with(context, input, |context, input| { NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks) }) } } /// A wrapper of LengthPercentage, whose value must be >= 0. pub type NonNegativeLengthPercentage = NonNegative; /// Either a NonNegativeLengthPercentage or the `normal` keyword. pub type NonNegativeLengthPercentageOrNormal = GenericLengthPercentageOrNormal; impl From for NonNegativeLengthPercentage { #[inline] fn from(len: NoCalcLength) -> Self { NonNegative(LengthPercentage::from(len)) } } impl Parse for NonNegativeLengthPercentage { #[inline] fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Self::parse_quirky(context, input, AllowQuirks::No) } } impl NonNegativeLengthPercentage { #[inline] /// Returns a `0%` value. pub fn zero_percent() -> Self { NonNegative(LengthPercentage::zero_percent()) } /// Parses a length or a percentage, allowing the unitless length quirk. /// #[inline] pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative) } } /// Either a `` or the `auto` keyword. /// /// Note that we use LengthPercentage just for convenience, since it pretty much /// is everything we care about, but we could just add a similar LengthOrAuto /// instead if we think getting rid of this weirdness is worth it. pub type LengthOrAuto = generics::LengthPercentageOrAuto; impl LengthOrAuto { /// Parses a length, allowing the unitless length quirk. /// #[inline] pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { Self::parse_with(context, input, |context, input| { Length::parse_quirky(context, input, allow_quirks) }) } } /// Either a non-negative `` or the `auto` keyword. pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto; /// Either a `` or a ``. pub type LengthOrNumber = GenericLengthOrNumber; /// A specified value for `min-width`, `min-height`, `width` or `height` property. pub type Size = GenericSize; impl Parse for Size { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { Size::parse_quirky(context, input, AllowQuirks::No) } } macro_rules! parse_size_non_length { ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{ let size = $input.try_parse(|input| { Ok(try_match_ident_ignore_ascii_case! { input, #[cfg(feature = "gecko")] "min-content" | "-moz-min-content" => $size::MinContent, #[cfg(feature = "gecko")] "max-content" | "-moz-max-content" => $size::MaxContent, #[cfg(feature = "gecko")] "fit-content" | "-moz-fit-content" => $size::FitContent, #[cfg(feature = "gecko")] "-moz-available" => $size::MozAvailable, $auto_or_none => $size::$auto_or_none_ident, }) }); if size.is_ok() { return size; } }}; } #[cfg(feature = "gecko")] fn is_fit_content_function_enabled() -> bool { static_prefs::pref!("layout.css.fit-content-function.enabled") } #[cfg(feature = "servo")] fn is_fit_content_function_enabled() -> bool { false } macro_rules! parse_fit_content_function { ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => { if is_fit_content_function_enabled() { if let Ok(length) = $input.try_parse(|input| { input.expect_function_matching("fit-content")?; input.parse_nested_block(|i| { NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks) }) }) { return Ok($size::FitContentFunction(length)); } } }; } impl Size { /// Parses, with quirks. pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { parse_size_non_length!(Size, input, "auto" => Auto); parse_fit_content_function!(Size, input, context, allow_quirks); let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; Ok(GenericSize::LengthPercentage(length)) } /// Returns `0%`. #[inline] pub fn zero_percent() -> Self { GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent()) } } /// A specified value for `max-width` or `max-height` property. pub type MaxSize = GenericMaxSize; impl Parse for MaxSize { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { MaxSize::parse_quirky(context, input, AllowQuirks::No) } } impl MaxSize { /// Parses, with quirks. pub fn parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result> { parse_size_non_length!(MaxSize, input, "none" => None); parse_fit_content_function!(MaxSize, input, context, allow_quirks); let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; Ok(GenericMaxSize::LengthPercentage(length)) } } /// A specified non-negative `` | ``. pub type NonNegativeLengthOrNumber = GenericLengthOrNumber;