Auto merge of #17783 - BorisChiou:stylo/animation/restrictions, r=nox

stylo: Bug 1374233 - Clamp interpolated values for properties which need to be restricted

Some properties only accept non-negative values, or values greater than or equal to one. It is possible to produce an negative interpolated values while using negative timing functions, so we have to apply a restriction to these values to avoid getting invalid values.

For example, line-height must be non-negative, but the output progress of some timing functions (e,g. cubic-bezier(0.25, -2, 0.75, 1)) may be a negative value, so the interpolated result of line-height is also negative.

---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix Bug 1374233.
- [X] These changes do not require tests because we have tests in Gecko side already.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17783)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-08-07 11:05:17 -05:00 committed by GitHub
commit 016ea11cba
56 changed files with 1039 additions and 371 deletions

View file

@ -5,7 +5,8 @@
//! Computed types for CSS values related to backgrounds.
use properties::animated_properties::{Animatable, RepeatableListAnimatable};
use values::animated::ToAnimatedZero;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
use values::animated::{ToAnimatedValue, ToAnimatedZero};
use values::computed::length::LengthOrPercentageOrAuto;
use values::generics::background::BackgroundSize as GenericBackgroundSize;
@ -56,3 +57,52 @@ impl ToAnimatedZero for BackgroundSize {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
}
impl ToAnimatedValue for BackgroundSize {
type AnimatedValue = Self;
#[inline]
fn to_animated_value(self) -> Self {
self
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
use app_units::Au;
use values::computed::Percentage;
let clamp_animated_value = |value: LengthOrPercentageOrAuto| -> LengthOrPercentageOrAuto {
match value {
LengthOrPercentageOrAuto::Length(len) => {
LengthOrPercentageOrAuto::Length(Au(::std::cmp::max(len.0, 0)))
},
LengthOrPercentageOrAuto::Percentage(percent) => {
LengthOrPercentageOrAuto::Percentage(Percentage(percent.0.max(0.)))
},
_ => value
}
};
match animated {
GenericBackgroundSize::Explicit { width, height } => {
GenericBackgroundSize::Explicit {
width: clamp_animated_value(width),
height: clamp_animated_value(height)
}
},
_ => animated
}
}
}
impl ToAnimatedValue for BackgroundSizeList {
type AnimatedValue = Self;
#[inline]
fn to_animated_value(self) -> Self {
self
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
BackgroundSizeList(ToAnimatedValue::from_animated_value(animated.0))
}
}

View file

@ -6,23 +6,23 @@
#[cfg(not(feature = "gecko"))]
use values::Impossible;
use values::computed::{Angle, Number};
use values::computed::{Angle, NonNegativeNumber};
use values::computed::color::Color;
use values::computed::length::Length;
use values::computed::length::{Length, NonNegativeLength};
use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
/// A computed value for a single shadow of the `box-shadow` property.
pub type BoxShadow = GenericBoxShadow<Color, Length, Length>;
pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
/// A computed value for a single `filter`.
#[cfg(feature = "gecko")]
pub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow>;
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow>;
/// A computed value for a single `filter`.
#[cfg(not(feature = "gecko"))]
pub type Filter = GenericFilter<Angle, Number, Length, Impossible>;
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>;
/// A computed value for the `drop-shadow()` filter.
pub type SimpleShadow = GenericSimpleShadow<Color, Length, Length>;
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;

View file

