diff options
22 files changed, 339 insertions, 645 deletions
diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 266ebbd750a..08d0557f896 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -20,7 +20,7 @@ use crate::stylesheets::{Origin, RulesMutateError}; use crate::values::computed::image::LineDirection; use crate::values::computed::transform::Matrix3D; use crate::values::computed::url::ComputedImageUrl; -use crate::values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image}; +use crate::values::computed::{Angle, Gradient, Image}; use crate::values::computed::{Integer, LengthOrPercentage}; use crate::values::computed::{LengthOrPercentageOrAuto, NonNegativeLengthOrPercentageOrAuto}; use crate::values::computed::{Percentage, TextAlign}; @@ -31,9 +31,10 @@ use crate::values::generics::rect::Rect; use crate::values::generics::NonNegative; use app_units::Au; use std::f32::consts::PI; +use style_traits::values::specified::AllowedNumericType; -impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue { - fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { +impl From<LengthOrPercentage> for nsStyleCoord_CalcValue { + fn from(other: LengthOrPercentage) -> nsStyleCoord_CalcValue { let has_percentage = other.percentage.is_some(); nsStyleCoord_CalcValue { mLength: other.unclamped_length().to_i32_au(), @@ -43,32 +44,19 @@ impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue { } } -impl From<nsStyleCoord_CalcValue> for CalcLengthOrPercentage { - fn from(other: nsStyleCoord_CalcValue) -> CalcLengthOrPercentage { +impl From<nsStyleCoord_CalcValue> for LengthOrPercentage { + fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentage { let percentage = if other.mHasPercent { Some(Percentage(other.mPercent)) } else { None }; - Self::new(Au(other.mLength).into(), percentage) - } -} - -impl From<LengthOrPercentage> for nsStyleCoord_CalcValue { - fn from(other: LengthOrPercentage) -> nsStyleCoord_CalcValue { - match other { - LengthOrPercentage::Length(px) => nsStyleCoord_CalcValue { - mLength: px.to_i32_au(), - mPercent: 0.0, - mHasPercent: false, - }, - LengthOrPercentage::Percentage(pc) => nsStyleCoord_CalcValue { - mLength: 0, - mPercent: pc.0, - mHasPercent: true, - }, - LengthOrPercentage::Calc(calc) => calc.into(), - } + Self::with_clamping_mode( + Au(other.mLength).into(), + percentage, + AllowedNumericType::All, + /* was_calc = */ true, + ) } } @@ -76,39 +64,15 @@ impl LengthOrPercentageOrAuto { /// Convert this value in an appropriate `nsStyleCoord::CalcValue`. pub fn to_calc_value(&self) -> Option<nsStyleCoord_CalcValue> { match *self { - LengthOrPercentageOrAuto::Length(px) => Some(nsStyleCoord_CalcValue { - mLength: px.to_i32_au(), - mPercent: 0.0, - mHasPercent: false, - }), - LengthOrPercentageOrAuto::Percentage(pc) => Some(nsStyleCoord_CalcValue { - mLength: 0, - mPercent: pc.0, - mHasPercent: true, - }), - LengthOrPercentageOrAuto::Calc(calc) => Some(calc.into()), + LengthOrPercentageOrAuto::LengthOrPercentage(len) => Some(From::from(len)), LengthOrPercentageOrAuto::Auto => None, } } } -impl From<nsStyleCoord_CalcValue> for LengthOrPercentage { - fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentage { - match (other.mHasPercent, other.mLength) { - (false, _) => LengthOrPercentage::Length(Au(other.mLength).into()), - (true, 0) => LengthOrPercentage::Percentage(Percentage(other.mPercent)), - _ => LengthOrPercentage::Calc(other.into()), - } - } -} - impl From<nsStyleCoord_CalcValue> for LengthOrPercentageOrAuto { fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentageOrAuto { - match (other.mHasPercent, other.mLength) { - (false, _) => LengthOrPercentageOrAuto::Length(Au(other.mLength).into()), - (true, 0) => LengthOrPercentageOrAuto::Percentage(Percentage(other.mPercent)), - _ => LengthOrPercentageOrAuto::Calc(other.into()), - } + LengthOrPercentageOrAuto::LengthOrPercentage(LengthOrPercentage::from(other)) } } @@ -116,9 +80,8 @@ impl From<nsStyleCoord_CalcValue> for LengthOrPercentageOrAuto { // disappear as we move more stuff to cbindgen. impl From<nsStyleCoord_CalcValue> for NonNegativeLengthOrPercentageOrAuto { fn from(other: nsStyleCoord_CalcValue) -> Self { - use style_traits::values::specified::AllowedNumericType; - NonNegative(if other.mLength < 0 || other.mPercent < 0. { - LengthOrPercentageOrAuto::Calc(CalcLengthOrPercentage::with_clamping_mode( + NonNegative( + LengthOrPercentageOrAuto::LengthOrPercentage(LengthOrPercentage::with_clamping_mode( Au(other.mLength).into(), if other.mHasPercent { Some(Percentage(other.mPercent)) @@ -126,10 +89,9 @@ impl From<nsStyleCoord_CalcValue> for NonNegativeLengthOrPercentageOrAuto { None }, AllowedNumericType::NonNegative, + /* was_calc = */ true, )) - } else { - other.into() - }) + ) } } @@ -143,20 +105,13 @@ fn line_direction(horizontal: LengthOrPercentage, vertical: LengthOrPercentage) use crate::values::computed::position::Position; use crate::values::specified::position::{X, Y}; - let horizontal_percentage = match horizontal { - LengthOrPercentage::Percentage(percentage) => Some(percentage.0), - _ => None, - }; - - let vertical_percentage = match vertical { - LengthOrPercentage::Percentage(percentage) => Some(percentage.0), - _ => None, - }; + let horizontal_percentage = horizontal.as_percentage(); + let vertical_percentage = vertical.as_percentage(); let horizontal_as_corner = horizontal_percentage.and_then(|percentage| { - if percentage == 0.0 { + if percentage.0 == 0.0 { Some(X::Left) - } else if percentage == 1.0 { + } else if percentage.0 == 1.0 { Some(X::Right) } else { None @@ -164,9 +119,9 @@ fn line_direction(horizontal: LengthOrPercentage, vertical: LengthOrPercentage) }); let vertical_as_corner = vertical_percentage.and_then(|percentage| { - if percentage == 0.0 { + if percentage.0 == 0.0 { Some(Y::Top) - } else if percentage == 1.0 { + } else if percentage.0 == 1.0 { Some(Y::Bottom) } else { None @@ -178,13 +133,13 @@ fn line_direction(horizontal: LengthOrPercentage, vertical: LengthOrPercentage) } if let Some(hc) = horizontal_as_corner { - if vertical_percentage == Some(0.5) { + if vertical_percentage == Some(Percentage(0.5)) { return LineDirection::Horizontal(hc); } } if let Some(vc) = vertical_as_corner { - if horizontal_percentage == Some(0.5) { + if horizontal_percentage == Some(Percentage(0.5)) { return LineDirection::Vertical(vc); } } diff --git a/components/style/gecko/values.rs b/components/style/gecko/values.rs index 39a5ac2de89..3c11d11e311 100644 --- a/components/style/gecko/values.rs +++ b/components/style/gecko/values.rs @@ -148,19 +148,21 @@ impl GeckoStyleCoordConvertible for NumberOrPercentage { impl GeckoStyleCoordConvertible for LengthOrPercentage { fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) { - let value = match *self { - LengthOrPercentage::Length(px) => CoordDataValue::Coord(px.to_i32_au()), - LengthOrPercentage::Percentage(p) => CoordDataValue::Percent(p.0), - LengthOrPercentage::Calc(calc) => CoordDataValue::Calc(calc.into()), - }; - coord.set_value(value); + if self.was_calc { + return coord.set_value(CoordDataValue::Calc((*self).into())) + } + debug_assert!(self.percentage.is_none() || self.unclamped_length() == Length::zero()); + if let Some(p) = self.percentage { + return coord.set_value(CoordDataValue::Percent(p.0)); + } + coord.set_value(CoordDataValue::Coord(self.unclamped_length().to_i32_au())) } fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> { match coord.as_value() { - CoordDataValue::Coord(coord) => Some(LengthOrPercentage::Length(Au(coord).into())), - CoordDataValue::Percent(p) => Some(LengthOrPercentage::Percentage(Percentage(p))), - CoordDataValue::Calc(calc) => Some(LengthOrPercentage::Calc(calc.into())), + CoordDataValue::Coord(coord) => Some(LengthOrPercentage::new(Au(coord).into(), None)), + CoordDataValue::Percent(p) => Some(LengthOrPercentage::new(Au(0).into(), Some(Percentage(p)))), + CoordDataValue::Calc(calc) => Some(calc.into()), _ => None, } } @@ -181,48 +183,32 @@ impl GeckoStyleCoordConvertible for Length { impl GeckoStyleCoordConvertible for LengthOrPercentageOrAuto { fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) { - let value = match *self { - LengthOrPercentageOrAuto::Length(px) => CoordDataValue::Coord(px.to_i32_au()), - LengthOrPercentageOrAuto::Percentage(p) => CoordDataValue::Percent(p.0), - LengthOrPercentageOrAuto::Auto => CoordDataValue::Auto, - LengthOrPercentageOrAuto::Calc(calc) => CoordDataValue::Calc(calc.into()), - }; - coord.set_value(value); + match *self { + LengthOrPercentageOrAuto::Auto => coord.set_value(CoordDataValue::Auto), + LengthOrPercentageOrAuto::LengthOrPercentage(ref lop) => lop.to_gecko_style_coord(coord), + } } fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> { match coord.as_value() { - CoordDataValue::Coord(coord) => { - Some(LengthOrPercentageOrAuto::Length(Au(coord).into())) - }, - CoordDataValue::Percent(p) => Some(LengthOrPercentageOrAuto::Percentage(Percentage(p))), CoordDataValue::Auto => Some(LengthOrPercentageOrAuto::Auto), - CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrAuto::Calc(calc.into())), - _ => None, + _ => LengthOrPercentage::from_gecko_style_coord(coord).map(LengthOrPercentageOrAuto::LengthOrPercentage), } } } impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone { fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) { - let value = match *self { - LengthOrPercentageOrNone::Length(px) => CoordDataValue::Coord(px.to_i32_au()), - LengthOrPercentageOrNone::Percentage(p) => CoordDataValue::Percent(p.0), - LengthOrPercentageOrNone::None => CoordDataValue::None, - LengthOrPercentageOrNone::Calc(calc) => CoordDataValue::Calc(calc.into()), - }; - coord.set_value(value); + match *self { + LengthOrPercentageOrNone::None => coord.set_value(CoordDataValue::None), + LengthOrPercentageOrNone::LengthOrPercentage(ref lop) => lop.to_gecko_style_coord(coord), + } } fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> { match coord.as_value() { - CoordDataValue::Coord(coord) => { - Some(LengthOrPercentageOrNone::Length(Au(coord).into())) - }, - CoordDataValue::Percent(p) => Some(LengthOrPercentageOrNone::Percentage(Percentage(p))), CoordDataValue::None => Some(LengthOrPercentageOrNone::None), - CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrNone::Calc(calc.into())), - _ => None, + _ => LengthOrPercentage::from_gecko_style_coord(coord).map(LengthOrPercentageOrNone::LengthOrPercentage), } } } diff --git a/components/style/gecko_bindings/sugar/ns_css_value.rs b/components/style/gecko_bindings/sugar/ns_css_value.rs index aa4f9947cd2..f9ed43f7a3d 100644 --- a/components/style/gecko_bindings/sugar/ns_css_value.rs +++ b/components/style/gecko_bindings/sugar/ns_css_value.rs @@ -69,11 +69,14 @@ impl nsCSSValue { /// Sets LengthOrPercentage value to this nsCSSValue. pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) { - match lop { - LengthOrPercentage::Length(px) => self.set_px(px.px()), - LengthOrPercentage::Percentage(pc) => self.set_percentage(pc.0), - LengthOrPercentage::Calc(calc) => bindings::Gecko_CSSValue_SetCalc(self, calc.into()), + if lop.was_calc { + return bindings::Gecko_CSSValue_SetCalc(self, lop.into()) } + debug_assert!(lop.percentage.is_none() || lop.unclamped_length() == Length::zero()); + if let Some(p) = lop.percentage { + return self.set_percentage(p.0); + } + self.set_px(lop.unclamped_length().px()); } /// Sets a px value to this nsCSSValue. @@ -90,13 +93,16 @@ impl nsCSSValue { pub unsafe fn get_lop(&self) -> LengthOrPercentage { match self.mUnit { nsCSSUnit::eCSSUnit_Pixel => { - LengthOrPercentage::Length(Length::new(bindings::Gecko_CSSValue_GetNumber(self))) + LengthOrPercentage::new( + Length::new(bindings::Gecko_CSSValue_GetNumber(self)), + None, + ) }, - nsCSSUnit::eCSSUnit_Percent => LengthOrPercentage::Percentage(Percentage( + nsCSSUnit::eCSSUnit_Percent => LengthOrPercentage::new_percent(Percentage( bindings::Gecko_CSSValue_GetPercentage(self), )), nsCSSUnit::eCSSUnit_Calc => { - LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into()) + bindings::Gecko_CSSValue_GetCalc(self).into() }, _ => panic!("Unexpected unit"), } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index d969f14f54a..036653d218e 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -558,18 +558,16 @@ def set_gecko_property(ffi_name, expr): }, CoordDataValue::Coord(coord) => { SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Length(Au(coord).into()) + LengthOrPercentage::new(Au(coord).into(), None) ) }, CoordDataValue::Percent(p) => { SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Percentage(Percentage(p)) + LengthOrPercentage::new(Au(0).into(), Some(Percentage(p))) ) }, CoordDataValue::Calc(calc) => { - SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Calc(calc.into()) - ) + SvgLengthOrPercentageOrNumber::LengthOrPercentage(calc.into()) }, _ => unreachable!("Unexpected coordinate in ${ident}"), }; @@ -5062,6 +5060,7 @@ clip-path pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T { use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; use crate::values::computed::LengthOrPercentage; + use crate::values::generics::NonNegative; use crate::values::generics::svg::{SVGStrokeDashArray, SvgLengthOrPercentageOrNumber}; if self.gecko.mContextFlags & CONTEXT_VALUE != 0 { @@ -5075,13 +5074,13 @@ clip-path vec.push(SvgLengthOrPercentageOrNumber::Number(number.into())), CoordDataValue::Coord(coord) => vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Length(Au(coord).into()).into())), + NonNegative(LengthOrPercentage::new(Au(coord).into(), None).into()))), CoordDataValue::Percent(p) => vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Percentage(Percentage(p)).into())), + NonNegative(LengthOrPercentage::new_percent(Percentage(p)).into()))), CoordDataValue::Calc(calc) => vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Calc(calc.into()).into())), + NonNegative(LengthOrPercentage::from(calc).clamp_to_non_negative()))), _ => unreachable!(), } } diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 5a7b88e3823..3eded4ea7ac 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -54,7 +54,7 @@ ${helpers.single_keyword( ${helpers.predefined_type( "text-indent", "LengthOrPercentage", - "computed::LengthOrPercentage::Length(computed::Length::new(0.))", + "computed::LengthOrPercentage::zero()", animation_value_type="ComputedValue", spec="https://drafts.csswg.org/css-text/#propdef-text-indent", allow_quirks=True, diff --git a/components/style/properties/longhands/margin.mako.rs b/components/style/properties/longhands/margin.mako.rs index 822fbf798a7..7762be1a23a 100644 --- a/components/style/properties/longhands/margin.mako.rs +++ b/components/style/properties/longhands/margin.mako.rs @@ -15,7 +15,7 @@ ${helpers.predefined_type( "margin-%s" % side[0], "LengthOrPercentageOrAuto", - "computed::LengthOrPercentageOrAuto::Length(computed::Length::new(0.))", + "computed::LengthOrPercentageOrAuto::zero()", alias=maybe_moz_logical_alias(product, side, "-moz-margin-%s"), allow_quirks=not side[1], animation_value_type="ComputedValue", diff --git a/components/style/stylesheets/viewport_rule.rs b/components/style/stylesheets/viewport_rule.rs index 4df576a15ab..209243228df 100644 --- a/components/style/stylesheets/viewport_rule.rs +++ b/components/style/stylesheets/viewport_rule.rs @@ -18,7 +18,7 @@ use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard use crate::str::CssStringWriter; use crate::stylesheets::{Origin, StylesheetInDocument}; use crate::values::computed::{Context, ToComputedValue}; -use crate::values::specified::{LengthOrPercentageOrAuto, NoCalcLength, ViewportPercentageLength}; +use crate::values::specified::{self, LengthOrPercentageOrAuto, NoCalcLength, ViewportPercentageLength}; use app_units::Au; use cssparser::CowRcStr; use cssparser::{parse_important, AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; @@ -157,7 +157,9 @@ impl FromMeta for ViewportLength { fn from_meta(value: &str) -> Option<ViewportLength> { macro_rules! specified { ($value:expr) => { - ViewportLength::Specified(LengthOrPercentageOrAuto::Length($value)) + ViewportLength::Specified(LengthOrPercentageOrAuto::LengthOrPercentage( + specified::LengthOrPercentage::Length($value) + )) }; } @@ -752,16 +754,10 @@ impl MaybeNew for ViewportConstraints { if let Some($value) = $value { match *$value { ViewportLength::Specified(ref length) => match *length { - LengthOrPercentageOrAuto::Length(ref value) => { - Some(Au::from(value.to_computed_value(&context))) - }, - LengthOrPercentageOrAuto::Percentage(value) => { - Some(initial_viewport.$dimension.scale_by(value.0)) - }, LengthOrPercentageOrAuto::Auto => None, - LengthOrPercentageOrAuto::Calc(ref calc) => calc + LengthOrPercentageOrAuto::LengthOrPercentage(ref lop) => Some(lop .to_computed_value(&context) - .to_used_value(Some(initial_viewport.$dimension)), + .to_used_value(initial_viewport.$dimension)), }, ViewportLength::ExtendToZoom => { // $extend_to will be 'None' if 'extend-to-zoom' is 'auto' diff --git a/components/style/values/animated/length.rs b/components/style/values/animated/length.rs index e303d2fcfce..b0d717b0e49 100644 --- a/components/style/values/animated/length.rs +++ b/components/style/values/animated/length.rs @@ -4,15 +4,14 @@ //! Animation implementation for various length-related types. -use super::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; -use crate::values::computed::length::{CalcLengthOrPercentage, Length}; -use crate::values::computed::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; +use super::{Animate, Procedure, ToAnimatedValue}; +use crate::values::computed::length::LengthOrPercentage; use crate::values::computed::MaxLength as ComputedMaxLength; use crate::values::computed::MozLength as ComputedMozLength; use crate::values::computed::Percentage; /// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc> -impl Animate for CalcLengthOrPercentage { +impl Animate for LengthOrPercentage { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| { @@ -28,42 +27,17 @@ impl Animate for CalcLengthOrPercentage { .unclamped_length() .animate(&other.unclamped_length(), procedure)?; let percentage = animate_percentage_half(self.percentage, other.percentage)?; - Ok(CalcLengthOrPercentage::with_clamping_mode( + let is_calc = self.was_calc || other.was_calc || self.percentage.is_some() != other.percentage.is_some(); + Ok(Self::with_clamping_mode( length, percentage, self.clamping_mode, + is_calc, )) } } -impl ToAnimatedZero for LengthOrPercentageOrAuto { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - match *self { - LengthOrPercentageOrAuto::Length(_) | - LengthOrPercentageOrAuto::Percentage(_) | - LengthOrPercentageOrAuto::Calc(_) => { - Ok(LengthOrPercentageOrAuto::Length(Length::new(0.))) - }, - LengthOrPercentageOrAuto::Auto => Err(()), - } - } -} - -impl ToAnimatedZero for LengthOrPercentageOrNone { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - match *self { - LengthOrPercentageOrNone::Length(_) | - LengthOrPercentageOrNone::Percentage(_) | - LengthOrPercentageOrNone::Calc(_) => { - Ok(LengthOrPercentageOrNone::Length(Length::new(0.))) - }, - LengthOrPercentageOrNone::None => Err(()), - } - } -} - +// FIXME(emilio): These should use NonNegative<> instead. impl ToAnimatedValue for ComputedMaxLength { type AnimatedValue = Self; @@ -74,18 +48,15 @@ impl ToAnimatedValue for ComputedMaxLength { #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { - use crate::values::computed::{Length, LengthOrPercentageOrNone, Percentage}; + use crate::values::computed::LengthOrPercentageOrNone; use crate::values::generics::length::MaxLength as GenericMaxLength; match animated { GenericMaxLength::LengthOrPercentageOrNone(lopn) => { let result = match lopn { - LengthOrPercentageOrNone::Length(px) => { - LengthOrPercentageOrNone::Length(Length::new(px.px().max(0.))) + LengthOrPercentageOrNone::LengthOrPercentage(len) => { + LengthOrPercentageOrNone::LengthOrPercentage(len.clamp_to_non_negative()) }, - LengthOrPercentageOrNone::Percentage(percentage) => { - LengthOrPercentageOrNone::Percentage(Percentage(percentage.0.max(0.))) - }, - _ => lopn, + LengthOrPercentageOrNone::None => lopn, }; GenericMaxLength::LengthOrPercentageOrNone(result) }, @@ -104,20 +75,10 @@ impl ToAnimatedValue for ComputedMozLength { #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { - use crate::values::computed::{Length, LengthOrPercentageOrAuto, Percentage}; use crate::values::generics::length::MozLength as GenericMozLength; match animated { GenericMozLength::LengthOrPercentageOrAuto(lopa) => { - let result = match lopa { - LengthOrPercentageOrAuto::Length(px) => { - LengthOrPercentageOrAuto::Length(Length::new(px.px().max(0.))) - }, - LengthOrPercentageOrAuto::Percentage(percentage) => { - LengthOrPercentageOrAuto::Percentage(Percentage(percentage.0.max(0.))) - }, - _ => lopa, - }; - GenericMozLength::LengthOrPercentageOrAuto(result) + GenericMozLength::LengthOrPercentageOrAuto(lopa.clamp_to_non_negative()) }, _ => animated, } diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index a7c947f810a..1655f2eaa26 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -9,7 +9,7 @@ //! module's raison d'être is to ultimately contain all these types. use crate::properties::PropertyId; -use crate::values::computed::length::CalcLengthOrPercentage; +use crate::values::computed::length::LengthOrPercentage; use crate::values::computed::url::ComputedUrl; use crate::values::computed::Angle as ComputedAngle; use crate::values::computed::Image; @@ -335,7 +335,7 @@ macro_rules! trivial_to_animated_value { } trivial_to_animated_value!(Au); -trivial_to_animated_value!(CalcLengthOrPercentage); +trivial_to_animated_value!(LengthOrPercentage); trivial_to_animated_value!(ComputedAngle); trivial_to_animated_value!(ComputedUrl); trivial_to_animated_value!(bool); diff --git a/components/style/values/animated/svg.rs b/components/style/values/animated/svg.rs index 1d8be90fb6e..7123542aef8 100644 --- a/components/style/values/animated/svg.rs +++ b/components/style/values/animated/svg.rs @@ -32,10 +32,16 @@ fn to_number_or_percentage( value: &SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number>, ) -> Result<NumberOrPercentage, ()> { Ok(match *value { - SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref l) => match *l { - LengthOrPercentage::Length(ref l) => NumberOrPercentage::Number(l.px()), - LengthOrPercentage::Percentage(ref p) => NumberOrPercentage::Percentage(*p), - LengthOrPercentage::Calc(..) => return Err(()), + SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref l) => { + match l.percentage { + Some(p) => { + if l.unclamped_length().px() != 0. { + return Err(()); + } + NumberOrPercentage::Percentage(p) + } + None => NumberOrPercentage::Number(l.length().px()) + } }, SvgLengthOrPercentageOrNumber::Number(ref n) => NumberOrPercentage::Number(*n), }) @@ -55,7 +61,7 @@ impl Animate for SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> { NumberOrPercentage::Percentage(ref this), NumberOrPercentage::Percentage(ref other), ) => Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage( - LengthOrPercentage::Percentage(this.animate(other, procedure)?), + LengthOrPercentage::new_percent(this.animate(other, procedure)?), )), _ => Err(()), } diff --git a/components/style/values/animated/transform.rs b/components/style/values/animated/transform.rs index 5223a195075..2804f4c0890 100644 --- a/components/style/values/animated/transform.rs +++ b/components/style/values/animated/transform.rs @@ -1167,17 +1167,6 @@ impl Animate for ComputedTransformOperation { // See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0. impl ComputeSquaredDistance for ComputedTransformOperation { fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - // For translate, We don't want to require doing layout in order to calculate the result, so - // drop the percentage part. However, dropping percentage makes us impossible to - // compute the distance for the percentage-percentage case, but Gecko uses the - // same formula, so it's fine for now. - // Note: We use pixel value to compute the distance for translate, so we have to - // convert Au into px. - let extract_pixel_length = |lop: &LengthOrPercentage| match *lop { - LengthOrPercentage::Length(px) => px.px(), - LengthOrPercentage::Percentage(_) => 0., - LengthOrPercentage::Calc(calc) => calc.length().px(), - }; match (self, other) { (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => { this.compute_squared_distance(other) @@ -1199,10 +1188,16 @@ impl ComputeSquaredDistance for ComputedTransformOperation { &TransformOperation::Translate3D(ref fx, ref fy, ref fz), &TransformOperation::Translate3D(ref tx, ref ty, ref tz), ) => { - let fx = extract_pixel_length(&fx); - let fy = extract_pixel_length(&fy); - let tx = extract_pixel_length(&tx); - let ty = extract_pixel_length(&ty); + // For translate, We don't want to require doing layout in order + // to calculate the result, so drop the percentage part. + // + // However, dropping percentage makes us impossible to compute + // the distance for the percentage-percentage case, but Gecko + // uses the same formula, so it's fine for now. + let fx = fx.length_component().px(); + let fy = fy.length_component().px(); + let tx = tx.length_component().px(); + let ty = ty.length_component().px(); Ok(fx.compute_squared_distance(&tx)? + fy.compute_squared_distance(&ty)? + diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index ba18e4b30c8..1c573ac3a2b 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -9,8 +9,6 @@ use crate::values::computed::position::Position; use crate::values::computed::url::ComputedImageUrl; -#[cfg(feature = "gecko")] -use crate::values::computed::Percentage; use crate::values::computed::{Angle, Color, Context}; use crate::values::computed::{Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue}; use crate::values::generics::image::{self as generic, CompatMode}; @@ -73,15 +71,10 @@ impl generic::LineDirection for LineDirection { LineDirection::Vertical(Y::Top) if compat_mode != CompatMode::Modern => true, LineDirection::Corner(..) => false, #[cfg(feature = "gecko")] - LineDirection::MozPosition( - Some(Position { - horizontal: LengthOrPercentage::Percentage(Percentage(x)), - vertical: LengthOrPercentage::Percentage(Percentage(y)), - }), - None, - ) => { + LineDirection::MozPosition(Some(Position { ref vertical, ref horizontal }), None) => { // `50% 0%` is the default value for line direction. - x == 0.5 && y == 0.0 + horizontal.as_percentage().map_or(false, |p| p.0 == 0.5) && + vertical.as_percentage().map_or(false, |p| p.0 == 0.0) }, _ => false, } diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 240b5ceb1ee..8a9425ee631 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -5,7 +5,7 @@ //! `<length>` computed values, and related ones. use super::{Context, Number, Percentage, ToComputedValue}; -use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; +use crate::values::animated::{ToAnimatedValue}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::length::MaxLength as GenericMaxLength; use crate::values::generics::length::MozLength as GenericMozLength; @@ -68,15 +68,38 @@ impl ToComputedValue for specified::Length { } #[allow(missing_docs)] -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedZero)] -pub struct CalcLengthOrPercentage { +#[derive(Clone, Copy, Debug, MallocSizeOf, ToAnimatedZero)] +pub struct LengthOrPercentage { #[animation(constant)] pub clamping_mode: AllowedNumericType, length: Length, pub percentage: Option<Percentage>, + /// Whether this was from a calc() expression. This is needed because right + /// now we don't treat calc() the same way as non-calc everywhere, but + /// that's a bug in most cases. + /// + /// Please don't add new uses of this that aren't for converting to Gecko's + /// representation, or to interpolate values. + /// + /// See https://github.com/w3c/csswg-drafts/issues/3482. + #[animation(constant)] + pub was_calc: bool, } -impl ComputeSquaredDistance for CalcLengthOrPercentage { +// FIXME(emilio): This is a bit of a hack that can disappear as soon as we share +// representation of LengthOrPercentage with Gecko. The issue here is that Gecko +// uses CalcValue to represent position components, so they always come back as +// was_calc == true, and we mess up in the transitions code. +// +// This was a pre-existing bug, though arguably so only in pretty obscure cases +// like calc(0px + 5%) and such. +impl PartialEq for LengthOrPercentage { + fn eq(&self, other: &Self) -> bool { + self.length == other.length && self.percentage == other.percentage + } +} + +impl ComputeSquaredDistance for LengthOrPercentage { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { // FIXME(nox): This looks incorrect to me, to add a distance between lengths @@ -89,24 +112,36 @@ impl ComputeSquaredDistance for CalcLengthOrPercentage { } } -impl CalcLengthOrPercentage { - /// Returns a new `CalcLengthOrPercentage`. +impl LengthOrPercentage { + /// Returns a new `LengthOrPercentage`. #[inline] pub fn new(length: Length, percentage: Option<Percentage>) -> Self { - Self::with_clamping_mode(length, percentage, AllowedNumericType::All) + Self::with_clamping_mode( + length, + percentage, + AllowedNumericType::All, + /* was_calc = */ false, + ) + } + + /// Returns a new `LengthOrPercentage` with zero length and some percentage. + pub fn new_percent(percentage: Percentage) -> Self { + Self::new(Length::zero(), Some(percentage)) } - /// Returns a new `CalcLengthOrPercentage` with a specific clamping mode. + /// Returns a new `LengthOrPercentage` with a specific clamping mode. #[inline] pub fn with_clamping_mode( length: Length, percentage: Option<Percentage>, clamping_mode: AllowedNumericType, + was_calc: bool, ) -> Self { Self { clamping_mode, length, percentage, + was_calc, } } @@ -131,22 +166,38 @@ impl CalcLengthOrPercentage { self.length } + /// Return the percentage value as CSSFloat. #[inline] pub fn percentage(&self) -> CSSFloat { self.percentage.map_or(0., |p| p.0) } + /// Returns the percentage component if this could be represented as a + /// non-calc percentage. + pub fn as_percentage(&self) -> Option<Percentage> { + if self.length.px() != 0. { + return None; + } + + let p = self.percentage?; + if self.clamping_mode.clamp(p.0) != p.0 { + return None; + } + + Some(p) + } + /// 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) + pub fn maybe_to_used_value(&self, container_len: Option<Au>) -> Option<Au> { + self.maybe_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> { + pub fn maybe_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(); @@ -158,81 +209,12 @@ impl CalcLengthOrPercentage { } } -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 { +impl ToCss for LengthOrPercentage { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write, { - use num_traits::Zero; - - let length = self.unclamped_length(); - match self.percentage { - Some(p) => { - if length.px() == 0. && self.clamping_mode.clamp(p.0) == p.0 { - return p.to_css(dest); - } - }, - None => { - if self.clamping_mode.clamp(length.px()) == length.px() { - return length.to_css(dest); - } - }, - } - - dest.write_str("calc(")?; - if let Some(percentage) = self.percentage { - percentage.to_css(dest)?; - if length.px() != 0. { - dest.write_str(if length.px() < Zero::zero() { - " - " - } else { - " + " - })?; - length.abs().to_css(dest)?; - } - } else { - length.to_css(dest)?; - } - - dest.write_str(")") + specified::LengthOrPercentage::from_computed_value(self).to_css(dest) } } @@ -243,7 +225,7 @@ impl specified::CalcLengthOrPercentage { context: &Context, zoom_fn: F, base_size: FontBaseSize, - ) -> CalcLengthOrPercentage + ) -> LengthOrPercentage where F: Fn(Length) -> Length, { @@ -277,10 +259,11 @@ impl specified::CalcLengthOrPercentage { } } - CalcLengthOrPercentage { + LengthOrPercentage { clamping_mode: self.clamping_mode, length: Length::new(length.min(f32::MAX).max(f32::MIN)), percentage: self.percentage, + was_calc: true, } } @@ -289,7 +272,7 @@ impl specified::CalcLengthOrPercentage { &self, context: &Context, base_size: FontBaseSize, - ) -> CalcLengthOrPercentage { + ) -> LengthOrPercentage { self.to_computed_value_with_zoom( context, |abs| context.maybe_zoom_text(abs.into()).0, @@ -324,15 +307,15 @@ impl specified::CalcLengthOrPercentage { } impl ToComputedValue for specified::CalcLengthOrPercentage { - type ComputedValue = CalcLengthOrPercentage; + type ComputedValue = LengthOrPercentage; - fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage { + fn to_computed_value(&self, context: &Context) -> LengthOrPercentage { // 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 { + fn from_computed_value(computed: &LengthOrPercentage) -> Self { specified::CalcLengthOrPercentage { clamping_mode: computed.clamping_mode, absolute: Some(AbsoluteLength::from_computed_value(&computed.length)), @@ -342,95 +325,33 @@ impl ToComputedValue for specified::CalcLengthOrPercentage { } } -#[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.)) + LengthOrPercentage::new(Length::new(0.), None) } - #[inline] /// 1px length value for SVG defaults + #[inline] pub fn one() -> LengthOrPercentage { - LengthOrPercentage::Length(Length::new(1.)) + LengthOrPercentage::new(Length::new(1.), None) } /// 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, - } + self.unclamped_length().px() == 0.0 && self.percentage.map_or(true, |p| p.0 == 0.0) } // 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(), - ), - } + ( + Au::from(self.unclamped_length()), + NotNan::new(self.percentage()).unwrap(), + ) } /// Returns the used value. @@ -440,27 +361,40 @@ impl LengthOrPercentage { /// 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() - }, - } + self.maybe_to_pixel_length(Some(containing_length)).unwrap() } /// Returns the clamped non-negative values. + /// + /// TODO(emilio): It's a bit unfortunate that this depends on whether the + /// value was a `calc()` value or not. Should it? #[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, - } + if self.was_calc { + return Self::with_clamping_mode( + self.length, + self.percentage, + AllowedNumericType::NonNegative, + self.was_calc, + ) + } + + debug_assert!(self.percentage.is_none() || self.unclamped_length() == Length::zero()); + if let Some(p) = self.percentage { + return Self::with_clamping_mode( + Length::zero(), + Some(p.clamp_to_non_negative()), + AllowedNumericType::NonNegative, + self.was_calc, + ); + } + + Self::with_clamping_mode( + self.length.clamp_to_non_negative(), + None, + AllowedNumericType::NonNegative, + self.was_calc, + ) } } @@ -470,71 +404,57 @@ impl ToComputedValue for specified::LengthOrPercentage { fn to_computed_value(&self, context: &Context) -> LengthOrPercentage { match *self { specified::LengthOrPercentage::Length(ref value) => { - LengthOrPercentage::Length(value.to_computed_value(context)) + LengthOrPercentage::new(value.to_computed_value(context), None) }, specified::LengthOrPercentage::Percentage(value) => { - LengthOrPercentage::Percentage(value) + LengthOrPercentage::new_percent(value) }, specified::LengthOrPercentage::Calc(ref calc) => { - LengthOrPercentage::Calc((**calc).to_computed_value(context)) + (**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), - )), + let length = computed.unclamped_length(); + if let Some(p) = computed.as_percentage() { + return specified::LengthOrPercentage::Percentage(p) + } + + let percentage = computed.percentage; + if percentage.is_none() && + computed.clamping_mode.clamp(length.px()) == length.px() { + return specified::LengthOrPercentage::Length( + ToComputedValue::from_computed_value(&length) + ) } + + specified::LengthOrPercentage::Calc(Box::new( + ToComputedValue::from_computed_value(computed), + )) } } impl IsZeroLength for LengthOrPercentage { #[inline] fn is_zero_length(&self) -> bool { - match *self { - LengthOrPercentage::Length(l) => l.0 == 0.0, - LengthOrPercentage::Percentage(p) => p.0 == 0.0, - LengthOrPercentage::Calc(c) => c.unclamped_length().0 == 0.0 && c.percentage() == 0.0, - } + self.is_definitely_zero() } } #[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")] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq, ToAnimatedZero, ToCss)] pub enum LengthOrPercentageOrAuto { - Length(Length), - Percentage(Percentage), + LengthOrPercentage(LengthOrPercentage), 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(())?, - )) - } - + /// Returns the `0` value. #[inline] - fn compute_squared_distance_fallback(&self, other: &Self) -> Result<SquaredDistance, ()> { - <Option<CalcLengthOrPercentage>>::compute_squared_distance( - &(*self).into(), - &(*other).into(), - ) + pub fn zero() -> Self { + LengthOrPercentageOrAuto::LengthOrPercentage(LengthOrPercentage::zero()) } } @@ -572,24 +492,21 @@ impl ToAnimatedValue for NonNegativeLengthOrPercentageOrAuto { 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, + LengthOrPercentage(ref l) => l.is_definitely_zero(), + Auto => false, } } - fn clamp_to_non_negative(self) -> Self { + /// Clamps the value to a non-negative value. + pub 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, + LengthOrPercentage(l) => LengthOrPercentage(l.clamp_to_non_negative()), + Auto => Auto, } } } @@ -600,16 +517,12 @@ impl ToComputedValue for specified::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::LengthOrPercentage(ref value) => { + LengthOrPercentageOrAuto::LengthOrPercentage( + value.to_computed_value(context), + ) }, specified::LengthOrPercentageOrAuto::Auto => LengthOrPercentageOrAuto::Auto, - specified::LengthOrPercentageOrAuto::Calc(ref calc) => { - LengthOrPercentageOrAuto::Calc((**calc).to_computed_value(context)) - }, } } @@ -617,78 +530,44 @@ impl ToComputedValue for specified::LengthOrPercentageOrAuto { 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::LengthOrPercentage(ref value) => { + specified::LengthOrPercentageOrAuto::LengthOrPercentage( + ToComputedValue::from_computed_value(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")] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, MallocSizeOf, PartialEq, ToAnimatedZero, ToCss)] pub enum LengthOrPercentageOrNone { - Length(Length), - Percentage(Percentage), - Calc(CalcLengthOrPercentage), + LengthOrPercentage(LengthOrPercentage), 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::LengthOrPercentage(ref lop) => { + Some(lop.to_used_value(containing_length)) }, - LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)), } } } +// FIXME(emilio): Derive this. 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::LengthOrPercentage(ref value) => { + LengthOrPercentageOrNone::LengthOrPercentage(value.to_computed_value(context)) }, specified::LengthOrPercentageOrNone::None => LengthOrPercentageOrNone::None, } @@ -698,15 +577,11 @@ impl ToComputedValue for specified::LengthOrPercentageOrNone { 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::LengthOrPercentage(value) => { + specified::LengthOrPercentageOrNone::LengthOrPercentage( + ToComputedValue::from_computed_value(&value), + ) }, - LengthOrPercentageOrNone::Calc(calc) => specified::LengthOrPercentageOrNone::Calc( - Box::new(ToComputedValue::from_computed_value(&calc)), - ), } } } @@ -719,19 +594,19 @@ impl ToAnimatedValue for NonNegativeLengthOrPercentage { #[inline] fn to_animated_value(self) -> Self::AnimatedValue { - self.into() + self.0 } #[inline] fn from_animated_value(animated: Self::AnimatedValue) -> Self { - animated.clamp_to_non_negative().into() + NonNegative(animated.clamp_to_non_negative()) } } impl From<NonNegativeLength> for NonNegativeLengthOrPercentage { #[inline] fn from(length: NonNegativeLength) -> Self { - LengthOrPercentage::Length(length.0).into() + LengthOrPercentage::new(length.0, None).into() } } @@ -749,6 +624,15 @@ impl From<NonNegativeLengthOrPercentage> for LengthOrPercentage { } } +// TODO(emilio): This is a really generic impl which is only needed to implement +// Animated and co for Spacing<>. Get rid of this, probably? +impl From<Au> for LengthOrPercentage { + #[inline] + fn from(length: Au) -> Self { + LengthOrPercentage::new(length.into(), None) + } +} + impl NonNegativeLengthOrPercentage { /// Get zero value. #[inline] @@ -798,11 +682,6 @@ impl CSSPixelLength { 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 { @@ -810,11 +689,19 @@ impl CSSPixelLength { } /// Return the absolute value of this length. + #[inline] pub fn abs(self) -> Self { CSSPixelLength::new(self.0.abs()) } + /// Return the clamped value of this length. + #[inline] + pub fn clamp_to_non_negative(self) -> Self { + CSSPixelLength::new(self.0.max(0.)) + } + /// Zero value + #[inline] pub fn zero() -> Self { CSSPixelLength::new(0.) } diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 4051511e28e..7877fddc192 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -62,7 +62,7 @@ pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, pub use self::gecko::ScrollSnapPoint; pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect}; pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength}; -pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage}; +pub use self::length::{Length, LengthOrNumber, LengthOrPercentage}; pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength}; pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto}; #[cfg(feature = "gecko")] diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs index 8c7976886d8..7bcdb59f89e 100644 --- a/components/style/values/computed/position.rs +++ b/components/style/values/computed/position.rs @@ -28,8 +28,8 @@ impl Position { #[inline] pub fn center() -> Self { Self::new( - LengthOrPercentage::Percentage(Percentage(0.5)), - LengthOrPercentage::Percentage(Percentage(0.5)), + LengthOrPercentage::new_percent(Percentage(0.5)), + LengthOrPercentage::new_percent(Percentage(0.5)), ) } diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs index e3493131544..14115295213 100644 --- a/components/style/values/computed/transform.rs +++ b/components/style/values/computed/transform.rs @@ -31,8 +31,8 @@ impl TransformOrigin { #[inline] pub fn initial_value() -> Self { Self::new( - LengthOrPercentage::Percentage(Percentage(0.5)), - LengthOrPercentage::Percentage(Percentage(0.5)), + LengthOrPercentage::new_percent(Percentage(0.5)), + LengthOrPercentage::new_percent(Percentage(0.5)), Length::new(0.), ) } diff --git a/components/style/values/generics/text.rs b/components/style/values/generics/text.rs index 090c3c1f687..2ee92513cf9 100644 --- a/components/style/values/generics/text.rs +++ b/components/style/values/generics/text.rs @@ -103,10 +103,7 @@ where } } -impl<V> ToAnimatedZero for Spacing<V> -where - V: From<Au>, -{ +impl<V> ToAnimatedZero for Spacing<V> { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index 158b7456c76..d4b69b02e72 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -288,18 +288,14 @@ impl ToAbsoluteLength for ComputedLength { impl ToAbsoluteLength for ComputedLengthOrPercentage { #[inline] fn to_pixel_length(&self, containing_len: Option<Au>) -> Result<CSSFloat, ()> { - let extract_pixel_length = |lop: &ComputedLengthOrPercentage| match *lop { - ComputedLengthOrPercentage::Length(px) => px.px(), - ComputedLengthOrPercentage::Percentage(_) => 0., - ComputedLengthOrPercentage::Calc(calc) => calc.length().px(), - }; - match containing_len { Some(relative_len) => Ok(self.to_pixel_length(relative_len).px()), // If we don't have reference box, we cannot resolve the used value, // so only retrieve the length part. This will be used for computing // distance without any layout info. - None => Ok(extract_pixel_length(self)), + // + // FIXME(emilio): This looks wrong. + None => Ok(self.length_component().px()), } } } diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index e5699742b14..57d7835e416 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -916,9 +916,7 @@ impl FontSize { info = parent.keyword_info.map(|i| i.compose(ratio, abs.into())); } let calc = calc.to_computed_value_zoomed(context, base_size); - calc.to_used_value(Some(base_size.resolve(context))) - .unwrap() - .into() + calc.to_used_value(base_size.resolve(context)).into() }, FontSize::Keyword(i) => { // As a specified keyword, this is keyword derived diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index e64385472a0..4be179212c8 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -789,6 +789,12 @@ impl LengthOrPercentage { LengthOrPercentage::Length(NoCalcLength::zero()) } + #[inline] + /// Returns a `0%` value. + pub fn zero_percent() -> LengthOrPercentage { + LengthOrPercentage::Percentage(computed::Percentage::zero()) + } + fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, @@ -898,24 +904,8 @@ impl IsZeroLength for LengthOrPercentage { #[allow(missing_docs)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] pub enum LengthOrPercentageOrAuto { - Length(NoCalcLength), - Percentage(computed::Percentage), + LengthOrPercentage(LengthOrPercentage), Auto, - Calc(Box<CalcLengthOrPercentage>), -} - -impl From<NoCalcLength> for LengthOrPercentageOrAuto { - #[inline] - fn from(len: NoCalcLength) -> Self { - LengthOrPercentageOrAuto::Length(len) - } -} - -impl From<computed::Percentage> for LengthOrPercentageOrAuto { - #[inline] - fn from(pc: computed::Percentage) -> Self { - LengthOrPercentageOrAuto::Percentage(pc) - } } impl LengthOrPercentageOrAuto { @@ -925,48 +915,16 @@ impl LengthOrPercentageOrAuto { num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>> { - // FIXME: remove early returns when lifetimes are non-lexical - { - 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(LengthOrPercentageOrAuto::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(LengthOrPercentageOrAuto::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_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - return Ok(LengthOrPercentageOrAuto::Length(NoCalcLength::Absolute( - AbsoluteLength::Px(value), - ))); - }, - Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => { - return Ok(LengthOrPercentageOrAuto::Auto); - }, - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - _ => return Err(location.new_unexpected_token_error(token.clone())), - } + if input.try(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(LengthOrPercentageOrAuto::Auto); } - let calc = input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i, num_context) - })?; - Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc))) + Ok(LengthOrPercentageOrAuto::LengthOrPercentage(LengthOrPercentage::parse_internal( + context, + input, + num_context, + allow_quirks, + )?)) } /// Parse a non-negative length, percentage, or auto. @@ -1000,13 +958,13 @@ impl LengthOrPercentageOrAuto { /// Returns a value representing a `0` length. pub fn zero() -> Self { - LengthOrPercentageOrAuto::Length(NoCalcLength::zero()) + LengthOrPercentageOrAuto::LengthOrPercentage(LengthOrPercentage::zero()) } /// Returns a value representing `0%`. #[inline] pub fn zero_percent() -> Self { - LengthOrPercentageOrAuto::Percentage(computed::Percentage::zero()) + LengthOrPercentageOrAuto::LengthOrPercentage(LengthOrPercentage::zero_percent()) } /// Parses, with quirks. @@ -1076,9 +1034,7 @@ impl Parse for NonNegativeLengthOrPercentageOrAuto { #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] #[allow(missing_docs)] pub enum LengthOrPercentageOrNone { - Length(NoCalcLength), - Percentage(computed::Percentage), - Calc(Box<CalcLengthOrPercentage>), + LengthOrPercentage(LengthOrPercentage), None, } @@ -1089,48 +1045,16 @@ impl LengthOrPercentageOrNone { num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>> { - // FIXME: remove early returns when lifetimes are non-lexical - { - 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(LengthOrPercentageOrNone::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(LengthOrPercentageOrNone::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_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - return Ok(LengthOrPercentageOrNone::Length(NoCalcLength::Absolute( - AbsoluteLength::Px(value), - ))); - }, - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => { - return Ok(LengthOrPercentageOrNone::None); - }, - _ => return Err(location.new_unexpected_token_error(token.clone())), - } + if input.try(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(LengthOrPercentageOrNone::None); } - let calc = input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i, num_context) - })?; - Ok(LengthOrPercentageOrNone::Calc(Box::new(calc))) + Ok(LengthOrPercentageOrNone::LengthOrPercentage(LengthOrPercentage::parse_internal( + context, + input, + num_context, + allow_quirks, + )?)) } /// Parse a non-negative LengthOrPercentageOrNone. diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index e2865071eeb..a7c6c9f41d6 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -10,7 +10,6 @@ use crate::hash::FxHashMap; use crate::parser::{Parse, ParserContext}; use crate::str::HTML_SPACE_CHARACTERS; -use crate::values::computed::CalcLengthOrPercentage; use crate::values::computed::LengthOrPercentage as ComputedLengthOrPercentage; use crate::values::computed::{Context, Percentage, ToComputedValue}; use crate::values::generics::position::Position as GenericPosition; @@ -255,25 +254,21 @@ impl<S: Side> ToComputedValue for PositionComponent<S> { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - PositionComponent::Center => ComputedLengthOrPercentage::Percentage(Percentage(0.5)), + PositionComponent::Center => ComputedLengthOrPercentage::new_percent(Percentage(0.5)), PositionComponent::Side(ref keyword, None) => { let p = Percentage(if keyword.is_start() { 0. } else { 1. }); - ComputedLengthOrPercentage::Percentage(p) + ComputedLengthOrPercentage::new_percent(p) }, PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => { - match length.to_computed_value(context) { - ComputedLengthOrPercentage::Length(length) => ComputedLengthOrPercentage::Calc( - CalcLengthOrPercentage::new(-length, Some(Percentage::hundred())), - ), - ComputedLengthOrPercentage::Percentage(p) => { - ComputedLengthOrPercentage::Percentage(Percentage(1.0 - p.0)) - }, - ComputedLengthOrPercentage::Calc(calc) => { - let p = Percentage(1. - calc.percentage.map_or(0., |p| p.0)); - let l = -calc.unclamped_length(); - ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(l, Some(p))) - }, - } + let length = length.to_computed_value(context); + let p = Percentage(1. - length.percentage()); + let l = -length.unclamped_length(); + ComputedLengthOrPercentage::with_clamping_mode( + l, + Some(p), + length.clamping_mode, + length.was_calc, + ) }, PositionComponent::Side(_, Some(ref length)) | PositionComponent::Length(ref length) => length.to_computed_value(context), diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index a5b36a24db5..bc8737ca95b 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -323,12 +323,12 @@ where fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { OriginComponent::Center => { - ComputedLengthOrPercentage::Percentage(ComputedPercentage(0.5)) + ComputedLengthOrPercentage::new_percent(ComputedPercentage(0.5)) }, OriginComponent::Length(ref length) => length.to_computed_value(context), OriginComponent::Side(ref keyword) => { let p = ComputedPercentage(if keyword.is_start() { 0. } else { 1. }); - ComputedLengthOrPercentage::Percentage(p) + ComputedLengthOrPercentage::new_percent(p) }, } } |