Auto merge of #13121 - emilio:negative-calc, r=SimonSapin

style: Properly track whether negative values of calc() are allowed

<!-- Please describe your changes on the following line: -->

r? @SimonSapin

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors

<!-- Either: -->
- [x] There are tests for these changes OR

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

In order to clamp them at computed value time.

<!-- 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/13121)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-09-01 11:41:35 -05:00 committed by GitHub
commit fbf77e40fe
3 changed files with 64 additions and 27 deletions

View file

@ -167,6 +167,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
self.compute_from_viewport_and_font_size(context.viewport_size(), self.compute_from_viewport_and_font_size(context.viewport_size(),
context.style().get_font().clone_font_size(), context.style().get_font().clone_font_size(),
context.style().root_font_size()) context.style().root_font_size())
} }
} }

View file

@ -286,22 +286,24 @@ impl Length {
} }
#[inline] #[inline]
fn parse_internal(input: &mut Parser, context: &AllowedNumericType) -> Result<Length, ()> { fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result<Length, ()> {
match try!(input.next()) { match try!(input.next()) {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) => Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit), Length::parse_dimension(value.value, unit),
Token::Number(ref value) if value.value == 0. => Token::Number(ref value) if value.value == 0. =>
Ok(Length::Absolute(Au(0))), Ok(Length::Absolute(Au(0))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
input.parse_nested_block(CalcLengthOrPercentage::parse_length), input.parse_nested_block(|input| {
CalcLengthOrPercentage::parse_length(input, context)
}),
_ => Err(()) _ => Err(())
} }
} }
pub fn parse(input: &mut Parser) -> Result<Length, ()> { pub fn parse(input: &mut Parser) -> Result<Length, ()> {
Length::parse_internal(input, &AllowedNumericType::All) Length::parse_internal(input, AllowedNumericType::All)
} }
pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> {
Length::parse_internal(input, &AllowedNumericType::NonNegative) Length::parse_internal(input, AllowedNumericType::NonNegative)
} }
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
match_ignore_ascii_case! { unit, match_ignore_ascii_case! { unit,
@ -467,6 +469,8 @@ pub struct CalcLengthOrPercentage {
pub ch: Option<FontRelativeLength>, pub ch: Option<FontRelativeLength>,
pub rem: Option<FontRelativeLength>, pub rem: Option<FontRelativeLength>,
pub percentage: Option<Percentage>, pub percentage: Option<Percentage>,
/// Whether the value returned can be negative at computed value time.
pub allowed_numeric_type: AllowedNumericType,
} }
impl CalcLengthOrPercentage { impl CalcLengthOrPercentage {
@ -617,15 +621,19 @@ impl CalcLengthOrPercentage {
} }
} }
fn parse_length(input: &mut Parser) -> Result<Length, ()> { fn parse_length(input: &mut Parser,
CalcLengthOrPercentage::parse(input, CalcUnit::Length).map(Length::Calc) context: AllowedNumericType) -> Result<Length, ()> {
CalcLengthOrPercentage::parse(input, CalcUnit::Length, context).map(Length::Calc)
} }
fn parse_length_or_percentage(input: &mut Parser) -> Result<CalcLengthOrPercentage, ()> { fn parse_length_or_percentage(input: &mut Parser,
CalcLengthOrPercentage::parse(input, CalcUnit::LengthOrPercentage) context: AllowedNumericType) -> Result<CalcLengthOrPercentage, ()> {
CalcLengthOrPercentage::parse(input, CalcUnit::LengthOrPercentage, context)
} }
fn parse(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcLengthOrPercentage, ()> { fn parse(input: &mut Parser,
expected_unit: CalcUnit,
context: AllowedNumericType) -> Result<CalcLengthOrPercentage, ()> {
let ast = try!(CalcLengthOrPercentage::parse_sum(input, expected_unit)); let ast = try!(CalcLengthOrPercentage::parse_sum(input, expected_unit));
let mut simplified = Vec::new(); let mut simplified = Vec::new();
@ -692,6 +700,7 @@ impl CalcLengthOrPercentage {
ch: ch.map(FontRelativeLength::Ch), ch: ch.map(FontRelativeLength::Ch),
rem: rem.map(FontRelativeLength::Rem), rem: rem.map(FontRelativeLength::Rem),
percentage: percentage.map(Percentage), percentage: percentage.map(Percentage),
allowed_numeric_type: context,
}) })
} }
@ -770,6 +779,7 @@ impl CalcLengthOrPercentage {
val.to_computed_value(viewport_size)); val.to_computed_value(viewport_size));
} }
} }
for val in &[self.ch, self.em, self.ex, self.rem] { for val in &[self.ch, self.em, self.ex, self.rem] {
if let Some(val) = *val { if let Some(val) = *val {
length = Some(length.unwrap_or(Au(0)) + val.to_computed_value( length = Some(length.unwrap_or(Au(0)) + val.to_computed_value(
@ -777,9 +787,23 @@ impl CalcLengthOrPercentage {
} }
} }
// https://drafts.csswg.org/css-values/#calc-range
let mut percentage = self.percentage.map(|p| p.0);
if let AllowedNumericType::NonNegative = self.allowed_numeric_type {
if let Some(ref mut length) = length {
*length = cmp::max(*length, Au(0));
}
if let Some(ref mut percentage) = percentage {
if *percentage < 0. {
*percentage = 0.;
}
}
}
computed::CalcLengthOrPercentage { computed::CalcLengthOrPercentage {
length: length, length: length,
percentage: self.percentage.map(|p| p.0) percentage: percentage,
} }
} }
} }
@ -884,7 +908,7 @@ impl LengthOrPercentage {
LengthOrPercentage::Length(Length::Absolute(Au(0))) LengthOrPercentage::Length(Length::Absolute(Au(0)))
} }
fn parse_internal(input: &mut Parser, context: &AllowedNumericType) fn parse_internal(input: &mut Parser, context: AllowedNumericType)
-> Result<LengthOrPercentage, ()> -> Result<LengthOrPercentage, ()>
{ {
match try!(input.next()) { match try!(input.next()) {
@ -895,7 +919,9 @@ impl LengthOrPercentage {
Token::Number(ref value) if value.value == 0. => Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))), Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); let calc = try!(input.parse_nested_block(|input| {
CalcLengthOrPercentage::parse_length_or_percentage(input, context)
}));
Ok(LengthOrPercentage::Calc(calc)) Ok(LengthOrPercentage::Calc(calc))
}, },
_ => Err(()) _ => Err(())
@ -903,11 +929,11 @@ impl LengthOrPercentage {
} }
#[inline] #[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentage, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
LengthOrPercentage::parse_internal(input, &AllowedNumericType::All) LengthOrPercentage::parse_internal(input, AllowedNumericType::All)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
LengthOrPercentage::parse_internal(input, &AllowedNumericType::NonNegative) LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative)
} }
} }
@ -942,7 +968,7 @@ impl ToCss for LengthOrPercentageOrAuto {
} }
impl LengthOrPercentageOrAuto { impl LengthOrPercentageOrAuto {
fn parse_internal(input: &mut Parser, context: &AllowedNumericType) fn parse_internal(input: &mut Parser, context: AllowedNumericType)
-> Result<LengthOrPercentageOrAuto, ()> -> Result<LengthOrPercentageOrAuto, ()>
{ {
match try!(input.next()) { match try!(input.next()) {
@ -955,7 +981,9 @@ impl LengthOrPercentageOrAuto {
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
Ok(LengthOrPercentageOrAuto::Auto), Ok(LengthOrPercentageOrAuto::Auto),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); let calc = try!(input.parse_nested_block(|input| {
CalcLengthOrPercentage::parse_length_or_percentage(input, context)
}));
Ok(LengthOrPercentageOrAuto::Calc(calc)) Ok(LengthOrPercentageOrAuto::Calc(calc))
}, },
_ => Err(()) _ => Err(())
@ -963,11 +991,11 @@ impl LengthOrPercentageOrAuto {
} }
#[inline] #[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
LengthOrPercentageOrAuto::parse_internal(input, &AllowedNumericType::All) LengthOrPercentageOrAuto::parse_internal(input, AllowedNumericType::All)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
LengthOrPercentageOrAuto::parse_internal(input, &AllowedNumericType::NonNegative) LengthOrPercentageOrAuto::parse_internal(input, AllowedNumericType::NonNegative)
} }
} }
@ -1001,7 +1029,7 @@ impl ToCss for LengthOrPercentageOrNone {
} }
} }
impl LengthOrPercentageOrNone { impl LengthOrPercentageOrNone {
fn parse_internal(input: &mut Parser, context: &AllowedNumericType) fn parse_internal(input: &mut Parser, context: AllowedNumericType)
-> Result<LengthOrPercentageOrNone, ()> -> Result<LengthOrPercentageOrNone, ()>
{ {
match try!(input.next()) { match try!(input.next()) {
@ -1012,7 +1040,9 @@ impl LengthOrPercentageOrNone {
Token::Number(ref value) if value.value == 0. => Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))), Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); let calc = try!(input.parse_nested_block(|input| {
CalcLengthOrPercentage::parse_length_or_percentage(input, context)
}));
Ok(LengthOrPercentageOrNone::Calc(calc)) Ok(LengthOrPercentageOrNone::Calc(calc))
}, },
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
@ -1022,11 +1052,11 @@ impl LengthOrPercentageOrNone {
} }
#[inline] #[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> {
LengthOrPercentageOrNone::parse_internal(input, &AllowedNumericType::All) LengthOrPercentageOrNone::parse_internal(input, AllowedNumericType::All)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> {
LengthOrPercentageOrNone::parse_internal(input, &AllowedNumericType::NonNegative) LengthOrPercentageOrNone::parse_internal(input, AllowedNumericType::NonNegative)
} }
} }
@ -1055,7 +1085,7 @@ impl ToCss for LengthOrNone {
} }
} }
impl LengthOrNone { impl LengthOrNone {
fn parse_internal(input: &mut Parser, context: &AllowedNumericType) fn parse_internal(input: &mut Parser, context: AllowedNumericType)
-> Result<LengthOrNone, ()> -> Result<LengthOrNone, ()>
{ {
match try!(input.next()) { match try!(input.next()) {
@ -1064,7 +1094,9 @@ impl LengthOrNone {
Token::Number(ref value) if value.value == 0. => Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrNone::Length(Length::Absolute(Au(0)))), Ok(LengthOrNone::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
input.parse_nested_block(CalcLengthOrPercentage::parse_length).map(LengthOrNone::Length), input.parse_nested_block(|input| {
CalcLengthOrPercentage::parse_length(input, context)
}).map(LengthOrNone::Length),
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
Ok(LengthOrNone::None), Ok(LengthOrNone::None),
_ => Err(()) _ => Err(())
@ -1072,11 +1104,11 @@ impl LengthOrNone {
} }
#[inline] #[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrNone, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrNone, ()> {
LengthOrNone::parse_internal(input, &AllowedNumericType::All) LengthOrNone::parse_internal(input, AllowedNumericType::All)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrNone, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrNone, ()> {
LengthOrNone::parse_internal(input, &AllowedNumericType::NonNegative) LengthOrNone::parse_internal(input, AllowedNumericType::NonNegative)
} }
} }
@ -1127,7 +1159,9 @@ impl LengthOrPercentageOrAutoOrContent {
Token::Ident(ref value) if value.eq_ignore_ascii_case("content") => Token::Ident(ref value) if value.eq_ignore_ascii_case("content") =>
Ok(LengthOrPercentageOrAutoOrContent::Content), Ok(LengthOrPercentageOrAutoOrContent::Content),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage)); let calc = try!(input.parse_nested_block(|input| {
CalcLengthOrPercentage::parse_length_or_percentage(input, context)
}));
Ok(LengthOrPercentageOrAutoOrContent::Calc(calc)) Ok(LengthOrPercentageOrAutoOrContent::Calc(calc))
}, },
_ => Err(()) _ => Err(())

View file

@ -63,6 +63,8 @@ macro_rules! __define_css_keyword_enum__actual {
pub mod specified { pub mod specified {
#[repr(u8)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AllowedNumericType { pub enum AllowedNumericType {
All, All,