Auto merge of #16144 - emilio:number-calc, r=heycam

style: Make numbers keep track of whether they were specified as calc().

Fixes #15960

<!-- 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/16144)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-03-27 06:13:39 -07:00 committed by GitHub
commit b6bc49225e
28 changed files with 778 additions and 319 deletions

View file

@ -200,7 +200,7 @@ impl ToFilterOps for filter::T {
Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)), Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)), Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)),
Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)), Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)),
Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.0)), Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.radians())),
Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)), Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)),
Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())), Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())),
Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)), Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),

View file

@ -496,8 +496,8 @@ impl LayoutElementHelpers for LayoutJS<Element> {
shared_lock, shared_lock,
PropertyDeclaration::BorderSpacing( PropertyDeclaration::BorderSpacing(
Box::new(border_spacing::SpecifiedValue { Box::new(border_spacing::SpecifiedValue {
horizontal: width_value.clone(), horizontal: width_value,
vertical: width_value, vertical: None,
})))); }))));
} }

View file

@ -343,7 +343,7 @@ impl PropertyAnimation {
#[inline] #[inline]
fn does_animate(&self) -> bool { fn does_animate(&self) -> bool {
self.property.does_animate() && self.duration != Time(0.0) self.property.does_animate() && self.duration.seconds() != 0.0
} }
/// Whether this animation has the same end value as another one. /// Whether this animation has the same end value as another one.
@ -681,7 +681,7 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
transition_property, name); transition_property, name);
match PropertyAnimation::from_transition_property(*transition_property, match PropertyAnimation::from_transition_property(*transition_property,
timing_function, timing_function,
Time(relative_duration as f32), Time::from_seconds(relative_duration as f32),
&from_style, &from_style,
&target_style) { &target_style) {
Some(property_animation) => { Some(property_animation) => {

View file

@ -59,13 +59,16 @@ impl From<SpecifiedTimingFunction> for nsTimingFunction {
match function { match function {
SpecifiedTimingFunction::Steps(steps, StartEnd::Start) => { SpecifiedTimingFunction::Steps(steps, StartEnd::Start) => {
tf.set_as_step(nsTimingFunction_Type::StepStart, steps); debug_assert!(steps.value() >= 0);
tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value() as u32);
}, },
SpecifiedTimingFunction::Steps(steps, StartEnd::End) => { SpecifiedTimingFunction::Steps(steps, StartEnd::End) => {
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps); debug_assert!(steps.value() >= 0);
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32);
}, },
SpecifiedTimingFunction::CubicBezier(p1, p2) => { SpecifiedTimingFunction::CubicBezier(p1, p2) => {
tf.set_as_cubic_bezier(p1, p2); tf.set_as_cubic_bezier(Point2D::new(p1.x.value, p1.y.value),
Point2D::new(p2.x.value, p2.y.value));
}, },
SpecifiedTimingFunction::Keyword(keyword) => { SpecifiedTimingFunction::Keyword(keyword) => {
match keyword { match keyword {

View file

@ -1283,7 +1283,7 @@ fn static_assert() {
use properties::longhands::font_size_adjust::computed_value::T; use properties::longhands::font_size_adjust::computed_value::T;
match v { match v {
T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32, T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32,
T::Number(n) => self.gecko.mFont.sizeAdjust = n.0 as f32, T::Number(n) => self.gecko.mFont.sizeAdjust = n,
} }
} }
@ -1293,11 +1293,10 @@ fn static_assert() {
pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T { pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T {
use properties::longhands::font_size_adjust::computed_value::T; use properties::longhands::font_size_adjust::computed_value::T;
use values::specified::Number;
match self.gecko.mFont.sizeAdjust { match self.gecko.mFont.sizeAdjust {
-1.0 => T::None, -1.0 => T::None,
_ => T::Number(Number(self.gecko.mFont.sizeAdjust)), _ => T::Number(self.gecko.mFont.sizeAdjust),
} }
} }
@ -1356,8 +1355,8 @@ fn static_assert() {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn ${type}_${ident}_at(&self, index: usize) pub fn ${type}_${ident}_at(&self, index: usize)
-> longhands::${type}_${ident}::computed_value::SingleComputedValue { -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
use values::specified::Time; use values::computed::Time;
Time(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.) Time::from_seconds(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.)
} }
${impl_animation_or_transition_count(type, ident, gecko_ffi_name)} ${impl_animation_or_transition_count(type, ident, gecko_ffi_name)}
${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)} ${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)}
@ -1665,7 +1664,7 @@ fn static_assert() {
"length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)", "length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)",
"percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)", "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)",
"lop" : "%s.set_lop(%s)", "lop" : "%s.set_lop(%s)",
"angle" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.0)", "angle" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.radians())",
"number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)", "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
} }
%> %>
@ -1739,7 +1738,7 @@ fn static_assert() {
css_value_getters = { css_value_getters = {
"length" : "Au(bindings::Gecko_CSSValue_GetAbsoluteLength(%s))", "length" : "Au(bindings::Gecko_CSSValue_GetAbsoluteLength(%s))",
"lop" : "%s.get_lop()", "lop" : "%s.get_lop()",
"angle" : "Angle(bindings::Gecko_CSSValue_GetAngle(%s))", "angle" : "Angle::from_radians(bindings::Gecko_CSSValue_GetAngle(%s))",
"number" : "bindings::Gecko_CSSValue_GetNumber(%s)", "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
} }
%> %>

View file

@ -19,7 +19,9 @@
#[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} } #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} }
% endif % endif
#[allow(unused_variables)] #[allow(unused_variables)]
#[inline] pub fn parse(context: &ParserContext, input: &mut Parser) #[inline]
pub fn parse(context: &ParserContext,
input: &mut Parser)
-> Result<SpecifiedValue, ()> { -> Result<SpecifiedValue, ()> {
% if needs_context: % if needs_context:
specified::${type}::${parse_method}(context, input) specified::${type}::${parse_method}(context, input)

View file

@ -36,7 +36,6 @@ use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
use values::computed::{MaxLength, MinLength}; use values::computed::{MaxLength, MinLength};
use values::computed::position::{HorizontalPosition, Position, VerticalPosition}; use values::computed::position::{HorizontalPosition, Position, VerticalPosition};
use values::computed::ToComputedValue; use values::computed::ToComputedValue;
use values::specified::Angle as SpecifiedAngle;
@ -455,7 +454,7 @@ impl Interpolate for i32 {
impl Interpolate for Angle { impl Interpolate for Angle {
#[inline] #[inline]
fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> { fn interpolate(&self, other: &Angle, progress: f64) -> Result<Self, ()> {
self.radians().interpolate(&other.radians(), progress).map(Angle) self.radians().interpolate(&other.radians(), progress).map(Angle::from_radians)
} }
} }
@ -952,7 +951,7 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
result.push(TransformOperation::Matrix(identity)); result.push(TransformOperation::Matrix(identity));
} }
TransformOperation::Skew(..) => { TransformOperation::Skew(..) => {
result.push(TransformOperation::Skew(Angle(0.0), Angle(0.0))); result.push(TransformOperation::Skew(Angle::zero(), Angle::zero()))
} }
TransformOperation::Translate(..) => { TransformOperation::Translate(..) => {
result.push(TransformOperation::Translate(LengthOrPercentage::zero(), result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
@ -963,7 +962,7 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
result.push(TransformOperation::Scale(1.0, 1.0, 1.0)); result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
} }
TransformOperation::Rotate(..) => { TransformOperation::Rotate(..) => {
result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0))); result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::zero()));
} }
TransformOperation::Perspective(..) => { TransformOperation::Perspective(..) => {
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
@ -1052,7 +1051,7 @@ fn interpolate_transform_list(from_list: &[TransformOperation],
} }
/// https://drafts.csswg.org/css-transforms/#Rotate3dDefined /// https://drafts.csswg.org/css-transforms/#Rotate3dDefined
fn rotate_to_matrix(x: f32, y: f32, z: f32, a: SpecifiedAngle) -> ComputedMatrix { fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix {
let half_rad = a.radians() / 2.0; let half_rad = a.radians() / 2.0;
let sc = (half_rad).sin() * (half_rad).cos(); let sc = (half_rad).sin() * (half_rad).cos();
let sq = (half_rad).sin().powi(2); let sq = (half_rad).sin().powi(2);

View file

@ -223,7 +223,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
#[inline] #[inline]
pub fn get_initial_specified_value() -> SpecifiedValue { pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(vec![Either::Second(Number(0.0))]) SpecifiedValue(vec![Either::Second(Number::new(0.0))])
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -486,7 +486,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
#[inline] #[inline]
pub fn get_initial_specified_value() -> SpecifiedValue { pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(vec![SingleSpecifiedValue::Number(Number(1.0))]) SpecifiedValue(vec![SingleSpecifiedValue::Number(Number::new(1.0))])
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {

View file

@ -419,13 +419,13 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
} }
#[inline] #[inline]
pub fn get_initial_value() -> Time { pub fn get_initial_value() -> computed_value::T {
Time(0.0) computed_value::T::zero()
} }
#[inline] #[inline]
pub fn get_initial_specified_value() -> SpecifiedValue { pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(0.0) Time::zero()
} }
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
@ -440,7 +440,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
extra_prefixes="moz webkit" extra_prefixes="moz webkit"
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function"> spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function">
use self::computed_value::StartEnd; use self::computed_value::StartEnd;
use values::specified::Number;
use euclid::point::{Point2D, TypedPoint2D}; use euclid::point::{Point2D, TypedPoint2D};
use std::fmt; use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -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"),
@ -547,8 +552,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum SpecifiedValue { pub enum SpecifiedValue {
CubicBezier(Point2D<f32>, Point2D<f32>), CubicBezier(Point2D<Number>, Point2D<Number>),
Steps(u32, StartEnd), Steps(specified::Integer, StartEnd),
Keyword(FunctionKeyword), Keyword(FunctionKeyword),
} }
@ -557,7 +562,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
if let Ok(function_name) = input.try(|input| input.expect_function()) { if let Ok(function_name) = input.try(|input| input.expect_function()) {
return match_ignore_ascii_case! { &function_name, return match_ignore_ascii_case! { &function_name,
"cubic-bezier" => { "cubic-bezier" => {
let (mut p1x, mut p1y, mut p2x, mut p2y) = (0.0, 0.0, 0.0, 0.0); let (mut p1x, mut p1y, mut p2x, mut p2y) =
(Number::new(0.0), Number::new(0.0), Number::new(0.0), Number::new(0.0));
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
p1x = try!(specified::parse_number(input)); p1x = try!(specified::parse_number(input));
try!(input.expect_comma()); try!(input.expect_comma());
@ -568,7 +574,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
p2y = try!(specified::parse_number(input)); p2y = try!(specified::parse_number(input));
Ok(()) Ok(())
})); }));
if p1x < 0.0 || p1x > 1.0 || p2x < 0.0 || p2x > 1.0 { if p1x.value < 0.0 || p1x.value > 1.0 ||
p2x.value < 0.0 || p2x.value > 1.0 {
return Err(()) return Err(())
} }
@ -576,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(())
} }
@ -593,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(())
} }
@ -602,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 {
@ -633,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)
@ -651,13 +661,15 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
type ComputedValue = computed_value::T; type ComputedValue = computed_value::T;
#[inline] #[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::CubicBezier(p1, p2) => { SpecifiedValue::CubicBezier(p1, p2) => {
computed_value::T::CubicBezier(p1, p2) computed_value::T::CubicBezier(
Point2D::new(p1.x.to_computed_value(context), p1.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 {
@ -676,10 +688,15 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
fn from_computed_value(computed: &computed_value::T) -> Self { fn from_computed_value(computed: &computed_value::T) -> Self {
match *computed { match *computed {
computed_value::T::CubicBezier(p1, p2) => { computed_value::T::CubicBezier(p1, p2) => {
SpecifiedValue::CubicBezier(p1, p2) SpecifiedValue::CubicBezier(
Point2D::new(Number::from_computed_value(&p1.x),
Number::from_computed_value(&p1.y)),
Point2D::new(Number::from_computed_value(&p2.x),
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)
}, },
} }
} }
@ -1083,6 +1100,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
fixpos_cb="True" fixpos_cb="True"
spec="https://drafts.csswg.org/css-transforms/#propdef-transform"> spec="https://drafts.csswg.org/css-transforms/#propdef-transform">
use app_units::Au; use app_units::Au;
use values::specified::Number;
use style_traits::ToCss; use style_traits::ToCss;
use values::CSSFloat; use values::CSSFloat;
use values::HasViewportPercentage; use values::HasViewportPercentage;
@ -1131,7 +1149,60 @@ ${helpers.predefined_type("scroll-snap-coordinate",
pub struct T(pub Option<Vec<ComputedOperation>>); pub struct T(pub Option<Vec<ComputedOperation>>);
} }
pub use self::computed_value::ComputedMatrix as SpecifiedMatrix; #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct SpecifiedMatrix {
pub m11: Number, pub m12: Number, pub m13: Number, pub m14: Number,
pub m21: Number, pub m22: Number, pub m23: Number, pub m24: Number,
pub m31: Number, pub m32: Number, pub m33: Number, pub m34: Number,
pub m41: Number, pub m42: Number, pub m43: Number, pub m44: Number,
}
impl ToComputedValue for SpecifiedMatrix {
type ComputedValue = computed_value::ComputedMatrix;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
computed_value::ComputedMatrix {
m11: self.m11.to_computed_value(context),
m12: self.m12.to_computed_value(context),
m13: self.m13.to_computed_value(context),
m14: self.m14.to_computed_value(context),
m21: self.m21.to_computed_value(context),
m22: self.m22.to_computed_value(context),
m23: self.m23.to_computed_value(context),
m24: self.m24.to_computed_value(context),
m31: self.m31.to_computed_value(context),
m32: self.m32.to_computed_value(context),
m33: self.m33.to_computed_value(context),
m34: self.m34.to_computed_value(context),
m41: self.m41.to_computed_value(context),
m42: self.m42.to_computed_value(context),
m43: self.m43.to_computed_value(context),
m44: self.m44.to_computed_value(context),
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
SpecifiedMatrix {
m11: Number::from_computed_value(&computed.m11),
m12: Number::from_computed_value(&computed.m12),
m13: Number::from_computed_value(&computed.m13),
m14: Number::from_computed_value(&computed.m14),
m21: Number::from_computed_value(&computed.m21),
m22: Number::from_computed_value(&computed.m22),
m23: Number::from_computed_value(&computed.m23),
m24: Number::from_computed_value(&computed.m24),
m31: Number::from_computed_value(&computed.m31),
m32: Number::from_computed_value(&computed.m32),
m33: Number::from_computed_value(&computed.m33),
m34: Number::from_computed_value(&computed.m34),
m41: Number::from_computed_value(&computed.m41),
m42: Number::from_computed_value(&computed.m42),
m43: Number::from_computed_value(&computed.m43),
m44: Number::from_computed_value(&computed.m44),
}
}
}
fn parse_two_lengths_or_percentages(context: &ParserContext, input: &mut Parser) fn parse_two_lengths_or_percentages(context: &ParserContext, input: &mut Parser)
-> Result<(specified::LengthOrPercentage, -> Result<(specified::LengthOrPercentage,
@ -1144,7 +1215,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
Ok((first, second)) Ok((first, second))
} }
fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> { fn parse_two_numbers(input: &mut Parser) -> Result<(Number, Number), ()> {
let first = try!(specified::parse_number(input)); let first = try!(specified::parse_number(input));
let second = input.try(|input| { let second = input.try(|input| {
try!(input.expect_comma()); try!(input.expect_comma());
@ -1159,7 +1230,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
let second = input.try(|input| { let second = input.try(|input| {
try!(input.expect_comma()); try!(input.expect_comma());
specified::Angle::parse(context, input) specified::Angle::parse(context, input)
}).unwrap_or(specified::Angle(0.0)); }).unwrap_or(specified::Angle::zero());
Ok((first, second)) Ok((first, second))
} }
@ -1182,8 +1253,8 @@ ${helpers.predefined_type("scroll-snap-coordinate",
specified::LengthOrPercentage, specified::LengthOrPercentage,
specified::LengthOrPercentage, specified::LengthOrPercentage,
specified::Length), specified::Length),
Scale(CSSFloat, CSSFloat, CSSFloat), Scale(Number, Number, Number),
Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle), Rotate(Number, Number, Number, specified::Angle),
Perspective(specified::Length), Perspective(specified::Length),
} }
@ -1323,21 +1394,19 @@ ${helpers.predefined_type("scroll-snap-coordinate",
if values.len() != 6 { if values.len() != 6 {
return Err(()) return Err(())
} }
result.push(SpecifiedOperation::Matrix( let matrix = SpecifiedMatrix {
SpecifiedMatrix { m11: values[0], m12: values[1], m13: Number::new(0.0), m14: Number::new(0.0),
m11: values[0], m12: values[1], m13: 0.0, m14: 0.0, m21: values[2], m22: values[3], m23: Number::new(0.0), m24: Number::new(0.0),
m21: values[2], m22: values[3], m23: 0.0, m24: 0.0, m31: Number::new(0.0), m32: Number::new(0.0), m33: Number::new(1.0), m34: Number::new(0.0),
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0, m41: values[4], m42: values[5], m43: Number::new(0.0), m44: Number::new(1.0),
m41: values[4], m42: values[5], m43: 0.0, m44: 1.0 };
})); result.push(SpecifiedOperation::Matrix(matrix));
Ok(()) Ok(())
})) }))
}, },
"matrix3d" => { "matrix3d" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| { let values = try!(input.parse_comma_separated(specified::parse_number));
specified::parse_number(input)
}));
if values.len() != 16 { if values.len() != 16 {
return Err(()) return Err(())
} }
@ -1412,29 +1481,37 @@ ${helpers.predefined_type("scroll-snap-coordinate",
}, },
"scale" => { "scale" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let (sx, sy) = try!(parse_two_floats(input)); let (sx, sy) = try!(parse_two_numbers(input));
result.push(SpecifiedOperation::Scale(sx, sy, 1.0)); result.push(SpecifiedOperation::Scale(sx,
sy,
Number::new(1.0)));
Ok(()) Ok(())
})) }))
}, },
"scalex" => { "scalex" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let sx = try!(specified::parse_number(input)); let sx = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0)); result.push(SpecifiedOperation::Scale(sx,
Number::new(1.0),
Number::new(1.0)));
Ok(()) Ok(())
})) }))
}, },
"scaley" => { "scaley" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let sy = try!(specified::parse_number(input)); let sy = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(1.0, sy, 1.0)); result.push(SpecifiedOperation::Scale(Number::new(1.0),
sy,
Number::new(1.0)));
Ok(()) Ok(())
})) }))
}, },
"scalez" => { "scalez" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let sz = try!(specified::parse_number(input)); let sz = try!(specified::parse_number(input));
result.push(SpecifiedOperation::Scale(1.0, 1.0, sz)); result.push(SpecifiedOperation::Scale(Number::new(1.0),
Number::new(1.0),
sz));
Ok(()) Ok(())
})) }))
}, },
@ -1452,28 +1529,40 @@ ${helpers.predefined_type("scroll-snap-coordinate",
"rotate" => { "rotate" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(context,input)); let theta = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); result.push(SpecifiedOperation::Rotate(Number::new(0.0),
Number::new(0.0),
Number::new(1.0),
theta));
Ok(()) Ok(())
})) }))
}, },
"rotatex" => { "rotatex" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(context,input)); let theta = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta)); result.push(SpecifiedOperation::Rotate(Number::new(1.0),
Number::new(0.0),
Number::new(0.0),
theta));
Ok(()) Ok(())
})) }))
}, },
"rotatey" => { "rotatey" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(context,input)); let theta = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta)); result.push(SpecifiedOperation::Rotate(Number::new(0.0),
Number::new(1.0),
Number::new(0.0),
theta));
Ok(()) Ok(())
})) }))
}, },
"rotatez" => { "rotatez" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(context,input)); let theta = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); result.push(SpecifiedOperation::Rotate(Number::new(0.0),
Number::new(0.0),
Number::new(1.0),
theta));
Ok(()) Ok(())
})) }))
}, },
@ -1501,14 +1590,14 @@ ${helpers.predefined_type("scroll-snap-coordinate",
"skewx" => { "skewx" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta_x = try!(specified::Angle::parse(context,input)); let theta_x = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0))); result.push(SpecifiedOperation::Skew(theta_x, specified::Angle::zero()));
Ok(()) Ok(())
})) }))
}, },
"skewy" => { "skewy" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta_y = try!(specified::Angle::parse(context,input)); let theta_y = try!(specified::Angle::parse(context,input));
result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y)); result.push(SpecifiedOperation::Skew(specified::Angle::zero(), theta_y));
Ok(()) Ok(())
})) }))
}, },
@ -1543,7 +1632,7 @@ ${helpers.predefined_type("scroll-snap-coordinate",
for operation in &self.0 { for operation in &self.0 {
match *operation { match *operation {
SpecifiedOperation::Matrix(ref matrix) => { SpecifiedOperation::Matrix(ref matrix) => {
result.push(computed_value::ComputedOperation::Matrix(*matrix)); result.push(computed_value::ComputedOperation::Matrix(matrix.to_computed_value(context)));
} }
SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => { SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => {
result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context), result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context),
@ -1551,14 +1640,22 @@ ${helpers.predefined_type("scroll-snap-coordinate",
tz.to_computed_value(context))); tz.to_computed_value(context)));
} }
SpecifiedOperation::Scale(sx, sy, sz) => { SpecifiedOperation::Scale(sx, sy, sz) => {
let sx = sx.to_computed_value(context);
let sy = sy.to_computed_value(context);
let sz = sz.to_computed_value(context);
result.push(computed_value::ComputedOperation::Scale(sx, sy, sz)); result.push(computed_value::ComputedOperation::Scale(sx, sy, sz));
} }
SpecifiedOperation::Rotate(ax, ay, az, theta) => { SpecifiedOperation::Rotate(ax, ay, az, theta) => {
let ax = ax.to_computed_value(context);
let ay = ay.to_computed_value(context);
let az = az.to_computed_value(context);
let theta = theta.to_computed_value(context);
let len = (ax * ax + ay * ay + az * az).sqrt(); let len = (ax * ax + ay * ay + az * az).sqrt();
result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta)); result.push(computed_value::ComputedOperation::Rotate(ax / len, ay / len, az / len, theta));
} }
SpecifiedOperation::Skew(theta_x, theta_y) => { SpecifiedOperation::Skew(theta_x, theta_y) => {
result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y)); result.push(computed_value::ComputedOperation::Skew(theta_x.to_computed_value(context),
theta_y.to_computed_value(context)));
} }
SpecifiedOperation::Perspective(ref d) => { SpecifiedOperation::Perspective(ref d) => {
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context))); result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
@ -1572,11 +1669,11 @@ ${helpers.predefined_type("scroll-snap-coordinate",
#[inline] #[inline]
fn from_computed_value(computed: &computed_value::T) -> Self { fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(computed.0.as_ref().map(|computed| { SpecifiedValue(computed.0.as_ref().map(|computed| {
let mut result = vec!(); let mut result = vec![];
for operation in computed { for operation in computed {
match *operation { match *operation {
computed_value::ComputedOperation::Matrix(ref matrix) => { computed_value::ComputedOperation::Matrix(ref matrix) => {
result.push(SpecifiedOperation::Matrix(*matrix)); result.push(SpecifiedOperation::Matrix(SpecifiedMatrix::from_computed_value(matrix)));
} }
computed_value::ComputedOperation::Translate(ref tx, ref ty, ref tz) => { computed_value::ComputedOperation::Translate(ref tx, ref ty, ref tz) => {
// XXXManishearth we lose information here; perhaps we should try to // XXXManishearth we lose information here; perhaps we should try to
@ -1586,14 +1683,23 @@ ${helpers.predefined_type("scroll-snap-coordinate",
ToComputedValue::from_computed_value(ty), ToComputedValue::from_computed_value(ty),
ToComputedValue::from_computed_value(tz))); ToComputedValue::from_computed_value(tz)));
} }
computed_value::ComputedOperation::Scale(sx, sy, sz) => { computed_value::ComputedOperation::Scale(ref sx, ref sy, ref sz) => {
result.push(SpecifiedOperation::Scale(sx, sy, sz)); result.push(SpecifiedOperation::Scale(
Number::from_computed_value(sx),
Number::from_computed_value(sy),
Number::from_computed_value(sz)));
} }
computed_value::ComputedOperation::Rotate(ax, ay, az, theta) => { computed_value::ComputedOperation::Rotate(ref ax, ref ay, ref az, ref theta) => {
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta)); result.push(SpecifiedOperation::Rotate(
Number::from_computed_value(ax),
Number::from_computed_value(ay),
Number::from_computed_value(az),
specified::Angle::from_computed_value(theta)));
} }
computed_value::ComputedOperation::Skew(theta_x, theta_y) => { computed_value::ComputedOperation::Skew(ref theta_x, ref theta_y) => {
result.push(SpecifiedOperation::Skew(theta_x, theta_y)); result.push(SpecifiedOperation::Skew(
specified::Angle::from_computed_value(theta_x),
specified::Angle::from_computed_value(theta_y)))
} }
computed_value::ComputedOperation::Perspective(ref d) => { computed_value::ComputedOperation::Perspective(ref d) => {
result.push(SpecifiedOperation::Perspective( result.push(SpecifiedOperation::Perspective(

View file

@ -246,31 +246,28 @@
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(pub 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)>);
}
#[inline] impl ToCss for T {
pub fn get_initial_value() -> computed_value::T { fn to_css<W>(&self, dest: &mut W) -> fmt::Result
computed_value::T(Vec::new()) where W: fmt::Write,
} {
use cssparser::serialize_identifier;
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
impl ToCss for SpecifiedValue {
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;
@ -280,18 +277,64 @@
} }
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(())
}
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
computed_value::T(self.0.iter().map(|entry| {
(entry.0.clone(), entry.1.to_computed_value(context))
}).collect::<Vec<_>>())
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
SpecifiedValue(computed.0.iter().map(|entry| {
(entry.0.clone(), specified::Integer::from_computed_value(&entry.1))
}).collect::<Vec<_>>())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(Vec::new())
}
no_viewport_percentage!(SpecifiedValue);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
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(()) 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 +350,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 +365,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)

View file

@ -655,30 +655,65 @@ ${helpers.single_keyword("font-variant-caps",
<%helpers:longhand products="gecko" name="font-size-adjust" animatable="True" <%helpers:longhand products="gecko" name="font-size-adjust" animatable="True"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust"> spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage; use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
use values::specified::Number;
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue); no_viewport_percentage!(SpecifiedValue);
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum SpecifiedValue { pub enum SpecifiedValue {
None, None,
Number(Number), Number(specified::Number),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
match *self {
SpecifiedValue::None => dest.write_str("none"),
SpecifiedValue::Number(number) => number.to_css(dest),
}
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
SpecifiedValue::None => computed_value::T::None,
SpecifiedValue::Number(ref n) => computed_value::T::Number(n.to_computed_value(context)),
}
}
fn from_computed_value(computed: &computed_value::T) -> Self {
match *computed {
computed_value::T::None => SpecifiedValue::None,
computed_value::T::Number(ref v) => SpecifiedValue::Number(specified::Number::from_computed_value(v)),
}
}
} }
pub mod computed_value { pub mod computed_value {
use style_traits::ToCss;
use std::fmt;
use properties::animated_properties::Interpolate; use properties::animated_properties::Interpolate;
use values::specified::Number; use std::fmt;
use style_traits::ToCss;
use values::CSSFloat;
pub use super::SpecifiedValue as T; #[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum T {
None,
Number(CSSFloat),
}
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::None => dest.write_str("none"), T::None => dest.write_str("none"),
T::Number(number) => number.to_css(dest), T::Number(number) => number.to_css(dest),
@ -690,14 +725,15 @@ ${helpers.single_keyword("font-variant-caps",
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> { fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
match (*self, *other) { match (*self, *other) {
(T::Number(ref number), T::Number(ref other)) => (T::Number(ref number), T::Number(ref other)) =>
Ok(T::Number(Number(try!(number.0.interpolate(&other.0, time))))), Ok(T::Number(try!(number.interpolate(other, time)))),
_ => Err(()), _ => Err(()),
} }
} }
} }
} }
#[inline] pub fn get_initial_value() -> computed_value::T { #[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::None computed_value::T::None
} }

View file

@ -96,7 +96,7 @@ ${helpers.single_keyword("image-rendering",
use std::f32::consts::PI; use std::f32::consts::PI;
use values::CSSFloat; use values::CSSFloat;
const TWO_PI: CSSFloat = 2.0*PI; const TWO_PI: CSSFloat = 2.0 * PI;
#[derive(Clone, PartialEq, Copy, Debug)] #[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -125,7 +125,7 @@ ${helpers.single_keyword("image-rendering",
} }
pub mod computed_value { pub mod computed_value {
use values::specified::Angle; use values::computed::Angle;
#[derive(Clone, PartialEq, Copy, Debug)] #[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -135,36 +135,35 @@ ${helpers.single_keyword("image-rendering",
} }
} }
const INITIAL_ANGLE: Angle = Angle(0.0);
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
computed_value::T::AngleWithFlipped(INITIAL_ANGLE, false) computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
} }
// According to CSS Content Module Level 3: // According to CSS Content Module Level 3:
// The computed value of the property is calculated by rounding the specified angle // The computed value of the property is calculated by rounding the specified angle
// to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn. // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
#[inline] #[inline]
fn normalize_angle(angle: &Angle) -> Angle { fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
let radians = angle.radians(); let radians = angle.radians();
let rounded_quarter_turns = (4.0 * radians / TWO_PI).round(); let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0; let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI; let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
Angle::from_radians(normalized_radians) computed::Angle::from_radians(normalized_radians)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T; type ComputedValue = computed_value::T;
#[inline] #[inline]
fn to_computed_value(&self, _: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
if let Some(ref angle) = self.angle { if let Some(ref angle) = self.angle {
let normalized_angle = normalize_angle(angle); let angle = angle.to_computed_value(context);
let normalized_angle = normalize_angle(&angle);
computed_value::T::AngleWithFlipped(normalized_angle, self.flipped) computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
} else { } else {
if self.flipped { if self.flipped {
computed_value::T::AngleWithFlipped(INITIAL_ANGLE, true) computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
} else { } else {
computed_value::T::FromImage computed_value::T::FromImage
} }
@ -175,8 +174,12 @@ ${helpers.single_keyword("image-rendering",
fn from_computed_value(computed: &computed_value::T) -> Self { fn from_computed_value(computed: &computed_value::T) -> Self {
match *computed { match *computed {
computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false }, computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
computed_value::T::AngleWithFlipped(angle, flipped) => computed_value::T::AngleWithFlipped(ref angle, flipped) => {
SpecifiedValue { angle: Some(angle), flipped: flipped }, SpecifiedValue {
angle: Some(Angle::from_computed_value(angle)),
flipped: flipped,
}
}
} }
} }
} }
@ -205,7 +208,7 @@ ${helpers.single_keyword("image-rendering",
let angle = input.try(|input| Angle::parse(context, input)).ok(); let angle = input.try(|input| Angle::parse(context, input)).ok();
let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok(); let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
let explicit_angle = if angle.is_none() && !flipped { let explicit_angle = if angle.is_none() && !flipped {
Some(INITIAL_ANGLE) Some(Angle::zero())
} else { } else {
angle angle
}; };

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" /> <%namespace name="helpers" file="/helpers.mako.rs" />
<% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %> <% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>
@ -52,7 +52,8 @@ ${helpers.single_keyword("caption-side", "top bottom",
impl HasViewportPercentage for SpecifiedValue { impl HasViewportPercentage for SpecifiedValue {
fn has_viewport_percentage(&self) -> bool { fn has_viewport_percentage(&self) -> bool {
return self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage() self.horizontal.has_viewport_percentage() ||
self.vertical.as_ref().map_or(false, |v| v.has_viewport_percentage())
} }
} }
@ -60,7 +61,7 @@ ${helpers.single_keyword("caption-side", "top bottom",
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SpecifiedValue { pub struct SpecifiedValue {
pub horizontal: specified::Length, pub horizontal: specified::Length,
pub vertical: specified::Length, pub vertical: Option<specified::Length>,
} }
#[inline] #[inline]
@ -72,10 +73,15 @@ ${helpers.single_keyword("caption-side", "top bottom",
} }
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,
{
try!(self.horizontal.to_css(dest)); try!(self.horizontal.to_css(dest));
if let Some(vertical) = self.vertical.as_ref() {
try!(dest.write_str(" ")); try!(dest.write_str(" "));
self.vertical.to_css(dest) vertical.to_css(dest)?;
}
Ok(())
} }
} }
@ -92,9 +98,10 @@ ${helpers.single_keyword("caption-side", "top bottom",
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
let horizontal = self.horizontal.to_computed_value(context);
computed_value::T { computed_value::T {
horizontal: self.horizontal.to_computed_value(context), horizontal: horizontal,
vertical: self.vertical.to_computed_value(context), vertical: self.vertical.as_ref().map_or(horizontal, |v| v.to_computed_value(context)),
} }
} }
@ -102,7 +109,7 @@ ${helpers.single_keyword("caption-side", "top bottom",
fn from_computed_value(computed: &computed_value::T) -> Self { fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue { SpecifiedValue {
horizontal: ToComputedValue::from_computed_value(&computed.horizontal), horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
vertical: ToComputedValue::from_computed_value(&computed.vertical), vertical: Some(ToComputedValue::from_computed_value(&computed.vertical)),
} }
} }
} }
@ -123,17 +130,17 @@ ${helpers.single_keyword("caption-side", "top bottom",
(None, None) => Err(()), (None, None) => Err(()),
(Some(length), None) => { (Some(length), None) => {
Ok(SpecifiedValue { Ok(SpecifiedValue {
horizontal: length.clone(), horizontal: length,
vertical: length, vertical: None,
}) })
} }
(Some(horizontal), Some(vertical)) => { (Some(horizontal), Some(vertical)) => {
Ok(SpecifiedValue { Ok(SpecifiedValue {
horizontal: horizontal, horizontal: horizontal,
vertical: vertical, vertical: Some(vertical),
}) })
} }
(None, Some(_)) => panic!("shouldn't happen"), (None, Some(_)) => unreachable!(),
} }
} }
</%helpers:longhand> </%helpers:longhand>

View file

@ -10,7 +10,7 @@
spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height"> spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height">
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use values::{CSSFloat, HasViewportPercentage}; use values::HasViewportPercentage;
impl HasViewportPercentage for SpecifiedValue { impl HasViewportPercentage for SpecifiedValue {
fn has_viewport_percentage(&self) -> bool { fn has_viewport_percentage(&self) -> bool {
@ -28,7 +28,7 @@
% if product == "gecko": % if product == "gecko":
MozBlockHeight, MozBlockHeight,
% endif % endif
Number(CSSFloat), Number(specified::Number),
LengthOrPercentage(specified::LengthOrPercentage), LengthOrPercentage(specified::LengthOrPercentage),
} }
@ -40,7 +40,7 @@
SpecifiedValue::MozBlockHeight => dest.write_str("-moz-block-height"), SpecifiedValue::MozBlockHeight => dest.write_str("-moz-block-height"),
% endif % endif
SpecifiedValue::LengthOrPercentage(ref value) => value.to_css(dest), SpecifiedValue::LengthOrPercentage(ref value) => value.to_css(dest),
SpecifiedValue::Number(number) => write!(dest, "{}", number), SpecifiedValue::Number(number) => number.to_css(dest),
} }
} }
} }
@ -52,7 +52,7 @@
// parsed as a plain Number rather than a Length (0px); this matches the behaviour // parsed as a plain Number rather than a Length (0px); this matches the behaviour
// of all major browsers // of all major browsers
input.try(specified::Number::parse_non_negative) input.try(specified::Number::parse_non_negative)
.map(|n| SpecifiedValue::Number(n.0)) .map(SpecifiedValue::Number)
.or_else(|()| { .or_else(|()| {
input.try(specified::LengthOrPercentage::parse_non_negative) input.try(specified::LengthOrPercentage::parse_non_negative)
.map(SpecifiedValue::LengthOrPercentage) .map(SpecifiedValue::LengthOrPercentage)
@ -116,7 +116,7 @@
% if product == "gecko": % if product == "gecko":
SpecifiedValue::MozBlockHeight => computed_value::T::MozBlockHeight, SpecifiedValue::MozBlockHeight => computed_value::T::MozBlockHeight,
% endif % endif
SpecifiedValue::Number(value) => computed_value::T::Number(value), SpecifiedValue::Number(value) => computed_value::T::Number(value.to_computed_value(context)),
SpecifiedValue::LengthOrPercentage(ref value) => { SpecifiedValue::LengthOrPercentage(ref value) => {
match *value { match *value {
specified::LengthOrPercentage::Length(ref value) => specified::LengthOrPercentage::Length(ref value) =>
@ -144,7 +144,9 @@
% if product == "gecko": % if product == "gecko":
computed_value::T::MozBlockHeight => SpecifiedValue::MozBlockHeight, computed_value::T::MozBlockHeight => SpecifiedValue::MozBlockHeight,
% endif % endif
computed_value::T::Number(value) => SpecifiedValue::Number(value), computed_value::T::Number(ref value) => {
SpecifiedValue::Number(specified::Number::from_computed_value(value))
},
computed_value::T::Length(au) => { computed_value::T::Length(au) => {
SpecifiedValue::LengthOrPercentage(specified::LengthOrPercentage::Length( SpecifiedValue::LengthOrPercentage(specified::LengthOrPercentage::Length(
ToComputedValue::from_computed_value(&au) ToComputedValue::from_computed_value(&au)

View file

@ -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.

View file

@ -70,8 +70,8 @@
if input.try(|input| input.expect_ident_matching("none")).is_ok() { if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(Longhands { return Ok(Longhands {
flex_grow: Number(0.0), flex_grow: Number::new(0.0),
flex_shrink: Number(0.0), flex_shrink: Number::new(0.0),
% if product == "gecko": % if product == "gecko":
flex_basis: LengthOrPercentageOrAuto::Auto flex_basis: LengthOrPercentageOrAuto::Auto
% else: % else:
@ -105,8 +105,8 @@
return Err(()) return Err(())
} }
Ok(Longhands { Ok(Longhands {
flex_grow: grow.unwrap_or(Number(1.0)), flex_grow: grow.unwrap_or(Number::new(1.0)),
flex_shrink: shrink.unwrap_or(Number(1.0)), flex_shrink: shrink.unwrap_or(Number::new(1.0)),
% if product == "gecko": % if product == "gecko":
flex_basis: basis.unwrap_or(LengthOrPercentageOrAuto::Length(NoCalcLength::zero())) flex_basis: basis.unwrap_or(LengthOrPercentageOrAuto::Length(NoCalcLength::zero()))
% else: % else:

View file

@ -537,27 +537,27 @@ impl ToComputedValue for specified::AngleOrCorner {
type ComputedValue = AngleOrCorner; type ComputedValue = AngleOrCorner;
#[inline] #[inline]
fn to_computed_value(&self, _: &Context) -> AngleOrCorner { fn to_computed_value(&self, context: &Context) -> AngleOrCorner {
match *self { match *self {
specified::AngleOrCorner::None => { specified::AngleOrCorner::None => {
AngleOrCorner::Angle(Angle(PI)) AngleOrCorner::Angle(Angle::from_radians(PI))
}, },
specified::AngleOrCorner::Angle(angle) => { specified::AngleOrCorner::Angle(angle) => {
AngleOrCorner::Angle(angle) AngleOrCorner::Angle(angle.to_computed_value(context))
}, },
specified::AngleOrCorner::Corner(horizontal, vertical) => { specified::AngleOrCorner::Corner(horizontal, vertical) => {
match (horizontal, vertical) { match (horizontal, vertical) {
(None, Some(VerticalDirection::Top)) => { (None, Some(VerticalDirection::Top)) => {
AngleOrCorner::Angle(Angle(0.0)) AngleOrCorner::Angle(Angle::from_radians(0.0))
}, },
(Some(HorizontalDirection::Right), None) => { (Some(HorizontalDirection::Right), None) => {
AngleOrCorner::Angle(Angle(PI * 0.5)) AngleOrCorner::Angle(Angle::from_radians(PI * 0.5))
}, },
(None, Some(VerticalDirection::Bottom)) => { (None, Some(VerticalDirection::Bottom)) => {
AngleOrCorner::Angle(Angle(PI)) AngleOrCorner::Angle(Angle::from_radians(PI))
}, },
(Some(HorizontalDirection::Left), None) => { (Some(HorizontalDirection::Left), None) => {
AngleOrCorner::Angle(Angle(PI * 1.5)) AngleOrCorner::Angle(Angle::from_radians(PI * 1.5))
}, },
(Some(horizontal), Some(vertical)) => { (Some(horizontal), Some(vertical)) => {
AngleOrCorner::Corner(horizontal, vertical) AngleOrCorner::Corner(horizontal, vertical)
@ -573,8 +573,8 @@ impl ToComputedValue for specified::AngleOrCorner {
#[inline] #[inline]
fn from_computed_value(computed: &AngleOrCorner) -> Self { fn from_computed_value(computed: &AngleOrCorner) -> Self {
match *computed { match *computed {
AngleOrCorner::Angle(angle) => { AngleOrCorner::Angle(ref angle) => {
specified::AngleOrCorner::Angle(angle) specified::AngleOrCorner::Angle(specified::Angle::from_computed_value(angle))
}, },
AngleOrCorner::Corner(horizontal, vertical) => { AngleOrCorner::Corner(horizontal, vertical) => {
specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical)) specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical))

View file

@ -20,7 +20,7 @@ pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword};
pub use super::{Auto, Either, None_}; pub use super::{Auto, Either, None_};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
pub use super::specified::{Angle, BorderStyle, GridLine, Percentage, Time, UrlOrNone}; pub use super::specified::{BorderStyle, GridLine, Percentage, UrlOrNone};
pub use super::specified::url::SpecifiedUrl; pub use super::specified::url::SpecifiedUrl;
pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto};
pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone};
@ -114,6 +114,76 @@ impl<T> ToComputedValue for T
} }
} }
/// A computed `<angle>` value.
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
pub struct Angle {
radians: CSSFloat,
}
impl Angle {
/// Construct a computed `Angle` value from a radian amount.
pub fn from_radians(radians: CSSFloat) -> Self {
Angle {
radians: radians,
}
}
/// Return the amount of radians this angle represents.
#[inline]
pub fn radians(&self) -> CSSFloat {
self.radians
}
/// Returns an angle that represents a rotation of zero radians.
pub fn zero() -> Self {
Self::from_radians(0.0)
}
}
impl ToCss for Angle {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
write!(dest, "{}rad", self.radians())
}
}
/// A computed `<time>` value.
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
pub struct Time {
seconds: CSSFloat,
}
impl Time {
/// Construct a computed `Time` value from a seconds amount.
pub fn from_seconds(seconds: CSSFloat) -> Self {
Time {
seconds: seconds,
}
}
/// Construct a computed `Time` value that represents zero seconds.
pub fn zero() -> Self {
Self::from_seconds(0.0)
}
/// Return the amount of seconds this time represents.
#[inline]
pub fn seconds(&self) -> CSSFloat {
self.seconds
}
}
impl ToCss for Time {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
write!(dest, "{}s", self.seconds())
}
}
impl ToComputedValue for specified::Color { impl ToComputedValue for specified::Color {
type ComputedValue = RGBA; type ComputedValue = RGBA;

View file

@ -507,8 +507,8 @@ pub struct CalcProductNode {
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum CalcValueNode { pub enum CalcValueNode {
Length(NoCalcLength), Length(NoCalcLength),
Angle(Angle), Angle(CSSFloat),
Time(Time), Time(CSSFloat),
Percentage(CSSFloat), Percentage(CSSFloat),
Number(CSSFloat), Number(CSSFloat),
Sum(Box<CalcSumNode>), Sum(Box<CalcSumNode>),
@ -615,10 +615,14 @@ impl CalcLengthOrPercentage {
NoCalcLength::parse_dimension(value.value, unit).map(CalcValueNode::Length) NoCalcLength::parse_dimension(value.value, unit).map(CalcValueNode::Length)
} }
(Token::Dimension(ref value, ref unit), CalcUnit::Angle) => { (Token::Dimension(ref value, ref unit), CalcUnit::Angle) => {
Angle::parse_dimension(value.value, unit).map(CalcValueNode::Angle) Angle::parse_dimension(value.value, unit).map(|angle| {
CalcValueNode::Angle(angle.radians())
})
} }
(Token::Dimension(ref value, ref unit), CalcUnit::Time) => { (Token::Dimension(ref value, ref unit), CalcUnit::Time) => {
Time::parse_dimension(value.value, unit).map(CalcValueNode::Time) Time::parse_dimension(value.value, unit).map(|time| {
CalcValueNode::Time(time.seconds())
})
} }
(Token::Percentage(ref value), CalcUnit::LengthOrPercentage) => (Token::Percentage(ref value), CalcUnit::LengthOrPercentage) =>
Ok(CalcValueNode::Percentage(value.unit_value)), Ok(CalcValueNode::Percentage(value.unit_value)),
@ -802,14 +806,14 @@ impl CalcLengthOrPercentage {
for value in simplified { for value in simplified {
match value { match value {
SimplifiedValueNode::Time(Time(val)) => SimplifiedValueNode::Time(val) =>
time = Some(time.unwrap_or(0.) + val), time = Some(time.unwrap_or(0.) + val),
_ => return Err(()), _ => return Err(()),
} }
} }
match time { match time {
Some(time) => Ok(Time(time)), Some(time) => Ok(Time::from_calc(time)),
_ => Err(()) _ => Err(())
} }
} }
@ -831,16 +835,23 @@ impl CalcLengthOrPercentage {
for value in simplified { for value in simplified {
match value { match value {
SimplifiedValueNode::Angle(Angle(val)) => SimplifiedValueNode::Angle(val) => {
angle = Some(angle.unwrap_or(0.) + val), angle = Some(angle.unwrap_or(0.) + val)
SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), }
// TODO(emilio): This `Number` logic looks fishy.
//
// In particular, this allows calc(2 - 2) to parse as an
// `Angle`, which doesn't seem desired to me.
SimplifiedValueNode::Number(val) => {
number = Some(number.unwrap_or(0.) + val)
}
_ => unreachable!() _ => unreachable!()
} }
} }
match (angle, number) { match (angle, number) {
(Some(angle), None) => Ok(Angle(angle)), (Some(angle), None) => Ok(Angle::from_calc(angle)),
(None, Some(value)) if value == 0. => Ok(Angle(0.)), (None, Some(value)) if value == 0. => Ok(Angle::from_calc(0.)),
_ => Err(()) _ => Err(())
} }
} }
@ -899,12 +910,24 @@ impl ToCss for CalcLengthOrPercentage {
/// A percentage value. /// A percentage value.
/// ///
/// [0 .. 100%] maps to [0.0 .. 1.0] /// [0 .. 100%] maps to [0.0 .. 1.0]
///
/// FIXME(emilio): There's no standard property that requires a `<percentage>`
/// without requiring also a `<length>`. If such a property existed, we'd need
/// to add special handling for `calc()` and percentages in here in the same way
/// as for `Angle` and `Time`, but the lack of this this is otherwise
/// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
///
/// As of today, only `-moz-image-rect` supports percentages without length.
/// This is not a regression, and that's a non-standard extension anyway, so I'm
/// not implementing it for now.
#[derive(Clone, PartialEq, Copy, Debug)] #[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Percentage(pub CSSFloat); pub struct Percentage(pub CSSFloat);
impl ToCss for Percentage { impl ToCss for Percentage {
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,
{
write!(dest, "{}%", self.0 * 100.) write!(dest, "{}%", self.0 * 100.)
} }
} }

View file

@ -18,7 +18,7 @@ use std::fmt;
use std::ops::Mul; use std::ops::Mul;
use style_traits::ToCss; use style_traits::ToCss;
use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_}; use super::{Auto, CSSFloat, CSSInteger, HasViewportPercentage, Either, None_};
use super::computed::{ComputedValueAsSpecified, Context}; use super::computed::{self, Context};
use super::computed::{Shadow as ComputedShadow, ToComputedValue}; use super::computed::{Shadow as ComputedShadow, ToComputedValue};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -166,8 +166,8 @@ impl<'a> Mul<CSSFloat> for &'a SimplifiedSumNode {
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum SimplifiedValueNode { pub enum SimplifiedValueNode {
Length(NoCalcLength), Length(NoCalcLength),
Angle(Angle), Angle(CSSFloat),
Time(Time), Time(CSSFloat),
Percentage(CSSFloat), Percentage(CSSFloat),
Number(CSSFloat), Number(CSSFloat),
Sum(Box<SimplifiedSumNode>), Sum(Box<SimplifiedSumNode>),
@ -179,25 +179,37 @@ impl<'a> Mul<CSSFloat> for &'a SimplifiedValueNode {
#[inline] #[inline]
fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode { fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode {
match *self { match *self {
SimplifiedValueNode::Length(ref l) => SimplifiedValueNode::Length(l.clone() * scalar), SimplifiedValueNode::Length(ref l) => {
SimplifiedValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p * scalar), SimplifiedValueNode::Length(l.clone() * scalar)
SimplifiedValueNode::Angle(Angle(a)) => SimplifiedValueNode::Angle(Angle(a * scalar)), },
SimplifiedValueNode::Time(Time(t)) => SimplifiedValueNode::Time(Time(t * scalar)), SimplifiedValueNode::Percentage(p) => {
SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar), SimplifiedValueNode::Percentage(p * scalar)
},
SimplifiedValueNode::Angle(a) => {
SimplifiedValueNode::Angle(a * scalar)
},
SimplifiedValueNode::Time(t) => {
SimplifiedValueNode::Time(t * scalar)
},
SimplifiedValueNode::Number(n) => {
SimplifiedValueNode::Number(n * scalar)
},
SimplifiedValueNode::Sum(ref s) => { SimplifiedValueNode::Sum(ref s) => {
let sum = &**s * scalar; let sum = &**s * scalar;
SimplifiedValueNode::Sum(Box::new(sum)) SimplifiedValueNode::Sum(Box::new(sum))
} },
} }
} }
} }
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_integer(input: &mut Parser) -> Result<CSSInteger, ()> { 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;
@ -210,7 +222,7 @@ pub fn parse_integer(input: &mut Parser) -> Result<CSSInteger, ()> {
} }
match result { match result {
Some(result) => Ok(result), Some(result) => Ok(Integer::from_calc(result)),
_ => Err(()) _ => Err(())
} }
} }
@ -219,17 +231,15 @@ pub fn parse_integer(input: &mut Parser) -> Result<CSSInteger, ()> {
} }
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_number(input: &mut Parser) -> Result<f32, ()> { pub fn parse_number(input: &mut Parser) -> Result<Number, ()> {
use std::f32;
match try!(input.next()) { match try!(input.next()) {
Token::Number(ref value) => { Token::Number(ref value) => {
use std::f32; Ok(Number {
if value.value.is_finite() { value: value.value.min(f32::MAX).max(f32::MIN),
Ok(value.value) was_calc: false,
} else if value.value.is_sign_positive() { })
Ok(f32::MAX)
} else {
Ok(f32::MIN)
}
}, },
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::Number))); let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Number)));
@ -245,7 +255,12 @@ pub fn parse_number(input: &mut Parser) -> Result<f32, ()> {
} }
match result { match result {
Some(result) => Ok(result), Some(result) => {
Ok(Number {
value: result.min(f32::MAX).max(f32::MIN),
was_calc: true,
})
},
_ => Err(()) _ => Err(())
} }
} }
@ -299,11 +314,36 @@ impl ToCss for BorderRadiusSize {
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug)] #[derive(Clone, PartialEq, PartialOrd, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
/// An angle, normalized to radians. /// An angle, normalized to radians.
pub struct Angle(pub CSSFloat); pub struct Angle {
radians: CSSFloat,
was_calc: bool,
}
impl ToCss for Angle { impl ToCss for Angle {
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 {
write!(dest, "{}rad", self.0) if self.was_calc {
dest.write_str("calc(")?;
}
write!(dest, "{}rad", self.radians)?;
if self.was_calc {
dest.write_str(")")?;
}
Ok(())
}
}
impl ToComputedValue for Angle {
type ComputedValue = computed::Angle;
fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
computed::Angle::from_radians(self.radians())
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Angle {
radians: computed.radians(),
was_calc: false,
}
} }
} }
@ -311,13 +351,29 @@ impl Angle {
#[inline] #[inline]
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn radians(self) -> f32 { pub fn radians(self) -> f32 {
self.0 self.radians
}
/// Returns an angle value that represents zero radians.
pub fn zero() -> Self {
Self::from_radians(0.0)
} }
#[inline] #[inline]
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn from_radians(r: f32) -> Self { pub fn from_radians(r: f32) -> Self {
Angle(r) Angle {
radians: r,
was_calc: false,
}
}
/// Returns an `Angle` parsed from a `calc()` expression.
pub fn from_calc(radians: CSSFloat) -> Self {
Angle {
radians: radians,
was_calc: true,
}
} }
} }
@ -330,7 +386,7 @@ impl Parse for Angle {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
match try!(input.next()) { match try!(input.next()) {
Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit), Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit),
Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)), Token::Number(ref value) if value.value == 0. => Ok(Angle::zero()),
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_angle) input.parse_nested_block(CalcLengthOrPercentage::parse_angle)
}, },
@ -342,13 +398,18 @@ impl Parse for Angle {
impl Angle { impl Angle {
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> { pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> {
match_ignore_ascii_case! { unit, let radians = match_ignore_ascii_case! { unit,
"deg" => Ok(Angle(value * RAD_PER_DEG)), "deg" => value * RAD_PER_DEG,
"grad" => Ok(Angle(value * RAD_PER_GRAD)), "grad" => value * RAD_PER_GRAD,
"turn" => Ok(Angle(value * RAD_PER_TURN)), "turn" => value * RAD_PER_TURN,
"rad" => Ok(Angle(value)), "rad" => value,
_ => Err(()) _ => return Err(())
} };
Ok(Angle {
radians: radians,
was_calc: false,
})
} }
} }
@ -479,28 +540,67 @@ impl BorderStyle {
/// A time in seconds according to CSS-VALUES § 6.2. /// A time in seconds according to CSS-VALUES § 6.2.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Time(pub CSSFloat); pub struct Time {
seconds: CSSFloat,
was_calc: bool,
}
impl Time { impl Time {
/// Return a `<time>` value that represents `seconds` seconds.
pub fn from_seconds(seconds: CSSFloat) -> Self {
Time {
seconds: seconds,
was_calc: false,
}
}
/// Returns a time that represents a duration of zero.
pub fn zero() -> Self {
Self::from_seconds(0.0)
}
/// Returns the time in fractional seconds. /// Returns the time in fractional seconds.
pub fn seconds(self) -> f32 { pub fn seconds(self) -> CSSFloat {
let Time(seconds) = self; self.seconds
seconds
} }
/// Parses a time according to CSS-VALUES § 6.2. /// Parses a time according to CSS-VALUES § 6.2.
fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> { fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {
if unit.eq_ignore_ascii_case("s") { let seconds = match_ignore_ascii_case! { unit,
Ok(Time(value)) "s" => value,
} else if unit.eq_ignore_ascii_case("ms") { "ms" => value / 1000.0,
Ok(Time(value / 1000.0)) _ => return Err(()),
} else { };
Err(())
Ok(Time {
seconds: seconds,
was_calc: false,
})
}
/// Returns a `Time` value from a CSS `calc()` expression.
pub fn from_calc(seconds: CSSFloat) -> Self {
Time {
seconds: seconds,
was_calc: true,
} }
} }
} }
impl ComputedValueAsSpecified for Time {} impl ToComputedValue for Time {
type ComputedValue = computed::Time;
fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
computed::Time::from_seconds(self.seconds())
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Time {
seconds: computed.seconds(),
was_calc: false,
}
}
}
impl Parse for Time { impl Parse for Time {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
@ -518,31 +618,53 @@ impl Parse for Time {
impl ToCss for Time { impl ToCss for Time {
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 {
write!(dest, "{}s", self.0) if self.was_calc {
dest.write_str("calc(")?;
}
write!(dest, "{}s", self.seconds)?;
if self.was_calc {
dest.write_str(")")?;
}
Ok(())
} }
} }
#[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 Number(pub CSSFloat); pub struct Number {
/// The numeric value itself.
pub value: CSSFloat,
/// Whether this came from a `calc()` expression. This is needed for
/// serialization purposes, since `calc(1)` should still serialize to
/// `calc(1)`, not just `1`.
was_calc: bool,
}
no_viewport_percentage!(Number); no_viewport_percentage!(Number);
impl Parse for Number { impl Parse for Number {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
parse_number(input).map(Number) parse_number(input)
} }
} }
impl Number { impl Number {
fn parse_with_minimum(input: &mut Parser, min: CSSFloat) -> Result<Number, ()> { fn parse_with_minimum(input: &mut Parser, min: CSSFloat) -> Result<Number, ()> {
match parse_number(input) { match parse_number(input) {
Ok(value) if value >= min => Ok(Number(value)), Ok(value) if value.value >= min => Ok(value),
_ => Err(()), _ => Err(()),
} }
} }
/// Returns a new number with the value `val`.
pub fn new(val: CSSFloat) -> Self {
Number {
value: val,
was_calc: false,
}
}
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn parse_non_negative(input: &mut Parser) -> Result<Number, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<Number, ()> {
Number::parse_with_minimum(input, 0.0) Number::parse_with_minimum(input, 0.0)
@ -558,17 +680,29 @@ impl ToComputedValue for Number {
type ComputedValue = CSSFloat; type ComputedValue = CSSFloat;
#[inline] #[inline]
fn to_computed_value(&self, _: &Context) -> CSSFloat { self.0 } fn to_computed_value(&self, _: &Context) -> CSSFloat { self.value }
#[inline] #[inline]
fn from_computed_value(computed: &CSSFloat) -> Self { fn from_computed_value(computed: &CSSFloat) -> Self {
Number(*computed) Number {
value: *computed,
was_calc: false,
}
} }
} }
impl ToCss for Number { impl ToCss for Number {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result
self.0.to_css(dest) where W: fmt::Write,
{
if self.was_calc {
dest.write_str("calc(")?;
}
self.value.to_css(dest)?;
if self.was_calc {
dest.write_str(")")?;
}
Ok(())
} }
} }
@ -606,7 +740,7 @@ impl ToCss for NumberOrPercentage {
#[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 Opacity(pub CSSFloat); pub struct Opacity(Number);
no_viewport_percentage!(Opacity); no_viewport_percentage!(Opacity);
@ -620,19 +754,13 @@ impl ToComputedValue for Opacity {
type ComputedValue = CSSFloat; type ComputedValue = CSSFloat;
#[inline] #[inline]
fn to_computed_value(&self, _: &Context) -> CSSFloat { fn to_computed_value(&self, context: &Context) -> CSSFloat {
if self.0 < 0.0 { self.0.to_computed_value(context).min(1.0).max(0.0)
0.0
} else if self.0 > 1.0 {
1.0
} else {
self.0
}
} }
#[inline] #[inline]
fn from_computed_value(computed: &CSSFloat) -> Self { fn from_computed_value(computed: &CSSFloat) -> Self {
Opacity(*computed) Opacity(Number::from_computed_value(computed))
} }
} }
@ -645,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(()),
} }
} }
@ -678,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(())
} }
} }
@ -697,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,
} }
} }

