diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2019-01-06 23:01:00 +0100 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2019-01-07 17:05:40 +0100 |
commit | ca503b4908cb45c20cc6777f9d01253057a86a97 (patch) | |
tree | 4194392292171d0dd3fcaf174b4b677c4881dad4 /components | |
parent | bffe2a699e9f0b2c41aa811ea2c3e90e40342419 (diff) | |
download | servo-ca503b4908cb45c20cc6777f9d01253057a86a97.tar.gz servo-ca503b4908cb45c20cc6777f9d01253057a86a97.zip |
style: Simplify computed::LengthOrPercentage and friends.
This is a first step to share LengthOrPercentage representation between Rust and
Gecko.
We need to preserve whether the value came from a calc() expression, for now at
least, since we do different things depending on whether we're calc or not right
now. See https://github.com/w3c/csswg-drafts/issues/3482 and dependent bugs for
example.
That means that the gecko conversion code needs to handle calc() in a bit of an
awkward way until I change it to not be needed (patches for that incoming in the
next few weeks I hope).
I need to add a hack to exclude other things from the PartialEq implementation
because the new conversion code is less lossy than the old one, and we relied on
the lousiness in AnimationValue comparison (in order to start transitions and
such, in [1] for example).
I expect to remove that manual PartialEq implementation as soon as I'm done with
the conversion.
The less lossy conversion does fix a few serialization bugs for animation values
though, like not loosing 0% values in calc() when interpolating lengths and
percentages, see the two modified tests:
* property-types.js
* test_animation_properties.html
Differential Revision: https://phabricator.services.mozilla.com/D15793
Diffstat (limited to 'components')
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) }, } } |