Refactor how calc() clamping is done on computed values (fixes #15296)

This commit is contained in:
Anthony Ramine 2017-05-16 15:44:59 +02:00
parent f935f2da01
commit d0b9bd9c64
13 changed files with 173 additions and 154 deletions

View file

@ -24,7 +24,7 @@ impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
let has_percentage = other.percentage.is_some();
nsStyleCoord_CalcValue {
mLength: other.length.0,
mLength: other.length().0,
mPercent: other.percentage.unwrap_or(0.0),
mHasPercent: has_percentage,
}
@ -38,10 +38,7 @@ impl From<nsStyleCoord_CalcValue> for CalcLengthOrPercentage {
} else {
None
};
CalcLengthOrPercentage {
length: Au(other.mLength),
percentage: percentage,
}
Self::new(Au(other.mLength), percentage)
}
}

View file

@ -1035,11 +1035,9 @@ impl Animatable for CalcLengthOrPercentage {
}
}
Ok(CalcLengthOrPercentage {
length: try!(self.length.add_weighted(&other.length, self_portion, other_portion)),
percentage: try!(add_weighted_half(self.percentage, other.percentage,
self_portion, other_portion)),
})
let length = self.length().add_weighted(&other.length(), self_portion, other_portion)?;
let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?;
Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
}
#[inline]

View file