@ -11,6 +11,8 @@ use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use super::{Number, ToComputedValue, Context};
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
use values::computed::{NonNegativeAu, NonNegativeNumber};
use values::generics::NonNegative;
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
use values::specified::length::ViewportPercentageLength;
@ -71,7 +73,7 @@ impl ToComputedValue for specified::NoCalcLength {
specified::NoCalcLength::ViewportPercentage(length) =>
length.to_computed_value(context.viewport_size()),
specified::NoCalcLength::ServoCharacterWidth(length) =>
length.to_computed_value(context.style().get_font().clone_font_size()),
length.to_computed_value(context.style().get_font().clone_font_size().0),
#[cfg(feature = "gecko")]
specified::NoCalcLength::Physical(length) =>
length.to_computed_value(context),
@ -265,7 +267,7 @@ impl specified::CalcLengthOrPercentage {
/// Compute font-size or line-height taking into account text-zoom if necessary.
pub fn to_computed_value_zoomed(&self, context: &Context) -> CalcLengthOrPercentage {
self.to_computed_value_with_zoom(context, |abs| context.maybe_zoom_text(abs))
self.to_computed_value_with_zoom(context, |abs| context.maybe_zoom_text(abs.into()).0)
}
}
@ -349,6 +351,20 @@ impl LengthOrPercentage {
},
}
}
/// Returns the clamped non-negative values.
#[inline]
pub fn clamp_to_non_negative(self) -> Self {
match self {
LengthOrPercentage::Length(length) => {
LengthOrPercentage::Length(Au(::std::cmp::max(length.0, 0)))
},
LengthOrPercentage::Percentage(percentage) => {
LengthOrPercentage::Percentage(Percentage(percentage.0.max(0.)))
},
_ => self
}
}
}
impl fmt::Debug for LengthOrPercentage {
@ -550,6 +566,43 @@ impl ToComputedValue for specified::LengthOrPercentageOrNone {
}
}
/// A wrapper of LengthOrPercentage, whose value must be >= 0.
pub type NonNegativeLengthOrPercentage = NonNegative<LengthOrPercentage>;
impl From<NonNegativeAu> for NonNegativeLengthOrPercentage {
#[inline]
fn from(length: NonNegativeAu) -> Self {
LengthOrPercentage::Length(length.0).into()
}
}
impl From<LengthOrPercentage> for NonNegativeLengthOrPercentage {
#[inline]
fn from(lop: LengthOrPercentage) -> Self {
NonNegative::<LengthOrPercentage>(lop)
}
}
impl NonNegativeLengthOrPercentage {
/// Get zero value.
#[inline]
pub fn zero() -> Self {
NonNegative::<LengthOrPercentage>(LengthOrPercentage::zero())
}
/// Returns true if the computed value is absolute 0 or 0%.
#[inline]
pub fn is_definitely_zero(&self) -> bool {
self.0.is_definitely_zero()
}
/// Returns the used value.
#[inline]
pub fn to_used_value(&self, containing_length: Au) -> Au {
self.0.to_used_value(containing_length)
}
}
/// A computed `<length>` value.
pub type Length = Au;
@ -573,6 +626,18 @@ impl LengthOrNumber {
/// Either a computed `<length>` or the `normal` keyword.
pub type LengthOrNormal = Either<Length, Normal>;
/// A wrapper of Length, whose value must be >= 0.
pub type NonNegativeLength = NonNegativeAu;
/// Either a computed NonNegativeLength or the `auto` keyword.
pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;
/// Either a computed NonNegativeLength or the `normal` keyword.
pub type NonNegativeLengthOrNormal = Either<NonNegativeLength, Normal>;
/// Either a computed NonNegativeLength or a NonNegativeNumber value.
pub type NonNegativeLengthOrNumber = Either<NonNegativeLength, NonNegativeNumber>;
/// A value suitable for a `min-width`, `min-height`, `width` or `height` property.
/// See values/specified/length.rs for more details.
#[allow(missing_docs)]

View file

@ -18,6 +18,7 @@ use std::f64::consts::PI;
use std::fmt;
use style_traits::ToCss;
use super::{CSSFloat, CSSInteger};
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
use super::generics::grid::TrackList as GenericTrackList;
@ -43,8 +44,9 @@ pub use super::generics::grid::GridLine;
pub use super::specified::url::SpecifiedUrl;
pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength, Percentage};
pub use self::length::NonNegativeLengthOrPercentage;
pub use self::position::Position;
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray};
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth};
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
pub use self::transform::{TimingFunction, TransformOrigin};
@ -136,12 +138,12 @@ impl<'a> Context<'a> {
/// Apply text-zoom if enabled
#[cfg(feature = "gecko")]
pub fn maybe_zoom_text(&self, size: Au) -> Au {
pub fn maybe_zoom_text(&self, size: NonNegativeAu) -> NonNegativeAu {
// We disable zoom for <svg:text> by unsetting the
// -x-text-zoom property, which leads to a false value
// in mAllowZoom
if self.style().get_font().gecko.mAllowZoom {
self.device().zoom_text(size)
self.device().zoom_text(size.0).into()
} else {
size
}
@ -149,7 +151,7 @@ impl<'a> Context<'a> {
/// (Servo doesn't do text-zoom)
#[cfg(feature = "servo")]
pub fn maybe_zoom_text(&self, size: Au) -> Au {
pub fn maybe_zoom_text(&self, size: NonNegativeAu) -> NonNegativeAu {
size
}
}
@ -425,6 +427,40 @@ impl ComputedValueAsSpecified for specified::BorderStyle {}
/// A `<number>` value.
pub type Number = CSSFloat;
/// A wrapper of Number, but the value >= 0.
pub type NonNegativeNumber = NonNegative<CSSFloat>;
impl From<CSSFloat> for NonNegativeNumber {
#[inline]
fn from(number: CSSFloat) -> NonNegativeNumber {
NonNegative::<CSSFloat>(number)
}
}
impl From<NonNegativeNumber> for CSSFloat {
#[inline]
fn from(number: NonNegativeNumber) -> CSSFloat {
number.0
}
}
/// A wrapper of Number, but the value >= 1.
pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;
impl From<CSSFloat> for GreaterThanOrEqualToOneNumber {
#[inline]
fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {
GreaterThanOrEqualToOne::<CSSFloat>(number)
}
}
impl From<GreaterThanOrEqualToOneNumber> for CSSFloat {
#[inline]
fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {
number.0
}
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToCss)]
@ -476,9 +512,25 @@ impl IntegerOrAuto {
}
}
/// A wrapper of Integer, but only accept a value >= 1.
pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;
impl From<CSSInteger> for PositiveInteger {
#[inline]
fn from(int: CSSInteger) -> PositiveInteger {
GreaterThanOrEqualToOne::<CSSInteger>(int)
}
}
/// PositiveInteger | auto
pub type PositiveIntegerOrAuto = Either<PositiveInteger, Auto>;
/// <length> | <percentage> | <number>
pub type LengthOrPercentageOrNumber = Either<Number, LengthOrPercentage>;
/// NonNegativeLengthOrPercentage | NonNegativeNumber
pub type NonNegativeLengthOrPercentageOrNumber = Either<NonNegativeNumber, NonNegativeLengthOrPercentage>;
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
@ -556,3 +608,40 @@ impl ClipRectOrAuto {
/// <color> | auto
pub type ColorOrAuto = Either<Color, Auto>;
/// A wrapper of Au, but the value >= 0.
pub type NonNegativeAu = NonNegative<Au>;
impl NonNegativeAu {
/// Return a zero value.
#[inline]
pub fn zero() -> Self {
NonNegative::<Au>(Au(0))
}
/// Return a NonNegativeAu from pixel.
#[inline]
pub fn from_px(px: i32) -> Self {
NonNegative::<Au>(Au::from_px(::std::cmp::max(px, 0)))
}
/// Get the inner value of |NonNegativeAu.0|.
#[inline]
pub fn value(self) -> i32 {
(self.0).0
}
/// Scale this NonNegativeAu.
#[inline]
pub fn scale_by(self, factor: f32) -> Self {
// scale this by zero if factor is negative.
NonNegative::<Au>(self.0.scale_by(factor.max(0.)))
}
}
impl From<Au> for NonNegativeAu {
#[inline]
fn from(au: Au) -> NonNegativeAu {
NonNegative::<Au>(au)
}
}

View file

@ -7,6 +7,7 @@
use app_units::Au;
use values::{Either, RGBA};
use values::computed::{LengthOrPercentageOrNumber, Opacity};
use values::computed::{NonNegativeAu, NonNegativeLengthOrPercentageOrNumber};
use values::generics::svg as generic;
/// Computed SVG Paint value
@ -43,8 +44,17 @@ impl From<Au> for SVGLength {
}
}
/// An non-negative wrapper of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeLengthOrPercentageOrNumber>;
impl From<NonNegativeAu> for SVGWidth {
fn from(length: NonNegativeAu) -> Self {
generic::SVGLength::Length(Either::Second(length.into()))
}
}
/// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<LengthOrPercentageOrNumber>;
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthOrPercentageOrNumber>;
impl Default for SVGStrokeDashArray {
fn default() -> Self {

View file

@ -4,10 +4,10 @@
//! Computed types for text properties.
use app_units::Au;
use properties::animated_properties::Animatable;
use values::{CSSInteger, CSSFloat};
use values::animated::ToAnimatedZero;
use values::computed::{NonNegativeAu, NonNegativeNumber};
use values::computed::length::{Length, LengthOrPercentage};
use values::generics::text::InitialLetter as GenericInitialLetter;
use values::generics::text::LineHeight as GenericLineHeight;
@ -23,7 +23,7 @@ pub type LetterSpacing = Spacing<Length>;
pub type WordSpacing = Spacing<LengthOrPercentage>;
/// A computed value for the `line-height` property.
pub type LineHeight = GenericLineHeight<CSSFloat, Au>;
pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeAu>;
impl Animatable for LineHeight {
#[inline]