servo/components/style/values/computed/length.rs
Matt Brubeck f6404f0ec2 Update dependencies to use new_debug_unrechable
Because reem/rust-debug-unreachable#6 makes `debug_unreachable` enable debug checks even in release builds since Rust 1.0.
2018-06-06 08:54:51 -07:00

1085 lines
35 KiB
Rust

/* 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 http://mozilla.org/MPL/2.0/. */
//! `<length>` computed values, and related ones.
use app_units::Au;
use logical_geometry::WritingMode;
use ordered_float::NotNan;
use properties::LonghandId;
use std::fmt::{self, Write};
use std::ops::{Add, Neg};
use style_traits::{CssWriter, ToCss};
use style_traits::values::specified::AllowedNumericType;
use super::{Context, Number, Percentage, ToComputedValue};
use values::{specified, Auto, CSSFloat, Either, Normal};
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::NonNegative;
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
use values::specified::length::ViewportPercentageLength;
pub use super::image::Image;
pub use values::specified::url::UrlOrNone;
pub use values::specified::{Angle, BorderStyle, Time};
impl ToComputedValue for specified::NoCalcLength {
type ComputedValue = CSSPixelLength;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
specified::NoCalcLength::Absolute(length) => length.to_computed_value(context),
specified::NoCalcLength::FontRelative(length) => {
length.to_computed_value(context, FontBaseSize::CurrentStyle)
},
specified::NoCalcLength::ViewportPercentage(length) => {
length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
},
specified::NoCalcLength::ServoCharacterWidth(length) => {
length.to_computed_value(context.style().get_font().clone_font_size().size())
},
}
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
specified::NoCalcLength::Absolute(AbsoluteLength::Px(computed.px()))
}
}
impl ToComputedValue for specified::Length {
type ComputedValue = CSSPixelLength;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
specified::Length::NoCalc(l) => l.to_computed_value(context),
specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(),
}
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
specified::Length::NoCalc(specified::NoCalcLength::from_computed_value(computed))
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero)]
pub struct CalcLengthOrPercentage {
#[animation(constant)]
pub clamping_mode: AllowedNumericType,
length: Length,
pub percentage: Option<Percentage>,
}
impl ComputeSquaredDistance for CalcLengthOrPercentage {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// FIXME(nox): This looks incorrect to me, to add a distance between lengths
// with a distance between percentages.
Ok(self.unclamped_length()
.compute_squared_distance(&other.unclamped_length())? +
self.percentage()
.compute_squared_distance(&other.percentage())?)
}
}
impl CalcLengthOrPercentage {
/// Returns a new `CalcLengthOrPercentage`.
#[inline]
pub fn new(length: Length, percentage: Option<Percentage>) -> Self {
Self::with_clamping_mode(length, percentage, AllowedNumericType::All)
}
/// Returns a new `CalcLengthOrPercentage` with a specific clamping mode.
#[inline]
pub fn with_clamping_mode(
length: Length,
percentage: Option<Percentage>,
clamping_mode: AllowedNumericType,
) -> Self {
Self {
clamping_mode,
length,
percentage,
}
}
/// Returns this `calc()` as a `<length>`.
///
/// Panics in debug mode if a percentage is present in the expression.
#[inline]
pub fn length(&self) -> CSSPixelLength {
debug_assert!(self.percentage.is_none());
self.length_component()
}
/// Returns the length component of this `calc()`
#[inline]
pub fn length_component(&self) -> CSSPixelLength {
CSSPixelLength::new(self.clamping_mode.clamp(self.length.px()))
}
/// Returns the `<length>` component of this `calc()`, unclamped.
#[inline]
pub fn unclamped_length(&self) -> CSSPixelLength {
self.length
}
/// Return the percentage value as CSSFloat.
#[inline]
pub fn percentage(&self) -> CSSFloat {
self.percentage.map_or(0., |p| p.0)
}
/// Convert the computed value into used value.
#[inline]
pub fn to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
self.to_pixel_length(container_len).map(Au::from)
}
/// If there are special rules for computing percentages in a value (e.g.
/// the height property), they apply whenever a calc() expression contains
/// percentages.
pub fn to_pixel_length(&self, container_len: Option<Au>) -> Option<Length> {
match (container_len, self.percentage) {
(Some(len), Some(percent)) => {
let pixel = self.length.px() + len.scale_by(percent.0).to_f32_px();
Some(Length::new(self.clamping_mode.clamp(pixel)))
},
(_, None) => Some(self.length()),
_ => None,
}
}
}
impl From<LengthOrPercentage> for CalcLengthOrPercentage {
fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage {
match len {
LengthOrPercentage::Percentage(this) => {
CalcLengthOrPercentage::new(Length::new(0.), Some(this))
},
LengthOrPercentage::Length(this) => CalcLengthOrPercentage::new(this, None),
LengthOrPercentage::Calc(this) => this,
}
}
}
impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> {
match len {
LengthOrPercentageOrAuto::Percentage(this) => {
Some(CalcLengthOrPercentage::new(Length::new(0.), Some(this)))
},
LengthOrPercentageOrAuto::Length(this) => Some(CalcLengthOrPercentage::new(this, None)),
LengthOrPercentageOrAuto::Calc(this) => Some(this),
LengthOrPercentageOrAuto::Auto => None,
}
}
}
impl From<LengthOrPercentageOrNone> for Option<CalcLengthOrPercentage> {
fn from(len: LengthOrPercentageOrNone) -> Option<CalcLengthOrPercentage> {
match len {
LengthOrPercentageOrNone::Percentage(this) => {
Some(CalcLengthOrPercentage::new(Length::new(0.), Some(this)))
},
LengthOrPercentageOrNone::Length(this) => Some(CalcLengthOrPercentage::new(this, None)),
LengthOrPercentageOrNone::Calc(this) => Some(this),
LengthOrPercentageOrNone::None => None,
}
}
}
impl ToCss for CalcLengthOrPercentage {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
use num_traits::Zero;
let (length, percentage) = match (self.length, self.percentage) {
(l, None) => return l.to_css(dest),
(l, Some(p)) if l.px() == 0. => return p.to_css(dest),
(l, Some(p)) => (l, p),
};
dest.write_str("calc(")?;
percentage.to_css(dest)?;
dest.write_str(if length.px() < Zero::zero() {
" - "
} else {
" + "
})?;
length.abs().to_css(dest)?;
dest.write_str(")")
}
}
impl specified::CalcLengthOrPercentage {
/// Compute the value, zooming any absolute units by the zoom function.
fn to_computed_value_with_zoom<F>(
&self,
context: &Context,
zoom_fn: F,
base_size: FontBaseSize,
) -> CalcLengthOrPercentage
where
F: Fn(Length) -> Length,
{
use std::f32;
let mut length = 0.;
if let Some(absolute) = self.absolute {
length += zoom_fn(absolute.to_computed_value(context)).px();
}
for val in &[
self.vw.map(ViewportPercentageLength::Vw),
self.vh.map(ViewportPercentageLength::Vh),
self.vmin.map(ViewportPercentageLength::Vmin),
self.vmax.map(ViewportPercentageLength::Vmax),
] {
if let Some(val) = *val {
let viewport_size = context.viewport_size_for_viewport_unit_resolution();
length += val.to_computed_value(viewport_size).px();
}
}
for val in &[
self.ch.map(FontRelativeLength::Ch),
self.em.map(FontRelativeLength::Em),
self.ex.map(FontRelativeLength::Ex),
self.rem.map(FontRelativeLength::Rem),
] {
if let Some(val) = *val {
length += val.to_computed_value(context, base_size).px();
}
}
CalcLengthOrPercentage {
clamping_mode: self.clamping_mode,
length: Length::new(length.min(f32::MAX).max(f32::MIN)),
percentage: self.percentage,
}
}
/// Compute font-size or line-height taking into account text-zoom if necessary.
pub fn to_computed_value_zoomed(
&self,
context: &Context,
base_size: FontBaseSize,
) -> CalcLengthOrPercentage {
self.to_computed_value_with_zoom(
context,
|abs| context.maybe_zoom_text(abs.into()).0,
base_size,
)
}
/// Compute the value into pixel length as CSSFloat without context,
/// so it returns Err(()) if there is any non-absolute unit.
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
if self.vw.is_some() || self.vh.is_some() || self.vmin.is_some() || self.vmax.is_some() ||
self.em.is_some() || self.ex.is_some() || self.ch.is_some() ||
self.rem.is_some() || self.percentage.is_some()
{
return Err(());
}
match self.absolute {
Some(abs) => Ok(abs.to_px()),
None => {
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
Err(())
},
}
}
}
impl ToComputedValue for specified::CalcLengthOrPercentage {
type ComputedValue = CalcLengthOrPercentage;
fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage {
// normal properties don't zoom, and compute em units against the current style's font-size
self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
}
#[inline]
fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self {
specified::CalcLengthOrPercentage {
clamping_mode: computed.clamping_mode,
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
percentage: computed.percentage,
..Default::default()
}
}
}
#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[css(derive_debug)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq,
ToAnimatedValue, ToAnimatedZero, ToCss)]
#[distance(fallback = "Self::compute_squared_distance_fallback")]
pub enum LengthOrPercentage {
Length(Length),
Percentage(Percentage),
Calc(CalcLengthOrPercentage),
}
impl LengthOrPercentage {
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
// Special handling for zero values since these should not require calc().
if self.is_definitely_zero() {
return other.to_animated_zero()?.animate(other, procedure);
}
if other.is_definitely_zero() {
return self.animate(&self.to_animated_zero()?, procedure);
}
let this = CalcLengthOrPercentage::from(*self);
let other = CalcLengthOrPercentage::from(*other);
Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
}
#[inline]
fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> {
CalcLengthOrPercentage::compute_squared_distance(&(*self).into(), &(*other).into())
}
}
impl From<Au> for LengthOrPercentage {
#[inline]
fn from(length: Au) -> Self {
LengthOrPercentage::Length(length.into())
}
}
impl LengthOrPercentage {
#[inline]
#[allow(missing_docs)]
pub fn zero() -> LengthOrPercentage {
LengthOrPercentage::Length(Length::new(0.))
}
#[inline]
/// 1px length value for SVG defaults
pub fn one() -> LengthOrPercentage {
LengthOrPercentage::Length(Length::new(1.))
}
/// Returns true if the computed value is absolute 0 or 0%.
///
/// (Returns false for calc() values, even if ones that may resolve to zero.)
#[inline]
pub fn is_definitely_zero(&self) -> bool {
use self::LengthOrPercentage::*;
match *self {
Length(l) => l.px() == 0.0,
Percentage(p) => p.0 == 0.0,
Calc(_) => false,
}
}
// CSSFloat doesn't implement Hash, so does CSSPixelLength. Therefore, we still use Au as the
// hash key.
#[allow(missing_docs)]
pub fn to_hash_key(&self) -> (Au, NotNan<f32>) {
use self::LengthOrPercentage::*;
match *self {
Length(l) => (Au::from(l), NotNan::new(0.0).unwrap()),
Percentage(p) => (Au(0), NotNan::new(p.0).unwrap()),
Calc(c) => (
Au::from(c.unclamped_length()),
NotNan::new(c.percentage()).unwrap(),
),
}
}
/// Returns the used value.
pub fn to_used_value(&self, containing_length: Au) -> Au {
Au::from(self.to_pixel_length(containing_length))
}
/// Returns the used value as CSSPixelLength.
pub fn to_pixel_length(&self, containing_length: Au) -> Length {
match *self {
LengthOrPercentage::Length(length) => length,
LengthOrPercentage::Percentage(p) => containing_length.scale_by(p.0).into(),
LengthOrPercentage::Calc(ref calc) => {
calc.to_pixel_length(Some(containing_length)).unwrap()
},
}
}
/// Returns the clamped non-negative values.
#[inline]
pub fn clamp_to_non_negative(self) -> Self {
match self {
LengthOrPercentage::Length(length) => {
LengthOrPercentage::Length(length.clamp_to_non_negative())
},
LengthOrPercentage::Percentage(percentage) => {
LengthOrPercentage::Percentage(percentage.clamp_to_non_negative())
},
_ => self,
}
}
}
impl ToComputedValue for specified::LengthOrPercentage {
type ComputedValue = LengthOrPercentage;
fn to_computed_value(&self, context: &Context) -> LengthOrPercentage {
match *self {
specified::LengthOrPercentage::Length(ref value) => {
LengthOrPercentage::Length(value.to_computed_value(context))
},
specified::LengthOrPercentage::Percentage(value) => {
LengthOrPercentage::Percentage(value)
},
specified::LengthOrPercentage::Calc(ref calc) => {
LengthOrPercentage::Calc((**calc).to_computed_value(context))
},
}
}
fn from_computed_value(computed: &LengthOrPercentage) -> Self {
match *computed {
LengthOrPercentage::Length(value) => {
specified::LengthOrPercentage::Length(ToComputedValue::from_computed_value(&value))
},
LengthOrPercentage::Percentage(value) => {
specified::LengthOrPercentage::Percentage(value)
},
LengthOrPercentage::Calc(ref calc) => specified::LengthOrPercentage::Calc(Box::new(
ToComputedValue::from_computed_value(calc),
)),
}
}
}
#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[css(derive_debug)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq, ToCss)]
#[distance(fallback = "Self::compute_squared_distance_fallback")]
pub enum LengthOrPercentageOrAuto {
Length(Length),
Percentage(Percentage),
Auto,
Calc(CalcLengthOrPercentage),
}
impl LengthOrPercentageOrAuto {
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
let this = <Option<CalcLengthOrPercentage>>::from(*self);
let other = <Option<CalcLengthOrPercentage>>::from(*other);
Ok(LengthOrPercentageOrAuto::Calc(this.animate(
&other,
procedure,
)?
.ok_or(())?))
}
#[inline]
fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> {
<Option<CalcLengthOrPercentage>>::compute_squared_distance(
&(*self).into(),
&(*other).into(),
)
}
}
/// A wrapper of LengthOrPercentageOrAuto, whose value must be >= 0.
pub type NonNegativeLengthOrPercentageOrAuto = NonNegative<LengthOrPercentageOrAuto>;
impl NonNegativeLengthOrPercentageOrAuto {
/// `auto`
#[inline]
pub fn auto() -> Self {
NonNegative(LengthOrPercentageOrAuto::Auto)
}
}
impl ToAnimatedValue for NonNegativeLengthOrPercentageOrAuto {
type AnimatedValue = LengthOrPercentageOrAuto;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
self.0
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
NonNegative(animated.clamp_to_non_negative())
}
}
impl LengthOrPercentageOrAuto {
/// Returns true if the computed value is absolute 0 or 0%.
///
/// (Returns false for calc() values, even if ones that may resolve to zero.)
#[inline]
pub fn is_definitely_zero(&self) -> bool {
use self::LengthOrPercentageOrAuto::*;
match *self {
Length(l) => l.px() == 0.0,
Percentage(p) => p.0 == 0.0,
Calc(_) | Auto => false,
}
}
fn clamp_to_non_negative(self) -> Self {
use self::LengthOrPercentageOrAuto::*;
match self {
Length(l) => Length(l.clamp_to_non_negative()),
Percentage(p) => Percentage(p.clamp_to_non_negative()),
_ => self,
}
}
}
impl ToComputedValue for specified::LengthOrPercentageOrAuto {
type ComputedValue = LengthOrPercentageOrAuto;
#[inline]
fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrAuto {
match *self {
specified::LengthOrPercentageOrAuto::Length(ref value) => {
LengthOrPercentageOrAuto::Length(value.to_computed_value(context))
},
specified::LengthOrPercentageOrAuto::Percentage(value) => {
LengthOrPercentageOrAuto::Percentage(value)
},
specified::LengthOrPercentageOrAuto::Auto => LengthOrPercentageOrAuto::Auto,
specified::LengthOrPercentageOrAuto::Calc(ref calc) => {
LengthOrPercentageOrAuto::Calc((**calc).to_computed_value(context))
},
}
}
#[inline]
fn from_computed_value(computed: &LengthOrPercentageOrAuto) -> Self {
match *computed {
LengthOrPercentageOrAuto::Auto => specified::LengthOrPercentageOrAuto::Auto,
LengthOrPercentageOrAuto::Length(value) => specified::LengthOrPercentageOrAuto::Length(
ToComputedValue::from_computed_value(&value),
),
LengthOrPercentageOrAuto::Percentage(value) => {
specified::LengthOrPercentageOrAuto::Percentage(value)
},
LengthOrPercentageOrAuto::Calc(calc) => specified::LengthOrPercentageOrAuto::Calc(
Box::new(ToComputedValue::from_computed_value(&calc)),
),
}
}
}
#[allow(missing_docs)]
#[animate(fallback = "Self::animate_fallback")]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[css(derive_debug)]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)]
#[distance(fallback = "Self::compute_squared_distance_fallback")]
pub enum LengthOrPercentageOrNone {
Length(Length),
Percentage(Percentage),
Calc(CalcLengthOrPercentage),
None,
}
impl LengthOrPercentageOrNone {
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
let this = <Option<CalcLengthOrPercentage>>::from(*self);
let other = <Option<CalcLengthOrPercentage>>::from(*other);
Ok(LengthOrPercentageOrNone::Calc(this.animate(
&other,
procedure,
)?
.ok_or(())?))
}
fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> {
<Option<CalcLengthOrPercentage>>::compute_squared_distance(
&(*self).into(),
&(*other).into(),
)
}
}
impl LengthOrPercentageOrNone {
/// Returns the used value.
pub fn to_used_value(&self, containing_length: Au) -> Option<Au> {
match *self {
LengthOrPercentageOrNone::None => None,
LengthOrPercentageOrNone::Length(length) => Some(Au::from(length)),
LengthOrPercentageOrNone::Percentage(percent) => {
Some(containing_length.scale_by(percent.0))
},
LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)),
}
}
}
impl ToComputedValue for specified::LengthOrPercentageOrNone {
type ComputedValue = LengthOrPercentageOrNone;
#[inline]
fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrNone {
match *self {
specified::LengthOrPercentageOrNone::Length(ref value) => {
LengthOrPercentageOrNone::Length(value.to_computed_value(context))
},
specified::LengthOrPercentageOrNone::Percentage(value) => {
LengthOrPercentageOrNone::Percentage(value)
},
specified::LengthOrPercentageOrNone::Calc(ref calc) => {
LengthOrPercentageOrNone::Calc((**calc).to_computed_value(context))
},
specified::LengthOrPercentageOrNone::None => LengthOrPercentageOrNone::None,
}
}
#[inline]
fn from_computed_value(computed: &LengthOrPercentageOrNone) -> Self {
match *computed {
LengthOrPercentageOrNone::None => specified::LengthOrPercentageOrNone::None,
LengthOrPercentageOrNone::Length(value) => specified::LengthOrPercentageOrNone::Length(
ToComputedValue::from_computed_value(&value),
),
LengthOrPercentageOrNone::Percentage(value) => {
specified::LengthOrPercentageOrNone::Percentage(value)
},
LengthOrPercentageOrNone::Calc(calc) => specified::LengthOrPercentageOrNone::Calc(
Box::new(ToComputedValue::from_computed_value(&calc)),
),
}
}
}
/// A wrapper of LengthOrPercentage, whose value must be >= 0.
pub type NonNegativeLengthOrPercentage = NonNegative<LengthOrPercentage>;
impl ToAnimatedValue for NonNegativeLengthOrPercentage {
type AnimatedValue = LengthOrPercentage;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
self.into()
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
animated.clamp_to_non_negative().into()
}
}
impl From<NonNegativeLength> for NonNegativeLengthOrPercentage {
#[inline]
fn from(length: NonNegativeLength) -> Self {
LengthOrPercentage::Length(length.0).into()
}
}
impl From<LengthOrPercentage> for NonNegativeLengthOrPercentage {
#[inline]
fn from(lop: LengthOrPercentage) -> Self {
NonNegative::<LengthOrPercentage>(lop)
}
}
impl From<NonNegativeLengthOrPercentage> for LengthOrPercentage {
#[inline]
fn from(lop: NonNegativeLengthOrPercentage) -> LengthOrPercentage {
lop.0
}
}
impl NonNegativeLengthOrPercentage {
/// Get zero value.
#[inline]
pub fn zero() -> Self {
NonNegative::<LengthOrPercentage>(LengthOrPercentage::zero())
}
/// Returns true if the computed value is absolute 0 or 0%.
#[inline]
pub fn is_definitely_zero(&self) -> bool {
self.0.is_definitely_zero()
}
/// Returns the used value.
#[inline]
pub fn to_used_value(&self, containing_length: Au) -> Au {
self.0.to_used_value(containing_length)
}
}
/// The computed `<length>` value.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
PartialOrd, ToAnimatedValue, ToAnimatedZero)]
pub struct CSSPixelLength(CSSFloat);
impl CSSPixelLength {
/// Return a new CSSPixelLength.
#[inline]
pub fn new(px: CSSFloat) -> Self {
CSSPixelLength(px)
}
/// Return the containing pixel value.
#[inline]
pub fn px(&self) -> CSSFloat {
self.0
}
#[inline]
fn clamp_to_non_negative(self) -> Self {
Self::new(self.px().max(0.))
}
/// Return the length with app_unit i32 type.
#[inline]
pub fn to_i32_au(&self) -> i32 {
Au::from(*self).0
}
/// Return the absolute value of this length.
pub fn abs(self) -> Self {
CSSPixelLength::new(self.0.abs())
}
/// Zero value
pub fn zero() -> Self {
CSSPixelLength::new(0.)
}
}
impl ToCss for CSSPixelLength {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.0.to_css(dest)?;
dest.write_str("px")
}
}
impl Neg for CSSPixelLength {
type Output = Self;
#[inline]
fn neg(self) -> Self {
CSSPixelLength::new(-self.0)
}
}
impl From<CSSPixelLength> for Au {
#[inline]
fn from(len: CSSPixelLength) -> Self {
Au::from_f32_px(len.0)
}
}
impl From<Au> for CSSPixelLength {
#[inline]
fn from(len: Au) -> Self {
CSSPixelLength::new(len.to_f32_px())
}
}
/// An alias of computed `<length>` value.
pub type Length = CSSPixelLength;
/// Either a computed `<length>` or the `auto` keyword.
pub type LengthOrAuto = Either<Length, Auto>;
/// Either a computed `<length>` or a `<number>` value.
pub type LengthOrNumber = Either<Length, Number>;
impl LengthOrNumber {
/// Returns `0`.
#[inline]
pub fn zero() -> Self {
Either::Second(0.)
}
}
/// Either a computed `<length>` or the `normal` keyword.
pub type LengthOrNormal = Either<Length, Normal>;
/// A wrapper of Length, whose value must be >= 0.
pub type NonNegativeLength = NonNegative<Length>;
impl ToAnimatedValue for NonNegativeLength {
type AnimatedValue = Length;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
self.0
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
NonNegativeLength::new(animated.px().max(0.))
}
}
impl NonNegativeLength {
/// Create a NonNegativeLength.
#[inline]
pub fn new(px: CSSFloat) -> Self {
NonNegative(Length::new(px.max(0.)))
}
/// Return a zero value.
#[inline]
pub fn zero() -> Self {
Self::new(0.)
}
/// Return the pixel value of |NonNegativeLength|.
#[inline]
pub fn px(&self) -> CSSFloat {
self.0.px()
}
#[inline]
/// Ensures it is non negative
pub fn clamp(self) -> Self {
if (self.0).0 < 0. {
Self::zero()
} else {
self
}
}
/// Scale this NonNegativeLength.
/// We scale NonNegativeLength by zero if the factor is negative because it doesn't
/// make sense to scale a negative factor on a non-negative length.
#[inline]
pub fn scale_by(&self, factor: f32) -> Self {
Self::new(self.0.px() * factor.max(0.))
}
}
impl Add<NonNegativeLength> for NonNegativeLength {
type Output = Self;
fn add(self, other: Self) -> Self {
NonNegativeLength::new(self.px() + other.px())
}
}
impl From<Length> for NonNegativeLength {
#[inline]
fn from(len: Length) -> Self {
NonNegative(len)
}
}
impl From<Au> for NonNegativeLength {
#[inline]
fn from(au: Au) -> Self {
NonNegative(au.into())
}
}
impl From<NonNegativeLength> for Au {
#[inline]
fn from(non_negative_len: NonNegativeLength) -> Self {
Au::from(non_negative_len.0)
}
}
/// Either a computed NonNegativeLength or the `auto` keyword.
pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;
/// Either a computed NonNegativeLength or the `normal` keyword.
pub type NonNegativeLengthOrNormal = Either<NonNegativeLength, Normal>;
/// Either a computed NonNegativeLengthOrPercentage or the `normal` keyword.
pub type NonNegativeLengthOrPercentageOrNormal = Either<NonNegativeLengthOrPercentage, Normal>;
/// A type for possible values for min- and max- flavors of width, height,
/// block-size, and inline-size.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
SpecifiedValueInfo, ToCss)]
pub enum ExtremumLength {
MozMaxContent,
MozMinContent,
MozFitContent,
MozAvailable,
}
impl ExtremumLength {
/// Returns whether this size keyword can be used for the given writing-mode
/// and property.
///
/// TODO: After these values are supported for both axes (and maybe
/// unprefixed, see bug 1322780) all this complexity can go away, and
/// everything can be derived (no need for uncacheable stuff).
fn valid_for(&self, wm: WritingMode, longhand: LonghandId) -> bool {
// We only make sense on the inline axis.
match longhand {
// FIXME(emilio): The flex-basis thing is not quite clear...
LonghandId::FlexBasis |
LonghandId::MinWidth |
LonghandId::MaxWidth |
LonghandId::Width => !wm.is_vertical(),
LonghandId::MinHeight | LonghandId::MaxHeight | LonghandId::Height => wm.is_vertical(),
LonghandId::MinInlineSize | LonghandId::MaxInlineSize | LonghandId::InlineSize => true,
// The block-* properties are rejected at parse-time, so they're
// unexpected here.
_ => {
debug_assert!(
false,
"Unexpected property using ExtremumLength: {:?}",
longhand,
);
false
},
}
}
}
/// A value suitable for a `min-width`, `min-height`, `width` or `height`
/// property.
///
/// See values/specified/length.rs for more details.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToAnimatedZero, ToCss)]
pub enum MozLength {
LengthOrPercentageOrAuto(LengthOrPercentageOrAuto),
#[animation(error)]
ExtremumLength(ExtremumLength),
}
impl MozLength {
/// Returns the `auto` value.
#[inline]
pub fn auto() -> Self {
MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto)
}
}
impl ToComputedValue for specified::MozLength {
type ComputedValue = MozLength;
#[inline]
fn to_computed_value(&self, context: &Context) -> MozLength {
debug_assert!(
context.for_non_inherited_property.is_some(),
"Someone added a MozLength to an inherited property? Evil!"
);
match *self {
specified::MozLength::LengthOrPercentageOrAuto(ref lopoa) => {
MozLength::LengthOrPercentageOrAuto(lopoa.to_computed_value(context))
},
specified::MozLength::ExtremumLength(ext) => {
context
.rule_cache_conditions
.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
if !ext.valid_for(
context.builder.writing_mode,
context.for_non_inherited_property.unwrap(),
) {
MozLength::auto()
} else {
MozLength::ExtremumLength(ext)
}
},
}
}
#[inline]
fn from_computed_value(computed: &MozLength) -> Self {
match *computed {
MozLength::LengthOrPercentageOrAuto(ref lopoa) => {
specified::MozLength::LengthOrPercentageOrAuto(
specified::LengthOrPercentageOrAuto::from_computed_value(lopoa),
)
},
MozLength::ExtremumLength(ext) => specified::MozLength::ExtremumLength(ext),
}
}
}
/// A value suitable for a `max-width` or `max-height` property.
/// See values/specified/length.rs for more details.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)]
pub enum MaxLength {
LengthOrPercentageOrNone(LengthOrPercentageOrNone),
#[animation(error)]
ExtremumLength(ExtremumLength),
}
impl MaxLength {
/// Returns the `none` value.
#[inline]
pub fn none() -> Self {
MaxLength::LengthOrPercentageOrNone(LengthOrPercentageOrNone::None)
}
}
impl ToComputedValue for specified::MaxLength {
type ComputedValue = MaxLength;
#[inline]
fn to_computed_value(&self, context: &Context) -> MaxLength {
debug_assert!(
context.for_non_inherited_property.is_some(),
"Someone added a MaxLength to an inherited property? Evil!"
);
match *self {
specified::MaxLength::LengthOrPercentageOrNone(ref lopon) => {
MaxLength::LengthOrPercentageOrNone(lopon.to_computed_value(context))
},
specified::MaxLength::ExtremumLength(ext) => {
context
.rule_cache_conditions
.borrow_mut()
.set_writing_mode_dependency(context.builder.writing_mode);
if !ext.valid_for(
context.builder.writing_mode,
context.for_non_inherited_property.unwrap(),
) {
MaxLength::none()
} else {
MaxLength::ExtremumLength(ext)
}
},
}
}
#[inline]
fn from_computed_value(computed: &MaxLength) -> Self {
match *computed {
MaxLength::LengthOrPercentageOrNone(ref lopon) => {
specified::MaxLength::LengthOrPercentageOrNone(
specified::LengthOrPercentageOrNone::from_computed_value(&lopon),
)
},
MaxLength::ExtremumLength(ref ext) => specified::MaxLength::ExtremumLength(ext.clone()),
}
}
}