@ -8,6 +8,7 @@ use app_units::{Au, AU_PER_PX};
use ordered_float::NotNaN;
use std::fmt;
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::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength};
@ -48,7 +49,7 @@ impl ToComputedValue for specified::Length {
fn to_computed_value(&self, context: &Context) -> Au {
match *self {
specified::Length::NoCalc(l) => l.to_computed_value(context),
specified::Length::Calc(range, ref calc) => range.clamp(calc.to_computed_value(context).length()),
specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(),
}
}
@ -62,15 +63,35 @@ impl ToComputedValue for specified::Length {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub struct CalcLengthOrPercentage {
pub length: Au,
pub clamping_mode: AllowedLengthType,
length: Au,
pub percentage: Option<CSSFloat>,
}
impl CalcLengthOrPercentage {
/// Returns a new `CalcLengthOrPercentage`.
#[inline]
pub fn new(length: Au, percentage: Option<CSSFloat>) -> Self {
Self::with_clamping_mode(length, percentage, AllowedLengthType::All)
}
/// Returns a new `CalcLengthOrPercentage` with a specific clamping mode.
#[inline]
pub fn with_clamping_mode(length: Au,
percentage: Option<CSSFloat>,
clamping_mode: AllowedLengthType)
-> Self {
Self {
clamping_mode: clamping_mode,
length: length,
percentage: percentage,
}
}
#[inline]
#[allow(missing_docs)]
pub fn length(&self) -> Au {
self.length
self.clamping_mode.clamp(self.length)
}
#[inline]
@ -81,10 +102,12 @@ impl CalcLengthOrPercentage {
/// 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_computed(&self, container_len: Option<Au>) -> Option<Au> {
pub fn to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
match (container_len, self.percentage) {
(Some(len), Some(percent)) => Some(self.length + len.scale_by(percent)),
(_, None) => Some(self.length),
(Some(len), Some(percent)) => {
Some(self.clamping_mode.clamp(self.length + len.scale_by(percent)))
},
(_, None) => Some(self.length()),
_ => None,
}
}
@ -94,16 +117,10 @@ impl From<LengthOrPercentage> for CalcLengthOrPercentage {
fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage {
match len {
LengthOrPercentage::Percentage(this) => {
CalcLengthOrPercentage {
length: Au(0),
percentage: Some(this),
}
CalcLengthOrPercentage::new(Au(0), Some(this))
}
LengthOrPercentage::Length(this) => {
CalcLengthOrPercentage {
length: this,
percentage: None,
}
CalcLengthOrPercentage::new(this, None)
}
LengthOrPercentage::Calc(this) => {
this
@ -116,16 +133,10 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> {
match len {
LengthOrPercentageOrAuto::Percentage(this) => {
Some(CalcLengthOrPercentage {
length: Au(0),
percentage: Some(this),
})
Some(CalcLengthOrPercentage::new(Au(0), Some(this)))
}
LengthOrPercentageOrAuto::Length(this) => {
Some(CalcLengthOrPercentage {
length: this,
percentage: None,
})
Some(CalcLengthOrPercentage::new(this, None))
}
LengthOrPercentageOrAuto::Calc(this) => {
Some(this)
@ -176,6 +187,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
}
CalcLengthOrPercentage {
clamping_mode: self.clamping_mode,
length: length,
percentage: self.percentage,
}
@ -184,6 +196,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
#[inline]
fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self {
specified::CalcLengthOrPercentage {
clamping_mode: computed.clamping_mode,
absolute: Some(computed.length),
percentage: computed.percentage,
..Default::default()
@ -235,6 +248,17 @@ impl LengthOrPercentage {
Calc(c) => (c.length(), NotNaN::new(c.percentage()).unwrap()),
}
}
/// Returns the used value.
pub fn to_used_value(&self, containing_length: Au) -> Au {
match *self {
LengthOrPercentage::Length(length) => length,
LengthOrPercentage::Percentage(p) => containing_length.scale_by(p),
LengthOrPercentage::Calc(ref calc) => {
calc.to_used_value(Some(containing_length)).unwrap()
},
}
}
}
impl fmt::Debug for LengthOrPercentage {
@ -481,6 +505,18 @@ pub enum LengthOrPercentageOrNone {
None,
}
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(length),
LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)),
LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)),
}
}
}
impl fmt::Debug for LengthOrPercentageOrNone {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {

View file

@ -12,6 +12,7 @@ use parser::ParserContext;
use std::ascii::AsciiExt;
use std::fmt;
use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use values::{CSSInteger, CSSFloat, HasViewportPercentage};
use values::specified::{Angle, Time};
use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength};
@ -63,6 +64,7 @@ pub enum CalcUnit {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub struct CalcLengthOrPercentage {
pub clamping_mode: AllowedLengthType,
pub absolute: Option<Au>,
pub vw: Option<CSSFloat>,
pub vh: Option<CSSFloat>,
@ -271,8 +273,12 @@ impl CalcNode {
/// Tries to simplify this expression into a `<length>` or `<percentage`>
/// value.
fn to_length_or_percentage(&self) -> Result<CalcLengthOrPercentage, ()> {
let mut ret = CalcLengthOrPercentage::default();
fn to_length_or_percentage(&self, clamping_mode: AllowedLengthType)
-> Result<CalcLengthOrPercentage, ()> {
let mut ret = CalcLengthOrPercentage {
clamping_mode: clamping_mode,
.. Default::default()
};
self.add_length_or_percentage_to(&mut ret, 1.0)?;
Ok(ret)
}
@ -498,21 +504,23 @@ impl CalcNode {
/// Convenience parsing function for `<length> | <percentage>`.
pub fn parse_length_or_percentage(
context: &ParserContext,
input: &mut Parser)
input: &mut Parser,
clamping_mode: AllowedLengthType)
-> Result<CalcLengthOrPercentage, ()>
{
Self::parse(context, input, CalcUnit::LengthOrPercentage)?
.to_length_or_percentage()
.to_length_or_percentage(clamping_mode)
}
/// Convenience parsing function for `<length>`.
pub fn parse_length(
context: &ParserContext,
input: &mut Parser)
input: &mut Parser,
clamping_mode: AllowedLengthType)
-> Result<CalcLengthOrPercentage, ()>
{
Self::parse(context, input, CalcUnit::Length)?
.to_length_or_percentage()
.to_length_or_percentage(clamping_mode)
}
/// Convenience parsing function for `<number>`.

View file

@ -539,7 +539,7 @@ pub enum Length {
/// A calc expression.
///
/// https://drafts.csswg.org/css-values/#calc-notation
Calc(AllowedLengthType, Box<CalcLengthOrPercentage>),
Calc(Box<CalcLengthOrPercentage>),
}
impl From<NoCalcLength> for Length {
@ -553,7 +553,7 @@ impl HasViewportPercentage for Length {
fn has_viewport_percentage(&self) -> bool {
match *self {
Length::NoCalc(ref inner) => inner.has_viewport_percentage(),
Length::Calc(_, ref calc) => calc.has_viewport_percentage(),
Length::Calc(ref calc) => calc.has_viewport_percentage(),
}
}
}
@ -562,7 +562,7 @@ impl ToCss for Length {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
Length::NoCalc(ref inner) => inner.to_css(dest),
Length::Calc(_, ref calc) => calc.to_css(dest),
Length::Calc(ref calc) => calc.to_css(dest),
}
}
}
@ -637,10 +637,7 @@ impl Length {
},
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
input.parse_nested_block(|input| {
CalcNode::parse_length(context, input)
.map(|calc| {
Length::Calc(num_context, Box::new(calc))
})
CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc)))
}),
_ => Err(())
}
@ -770,7 +767,7 @@ impl From<Length> for LengthOrPercentage {
fn from(len: Length) -> LengthOrPercentage {
match len {
Length::NoCalc(l) => LengthOrPercentage::Length(l),
Length::Calc(_, l) => LengthOrPercentage::Calc(l),
Length::Calc(l) => LengthOrPercentage::Calc(l),
}
}
}
@ -832,7 +829,7 @@ impl LengthOrPercentage {
Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value.value))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i)
CalcNode::parse_length_or_percentage(context, i, num_context)
}));
Ok(LengthOrPercentage::Calc(Box::new(calc)))
},
@ -986,7 +983,7 @@ impl LengthOrPercentageOrAuto {
Ok(LengthOrPercentageOrAuto::Auto),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i)
CalcNode::parse_length_or_percentage(context, i, num_context)
}));
Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc)))
},
@ -1092,7 +1089,7 @@ impl LengthOrPercentageOrNone {
}
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i)
CalcNode::parse_length_or_percentage(context, i, num_context)
}));
Ok(LengthOrPercentageOrNone::Calc(Box::new(calc)))
},
@ -1169,7 +1166,7 @@ impl LengthOrPercentageOrAutoOrContent {
Ok(LengthOrPercentageOrAutoOrContent::Content),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i)
CalcNode::parse_length_or_percentage(context, i, num_context)
}));
Ok(LengthOrPercentageOrAutoOrContent::Calc(Box::new(calc)))
},

View file

@ -234,19 +234,14 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
match length.to_computed_value(context) {
ComputedLengthOrPercentage::Length(length) => {
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage {
length: -length,
percentage: Some(1.0),
})
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-length, Some(1.0)))
},
ComputedLengthOrPercentage::Percentage(p) => {
ComputedLengthOrPercentage::Percentage(1.0 - p)
},
ComputedLengthOrPercentage::Calc(calc) => {
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage {
length: -calc.length,
percentage: Some(1.0 - calc.percentage.unwrap_or(0.)),
})
let p = 1. - calc.percentage.unwrap_or(0.);
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-calc.length(), Some(p)))
},
}
},