View file

@ -1202,7 +1202,7 @@ pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(declarations:
BorderSpacing => Box::new( BorderSpacing => Box::new(
BorderSpacing { BorderSpacing {
horizontal: nocalc.into(), horizontal: nocalc.into(),
vertical: nocalc.into(), vertical: None,
} }
), ),
}; };

View file

@ -55,7 +55,7 @@ fn test_linear_gradient() {
font_metrics_provider: None, font_metrics_provider: None,
}; };
assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context), assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context),
computed::AngleOrCorner::Angle(Angle(PI))); computed::AngleOrCorner::Angle(Angle::from_radians(PI)));
} }
#[test] #[test]

View file

@ -573,15 +573,14 @@ mod shorthand_serialization {
#[test] #[test]
fn flex_should_serialize_all_available_properties() { fn flex_should_serialize_all_available_properties() {
use style::values::specified::Number as NumberContainer; use style::values::specified::{Number, Percentage};
use style::values::specified::Percentage as PercentageContainer;
let mut properties = Vec::new(); let mut properties = Vec::new();
let grow = NumberContainer(2f32); let grow = Number::new(2f32);
let shrink = NumberContainer(3f32); let shrink = Number::new(3f32);
let basis = let basis =
LengthOrPercentageOrAutoOrContent::Percentage(PercentageContainer(0.5f32)); LengthOrPercentageOrAutoOrContent::Percentage(Percentage(0.5f32));
properties.push(PropertyDeclaration::FlexGrow(grow)); properties.push(PropertyDeclaration::FlexGrow(grow));
properties.push(PropertyDeclaration::FlexShrink(shrink)); properties.push(PropertyDeclaration::FlexShrink(shrink));
@ -1174,13 +1173,14 @@ mod shorthand_serialization {
mod counter_increment { mod counter_increment {
pub use super::*; pub use super::*;
pub use style::properties::longhands::counter_increment::SpecifiedValue as CounterIncrement; pub use style::properties::longhands::counter_increment::SpecifiedValue as CounterIncrement;
use style::values::specified::Integer;
#[test] #[test]
fn counter_increment_with_properties_should_serialize_correctly() { fn counter_increment_with_properties_should_serialize_correctly() {
let mut properties = Vec::new(); let mut properties = Vec::new();
properties.push(("counter1".to_owned(), 1)); properties.push(("counter1".to_owned(), Integer::new(1)));
properties.push(("counter2".to_owned(), -4)); properties.push(("counter2".to_owned(), Integer::new(-4)));
let counter_increment = CounterIncrement(properties); let counter_increment = CounterIncrement(properties);
let counter_increment_css = "counter1 1 counter2 -4"; let counter_increment_css = "counter1 1 counter2 -4";

View file

@ -1,13 +1,5 @@
[serialize-values.html] [serialize-values.html]
type: testharness type: testharness
[border-spacing: 0px]
expected: FAIL
[border-spacing: 1px]
expected: FAIL
[border-spacing: .1em]
expected: FAIL
[content: url("http://localhost/")] [content: url("http://localhost/")]
expected: FAIL expected: FAIL

View file

@ -11057,6 +11057,12 @@
{} {}
] ]
], ],
"css/calc-number-serialization.html": [
[
"/_mozilla/css/calc-number-serialization.html",
{}
]
],
"css/empty-keyframes.html": [ "css/empty-keyframes.html": [
[ [
"/_mozilla/css/empty-keyframes.html", "/_mozilla/css/empty-keyframes.html",
@ -20700,6 +20706,10 @@
"94dc4aac4c546a74add074be0d3e604c8b196888", "94dc4aac4c546a74add074be0d3e604c8b196888",
"reftest" "reftest"
], ],
"css/calc-number-serialization.html": [
"b9e77c824d0c96e51d51fb8f1e923d3ab67be027",
"testharness"
],
"css/canvas_as_block_element_a.html": [ "css/canvas_as_block_element_a.html": [
"668d93da2dab9722998cc7c5785c20e2ab9a1ced", "668d93da2dab9722998cc7c5785c20e2ab9a1ced",
"reftest" "reftest"
@ -25113,7 +25123,7 @@
"testharness" "testharness"
], ],
"mozilla/calc.html": [ "mozilla/calc.html": [
"c333be7c567aebd1ed2f763d0ed644165ae8bd3b", "c239c441c90e4d4ed94458eae5b2defabb47e984",
"testharness" "testharness"
], ],
"mozilla/canvas.initial.reset.2dstate.html": [ "mozilla/canvas.initial.reset.2dstate.html": [

View file

@ -1,14 +1,3 @@
[calc.html] [calc.html]
type: testharness type: testharness
[calc for border-width] prefs: [layout.column-count.enabled:true,layout.column-width.enabled:true,layout.column-gap.enabled:true]
expected: FAIL
[calc for column-width]
expected: FAIL
[calc for column-gap]
expected: FAIL
[calc for column-count]
expected: FAIL

View file

@ -0,0 +1,18 @@
<!doctype html>
<meta charset="utf-8">
<title>CSS test: Calc expressions with numbers should still serialize as calc()</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="test"></div>
<script>
var div = document.querySelector('#test');
test(function() {
div.style.lineHeight = "calc(1)";
assert_equals(div.style.lineHeight, "calc(1)");
}, "calc of an integer expression should be preserved");
test(function() {
div.style.lineHeight = "calc(1 + 3)";
assert_equals(div.style.lineHeight, "calc(4)");
}, "expressions on calc() should be simplified");
</script>

View file

@ -6,6 +6,10 @@
#outer { #outer {
width: 1000px; width: 1000px;
} }
#inner {
border-style: solid; /* So used border == computed border */
outline-style: solid; /* So used outline-width == computed outline-width */
}
</style> </style>
</head> </head>
<body> <body>
@ -76,7 +80,9 @@ var lengthProperties = [
lengthProperties.forEach(function(prop) { lengthProperties.forEach(function(prop) {
test(function() { test(function() {
div.style.setProperty(prop, 'calc(1px)'); div.style.setProperty(prop, 'calc(1px)');
assert_equals(div.style.getPropertyValue(prop), 'calc(1px)'); assert_equals(div.style.getPropertyValue(prop), 'calc(1px)', div.style.getPropertyValue(prop));
let computed = getComputedStyle(div).getPropertyValue(prop);
assert_equals(computed, '1px', prop + ': expected 1px, got ' + computed);
}, 'calc for ' + prop); }, 'calc for ' + prop);
}); });
@ -112,12 +118,14 @@ var timeProperties = [
timeProperties.forEach(function(prop) { timeProperties.forEach(function(prop) {
test(function() { test(function() {
div.style.setProperty(prop, 'calc(1s)'); div.style.setProperty(prop, 'calc(1s)');
assert_equals(div.style.getPropertyValue(prop), '1s'); assert_equals(div.style.getPropertyValue(prop), 'calc(1s)');
assert_equals(getComputedStyle(div).getPropertyValue(prop), '1s');
}, 'calc for ' + prop); }, 'calc for ' + prop);
}); });
var numberProperties = [ var numberProperties = [
'z-index', 'z-index',
'order',
'column-count', 'column-count',
'opacity', 'opacity',
]; ];
@ -125,13 +133,14 @@ var numberProperties = [
numberProperties.forEach(function(prop) { numberProperties.forEach(function(prop) {
test(function() { test(function() {
div.style.setProperty(prop, 'calc(1)'); div.style.setProperty(prop, 'calc(1)');
assert_equals(div.style.getPropertyValue(prop), '1'); assert_equals(div.style.getPropertyValue(prop), 'calc(1)');
assert_equals(getComputedStyle(div).getPropertyValue(prop), '1');
}, 'calc for ' + prop); }, 'calc for ' + prop);
}); });
var otherProperties = [ var otherProperties = [
['border-width', 'calc(1px)', '1px 1px 1px 1px'], ['border-width', 'calc(1px)', 'calc(1px)'],
['border-spacing', 'calc(1px)', 'calc(1px) calc(1px)'], ['border-spacing', 'calc(1px)', 'calc(1px)'],
['transform-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50% 0px'], ['transform-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50% 0px'],
['perspective-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50%'], ['perspective-origin', 'calc(1px + 0%)', 'calc(1px + 0%) 50%'],
['background-size', 'calc(1px + 0%)', 'calc(1px + 0%) auto'], ['background-size', 'calc(1px + 0%)', 'calc(1px + 0%) auto'],
@ -140,6 +149,7 @@ var otherProperties = [
['border-bottom-left-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], ['border-bottom-left-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'],
['border-top-right-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], ['border-top-right-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'],
['border-bottom-right-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'], ['border-bottom-right-radius', 'calc(1px + 0%)', 'calc(1px + 0%) calc(1px + 0%)'],
['counter-increment', 'foo calc(1 + 1)', 'foo calc(2)'],
]; ];
otherProperties.forEach(function(testcase) { otherProperties.forEach(function(testcase) {
@ -150,7 +160,7 @@ otherProperties.forEach(function(testcase) {
}); });
/* TODO: test these: /* TODO: test these:
counter-increment, counter-reset, counter-reset,
color, box-shadow, clip, text-shadow, transform color, box-shadow, clip, text-shadow, transform
transition-timing-function transition-timing-function
angles angles