mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
style: Properly handle calc inside integer expressions.
This commit is contained in:
parent
8175bfd60b
commit
8205a0de90
4 changed files with 136 additions and 59 deletions
|
@ -487,6 +487,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
use values::specified;
|
||||||
|
|
||||||
pub use super::parse;
|
pub use super::parse;
|
||||||
|
|
||||||
|
@ -498,7 +499,9 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for T {
|
impl ToCss for T {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
match *self {
|
match *self {
|
||||||
T::CubicBezier(p1, p2) => {
|
T::CubicBezier(p1, p2) => {
|
||||||
try!(dest.write_str("cubic-bezier("));
|
try!(dest.write_str("cubic-bezier("));
|
||||||
|
@ -512,7 +515,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
dest.write_str(")")
|
dest.write_str(")")
|
||||||
}
|
}
|
||||||
T::Steps(steps, start_end) => {
|
T::Steps(steps, start_end) => {
|
||||||
super::serialize_steps(dest, steps, start_end)
|
super::serialize_steps(dest, specified::Integer::new(steps as i32), start_end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,7 +529,9 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for StartEnd {
|
impl ToCss for StartEnd {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
match *self {
|
match *self {
|
||||||
StartEnd::Start => dest.write_str("start"),
|
StartEnd::Start => dest.write_str("start"),
|
||||||
StartEnd::End => dest.write_str("end"),
|
StartEnd::End => dest.write_str("end"),
|
||||||
|
@ -548,7 +553,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub enum SpecifiedValue {
|
pub enum SpecifiedValue {
|
||||||
CubicBezier(Point2D<Number>, Point2D<Number>),
|
CubicBezier(Point2D<Number>, Point2D<Number>),
|
||||||
Steps(u32, StartEnd),
|
Steps(specified::Integer, StartEnd),
|
||||||
Keyword(FunctionKeyword),
|
Keyword(FunctionKeyword),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,10 +583,10 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
Ok(SpecifiedValue::CubicBezier(p1, p2))
|
Ok(SpecifiedValue::CubicBezier(p1, p2))
|
||||||
},
|
},
|
||||||
"steps" => {
|
"steps" => {
|
||||||
let (mut step_count, mut start_end) = (0, StartEnd::End);
|
let (mut step_count, mut start_end) = (specified::Integer::new(0), StartEnd::End);
|
||||||
try!(input.parse_nested_block(|input| {
|
try!(input.parse_nested_block(|input| {
|
||||||
step_count = try!(specified::parse_integer(input));
|
step_count = try!(specified::parse_integer(input));
|
||||||
if step_count < 1 {
|
if step_count.value() < 1 {
|
||||||
return Err(())
|
return Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +600,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
Ok(SpecifiedValue::Steps(step_count as u32, start_end))
|
Ok(SpecifiedValue::Steps(step_count, start_end))
|
||||||
},
|
},
|
||||||
_ => Err(())
|
_ => Err(())
|
||||||
}
|
}
|
||||||
|
@ -604,8 +609,11 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_steps<W>(dest: &mut W, steps: u32,
|
fn serialize_steps<W>(dest: &mut W,
|
||||||
start_end: StartEnd) -> fmt::Result where W: fmt::Write {
|
steps: specified::Integer,
|
||||||
|
start_end: StartEnd) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
try!(dest.write_str("steps("));
|
try!(dest.write_str("steps("));
|
||||||
try!(steps.to_css(dest));
|
try!(steps.to_css(dest));
|
||||||
if let StartEnd::Start = start_end {
|
if let StartEnd::Start = start_end {
|
||||||
|
@ -635,10 +643,10 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
SpecifiedValue::Keyword(keyword) => {
|
SpecifiedValue::Keyword(keyword) => {
|
||||||
match keyword {
|
match keyword {
|
||||||
FunctionKeyword::StepStart => {
|
FunctionKeyword::StepStart => {
|
||||||
serialize_steps(dest, 1, StartEnd::Start)
|
serialize_steps(dest, specified::Integer::new(1), StartEnd::Start)
|
||||||
},
|
},
|
||||||
FunctionKeyword::StepEnd => {
|
FunctionKeyword::StepEnd => {
|
||||||
serialize_steps(dest, 1, StartEnd::End)
|
serialize_steps(dest, specified::Integer::new(1), StartEnd::End)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
keyword.to_css(dest)
|
keyword.to_css(dest)
|
||||||
|
@ -661,7 +669,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
Point2D::new(p2.x.to_computed_value(context), p2.y.to_computed_value(context)))
|
Point2D::new(p2.x.to_computed_value(context), p2.y.to_computed_value(context)))
|
||||||
},
|
},
|
||||||
SpecifiedValue::Steps(count, start_end) => {
|
SpecifiedValue::Steps(count, start_end) => {
|
||||||
computed_value::T::Steps(count, start_end)
|
computed_value::T::Steps(count.to_computed_value(context) as u32, start_end)
|
||||||
},
|
},
|
||||||
SpecifiedValue::Keyword(keyword) => {
|
SpecifiedValue::Keyword(keyword) => {
|
||||||
match keyword {
|
match keyword {
|
||||||
|
@ -687,7 +695,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
Number::from_computed_value(&p2.y)))
|
Number::from_computed_value(&p2.y)))
|
||||||
},
|
},
|
||||||
computed_value::T::Steps(count, start_end) => {
|
computed_value::T::Steps(count, start_end) => {
|
||||||
SpecifiedValue::Steps(count, start_end)
|
let int_count = count as i32;
|
||||||
|
SpecifiedValue::Steps(specified::Integer::from_computed_value(&int_count), start_end)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,17 +246,63 @@
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use super::content;
|
use super::content;
|
||||||
use values::HasViewportPercentage;
|
use values::HasViewportPercentage;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
|
||||||
|
|
||||||
use cssparser::{Token, serialize_identifier};
|
use cssparser::{Token, serialize_identifier};
|
||||||
use std::borrow::{Cow, ToOwned};
|
use std::borrow::{Cow, ToOwned};
|
||||||
|
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct SpecifiedValue(Vec<(String, specified::Integer)>);
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
|
use std::fmt;
|
||||||
|
use style_traits::ToCss;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct T(pub Vec<(String,i32)>);
|
pub struct T(pub Vec<(String, i32)>);
|
||||||
|
|
||||||
|
impl ToCss for T {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
|
use cssparser::serialize_identifier;
|
||||||
|
if self.0.is_empty() {
|
||||||
|
return dest.write_str("none")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
for pair in &self.0 {
|
||||||
|
if !first {
|
||||||
|
try!(dest.write_str(" "));
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
try!(serialize_identifier(&pair.0, dest));
|
||||||
|
try!(dest.write_str(" "));
|
||||||
|
try!(pair.1.to_css(dest));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for SpecifiedValue {
|
||||||
|
type ComputedValue = computed_value::T;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
|
let mut ret = Vec::with_capacity(self.0.len());
|
||||||
|
for entry in &self.0 {
|
||||||
|
ret.push((entry.0.clone(), entry.1.to_computed_value(context)));
|
||||||
|
}
|
||||||
|
computed_value::T(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
|
let mut ret = Vec::with_capacity(computed.0.len());
|
||||||
|
for entry in &computed.0 {
|
||||||
|
ret.push((entry.0.clone(), specified::Integer::from_computed_value(&entry.1)));
|
||||||
|
}
|
||||||
|
SpecifiedValue(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -264,15 +310,15 @@
|
||||||
computed_value::T(Vec::new())
|
computed_value::T(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
|
||||||
no_viewport_percentage!(SpecifiedValue);
|
no_viewport_percentage!(SpecifiedValue);
|
||||||
|
|
||||||
impl ToCss for SpecifiedValue {
|
impl ToCss for SpecifiedValue {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
if self.0.is_empty() {
|
if self.0.is_empty() {
|
||||||
return dest.write_str("none");
|
return dest.write_str("none");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for pair in &self.0 {
|
for pair in &self.0 {
|
||||||
if !first {
|
if !first {
|
||||||
|
@ -280,18 +326,19 @@
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
try!(serialize_identifier(&pair.0, dest));
|
try!(serialize_identifier(&pair.0, dest));
|
||||||
try!(write!(dest, " {}", pair.1));
|
try!(dest.write_str(" "));
|
||||||
|
try!(pair.1.to_css(dest));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||||||
parse_common(1, input)
|
parse_common(1, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_common(default_value: i32, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
pub fn parse_common(default_value: i32, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||||||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||||
return Ok(SpecifiedValue(Vec::new()))
|
return Ok(SpecifiedValue(Vec::new()))
|
||||||
}
|
}
|
||||||
|
@ -307,7 +354,7 @@
|
||||||
return Err(())
|
return Err(())
|
||||||
}
|
}
|
||||||
let counter_delta =
|
let counter_delta =
|
||||||
input.try(|input| specified::parse_integer(input)).unwrap_or(default_value);
|
input.try(|input| specified::parse_integer(input)).unwrap_or(specified::Integer::new(default_value));
|
||||||
counters.push((counter_name, counter_delta))
|
counters.push((counter_name, counter_delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +369,7 @@
|
||||||
<%helpers:longhand name="counter-reset" animatable="False"
|
<%helpers:longhand name="counter-reset" animatable="False"
|
||||||
spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
|
spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
|
||||||
pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
|
pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
|
||||||
use super::counter_increment::{parse_common};
|
use super::counter_increment::parse_common;
|
||||||
|
|
||||||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||||||
parse_common(0, input)
|
parse_common(0, input)
|
||||||
|
|
|
@ -130,27 +130,9 @@ ${helpers.predefined_type("flex-shrink", "Number",
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-flexbox/#propdef-order
|
// https://drafts.csswg.org/css-flexbox/#propdef-order
|
||||||
<%helpers:longhand name="order" animatable="True" extra_prefixes="webkit"
|
${helpers.predefined_type("order", "Integer", "0",
|
||||||
spec="https://drafts.csswg.org/css-flexbox/#order-property">
|
animatable=True,
|
||||||
use values::computed::ComputedValueAsSpecified;
|
spec="https://drafts.csswg.org/css-flexbox/#order-property")}
|
||||||
|
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
|
||||||
|
|
||||||
pub type SpecifiedValue = computed_value::T;
|
|
||||||
|
|
||||||
pub mod computed_value {
|
|
||||||
pub type T = i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
|
||||||
specified::parse_integer(input)
|
|
||||||
}
|
|
||||||
</%helpers:longhand>
|
|
||||||
|
|
||||||
// FIXME: Gecko doesn't support content value yet.
|
// FIXME: Gecko doesn't support content value yet.
|
||||||
// FIXME: This property should be animatable.
|
// FIXME: This property should be animatable.
|
||||||
|
|
|
@ -203,11 +203,13 @@ impl<'a> Mul<CSSFloat> for &'a SimplifiedValueNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub fn parse_integer(input: &mut Parser) -> Result<i32, ()> {
|
pub fn parse_integer(input: &mut Parser) -> Result<Integer, ()> {
|
||||||
match try!(input.next()) {
|
match try!(input.next()) {
|
||||||
Token::Number(ref value) => value.int_value.ok_or(()),
|
Token::Number(ref value) => value.int_value.ok_or(()).map(Integer::new),
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||||
let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer)));
|
let ast = try!(input.parse_nested_block(|i| {
|
||||||
|
CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer)
|
||||||
|
}));
|
||||||
|
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
|
|
||||||
|
@ -220,7 +222,7 @@ pub fn parse_integer(input: &mut Parser) -> Result<i32, ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Some(result) => Ok(result),
|
Some(result) => Ok(Integer::from_calc(result)),
|
||||||
_ => Err(())
|
_ => Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -771,21 +773,47 @@ impl ToCss for Opacity {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct Integer(pub CSSInteger);
|
pub struct Integer {
|
||||||
|
value: CSSInteger,
|
||||||
|
was_calc: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Integer {
|
||||||
|
/// Trivially constructs a new `Integer` value.
|
||||||
|
pub fn new(val: CSSInteger) -> Self {
|
||||||
|
Integer {
|
||||||
|
value: val,
|
||||||
|
was_calc: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the integer value associated with this value.
|
||||||
|
pub fn value(&self) -> CSSInteger {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trivially constructs a new integer value from a `calc()` expression.
|
||||||
|
pub fn from_calc(val: CSSInteger) -> Self {
|
||||||
|
Integer {
|
||||||
|
value: val,
|
||||||
|
was_calc: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
no_viewport_percentage!(Integer);
|
no_viewport_percentage!(Integer);
|
||||||
|
|
||||||
impl Parse for Integer {
|
impl Parse for Integer {
|
||||||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
parse_integer(input).map(Integer)
|
parse_integer(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Integer {
|
impl Integer {
|
||||||
fn parse_with_minimum(input: &mut Parser, min: i32) -> Result<Integer, ()> {
|
fn parse_with_minimum(input: &mut Parser, min: i32) -> Result<Integer, ()> {
|
||||||
match parse_integer(input) {
|
match parse_integer(input) {
|
||||||
Ok(value) if value < min => Err(()),
|
Ok(value) if value.value() >= min => Ok(value),
|
||||||
value => value.map(Integer),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,17 +832,26 @@ impl ToComputedValue for Integer {
|
||||||
type ComputedValue = i32;
|
type ComputedValue = i32;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_computed_value(&self, _: &Context) -> i32 { self.0 }
|
fn to_computed_value(&self, _: &Context) -> i32 { self.value }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_computed_value(computed: &i32) -> Self {
|
fn from_computed_value(computed: &i32) -> Self {
|
||||||
Integer(*computed)
|
Integer::new(*computed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for Integer {
|
impl ToCss for Integer {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
write!(dest, "{}", self.0)
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
|
if self.was_calc {
|
||||||
|
dest.write_str("calc(")?;
|
||||||
|
}
|
||||||
|
write!(dest, "{}", self.value)?;
|
||||||
|
if self.was_calc {
|
||||||
|
dest.write_str(")")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,9 +860,11 @@ pub type IntegerOrAuto = Either<Integer, Auto>;
|
||||||
|
|
||||||
impl IntegerOrAuto {
|
impl IntegerOrAuto {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub fn parse_positive(context: &ParserContext, input: &mut Parser) -> Result<IntegerOrAuto, ()> {
|
pub fn parse_positive(context: &ParserContext,
|
||||||
|
input: &mut Parser)
|
||||||
|
-> Result<IntegerOrAuto, ()> {
|
||||||
match IntegerOrAuto::parse(context, input) {
|
match IntegerOrAuto::parse(context, input) {
|
||||||
Ok(Either::First(Integer(value))) if value <= 0 => Err(()),
|
Ok(Either::First(integer)) if integer.value() <= 0 => Err(()),
|
||||||
result => result,
|
result => result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue