Auto merge of #7496 - servo:calc_, r=SimonSapin

Implement CSS3 Calc

This is #7185 with one commit added to make it build merged with master, which got support for the `ch` unit in the meantime.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/7496)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2015-09-02 02:33:18 -06:00
commit a547ae6826
21 changed files with 873 additions and 63 deletions

View file

@ -4,6 +4,7 @@
#![feature(arc_unique)]
#![feature(box_syntax)]
#![feature(box_patterns)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
#![feature(custom_derive)]

View file

@ -760,7 +760,7 @@ pub mod longhands {
pub mod computed_value {
use std::fmt;
use util::geometry::Au;
use values::CSSFloat;
use values::{CSSFloat, computed};
#[allow(non_camel_case_types)]
#[derive(PartialEq, Copy, Clone, HeapSizeOf)]
pub enum T {
@ -769,6 +769,7 @@ pub mod longhands {
% endfor
Length(Au),
Percentage(CSSFloat),
Calc(computed::Calc),
}
impl fmt::Debug for T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -778,6 +779,7 @@ pub mod longhands {
% endfor
&T::Length(length) => write!(f, "{:?}", length),
&T::Percentage(number) => write!(f, "{}%", number),
&T::Calc(calc) => write!(f, "{:?}", calc)
}
}
}
@ -789,6 +791,7 @@ pub mod longhands {
% endfor
T::Length(value) => value.to_css(dest),
T::Percentage(percentage) => write!(dest, "{}%", percentage * 100.),
T::Calc(calc) => calc.to_css(dest),
}
}
}
@ -809,12 +812,12 @@ pub mod longhands {
% endfor
SpecifiedValue::LengthOrPercentage(value) => {
match value.to_computed_value(context) {
computed::LengthOrPercentage::Length(value) => {
computed_value::T::Length(value)
}
computed::LengthOrPercentage::Percentage(value) => {
computed_value::T::Percentage(value)
}
computed::LengthOrPercentage::Length(value) =>
computed_value::T::Length(value),
computed::LengthOrPercentage::Percentage(value) =>
computed_value::T::Percentage(value),
computed::LengthOrPercentage::Calc(value) =>
computed_value::T::Calc(value),
}
}
}
@ -1910,10 +1913,12 @@ pub mod longhands {
/// <length> | <percentage> | <absolute-size> | <relative-size>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
input.try(specified::LengthOrPercentage::parse_non_negative)
.map(|value| match value {
specified::LengthOrPercentage::Length(value) => value,
.and_then(|value| match value {
specified::LengthOrPercentage::Length(value) => Ok(value),
specified::LengthOrPercentage::Percentage(value) =>
specified::Length::FontRelative(specified::FontRelativeLength::Em(value))
Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(value.0))),
// FIXME(dzbarsky) handle calc for font-size
specified::LengthOrPercentage::Calc(_) => Err(())
})
.or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
@ -3984,6 +3989,7 @@ pub mod longhands {
}
pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> {
use values::specified::{LengthOrPercentage, Percentage};
let (mut horizontal, mut vertical, mut depth) = (None, None, None);
loop {
if let Err(_) = input.try(|input| {
@ -3992,37 +3998,37 @@ pub mod longhands {
token,
"left" => {
if horizontal.is_none() {
horizontal = Some(specified::LengthOrPercentage::Percentage(0.0))
horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.0)))
} else {
return Err(())
}
},
"center" => {
if horizontal.is_none() {
horizontal = Some(specified::LengthOrPercentage::Percentage(0.5))
horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.5)))
} else if vertical.is_none() {
vertical = Some(specified::LengthOrPercentage::Percentage(0.5))
vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5)))
} else {
return Err(())
}
},
"right" => {
if horizontal.is_none() {
horizontal = Some(specified::LengthOrPercentage::Percentage(1.0))
horizontal = Some(LengthOrPercentage::Percentage(Percentage(1.0)))
} else {
return Err(())
}
},
"top" => {
if vertical.is_none() {
vertical = Some(specified::LengthOrPercentage::Percentage(0.0))
vertical = Some(LengthOrPercentage::Percentage(Percentage(0.0)))
} else {
return Err(())
}
},
"bottom" => {
if vertical.is_none() {
vertical = Some(specified::LengthOrPercentage::Percentage(1.0))
vertical = Some(LengthOrPercentage::Percentage(Percentage(1.0)))
} else {
return Err(())
}
@ -4031,13 +4037,13 @@ pub mod longhands {
}
Ok(())
}) {
match specified::LengthOrPercentage::parse(input) {
match LengthOrPercentage::parse(input) {
Ok(value) => {
if horizontal.is_none() {
horizontal = Some(value);
} else if vertical.is_none() {
vertical = Some(value);
} else if let specified::LengthOrPercentage::Length(length) = value {
} else if let LengthOrPercentage::Length(length) = value {
depth = Some(length);
} else {
break;
@ -4065,7 +4071,7 @@ pub mod longhands {
<%self:longhand name="transform-origin">
use values::computed::Context;
use values::specified::{Length, LengthOrPercentage};
use values::specified::{Length, LengthOrPercentage, Percentage};
use cssparser::ToCss;
use std::fmt;
@ -4121,8 +4127,8 @@ pub mod longhands {
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
let result = try!(super::parse_origin(context, input));
Ok(SpecifiedValue {
horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(0.5)),
vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(0.5)),
horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
depth: result.depth.unwrap_or(Length::Absolute(Au(0))),
})
}
@ -4147,7 +4153,7 @@ pub mod longhands {
<%self:longhand name="perspective-origin">
use values::computed::Context;
use values::specified::LengthOrPercentage;
use values::specified::{LengthOrPercentage, Percentage};
use cssparser::ToCss;
use std::fmt;
@ -4197,8 +4203,8 @@ pub mod longhands {
match result.depth {
Some(_) => Err(()),
None => Ok(SpecifiedValue {
horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(0.5)),
vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(0.5)),
horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
})
}
}

View file

@ -358,19 +358,359 @@ pub mod specified {
}
}
#[derive(Clone, Debug)]
struct CalcSumNode {
products: Vec<CalcProductNode>,
}
#[derive(Clone, Debug)]
struct CalcProductNode {
values: Vec<CalcValueNode>
}
#[derive(Clone, Debug)]
enum CalcValueNode {
Length(Length),
Percentage(CSSFloat),
Number(CSSFloat),
Sum(Box<CalcSumNode>),
}
#[derive(Clone, Debug)]
struct SimplifiedSumNode {
values: Vec<SimplifiedValueNode>,
}
impl<'a> Mul<CSSFloat> for &'a SimplifiedSumNode {
type Output = SimplifiedSumNode;
#[inline]
fn mul(self, scalar: CSSFloat) -> SimplifiedSumNode {
SimplifiedSumNode {
values: self.values.iter().map(|p| p * scalar).collect()
}
}
}
#[derive(Clone, Debug)]
enum SimplifiedValueNode {
Length(Length),
Percentage(CSSFloat),
Number(CSSFloat),
Sum(Box<SimplifiedSumNode>),
}
impl<'a> Mul<CSSFloat> for &'a SimplifiedValueNode {
type Output = SimplifiedValueNode;
#[inline]
fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode {
match self {
&SimplifiedValueNode::Length(l) => SimplifiedValueNode::Length(l * scalar),
&SimplifiedValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p * scalar),
&SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar),
&SimplifiedValueNode::Sum(box ref s) => {
let sum = s * scalar;
SimplifiedValueNode::Sum(box sum)
}
}
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct Calc {
pub absolute: Option<Au>,
pub vw: Option<ViewportPercentageLength>,
pub vh: Option<ViewportPercentageLength>,
pub vmin: Option<ViewportPercentageLength>,
pub vmax: Option<ViewportPercentageLength>,
pub em: Option<FontRelativeLength>,
pub ex: Option<FontRelativeLength>,
pub ch: Option<FontRelativeLength>,
pub rem: Option<FontRelativeLength>,
pub percentage: Option<Percentage>,
}
impl Calc {
fn parse_sum(input: &mut Parser) -> Result<CalcSumNode, ()> {
let mut products = Vec::new();
products.push(try!(Calc::parse_product(input)));
loop {
match input.next() {
Ok(Token::Delim('+')) => {
products.push(try!(Calc::parse_product(input)));
}
Ok(Token::Delim('-')) => {
let mut right = try!(Calc::parse_product(input));
right.values.push(CalcValueNode::Number(-1.));
products.push(right);
}
Ok(_) => return Err(()),
_ => break
}
}
Ok(CalcSumNode { products: products })
}
fn parse_product(input: &mut Parser) -> Result<CalcProductNode, ()> {
let mut values = Vec::new();
values.push(try!(Calc::parse_value(input)));
loop {
let position = input.position();
match input.next() {
Ok(Token::Delim('*')) => {
values.push(try!(Calc::parse_value(input)));
}
Ok(Token::Delim('/')) => {
if let Ok(Token::Number(ref value)) = input.next() {
if value.value == 0. {
return Err(());
}
values.push(CalcValueNode::Number(1. / value.value));
} else {
return Err(());
}
}
_ => {
input.reset(position);
break
}
}
}
Ok(CalcProductNode { values: values })
}
fn parse_value(input: &mut Parser) -> Result<CalcValueNode, ()> {
match input.next() {
Ok(Token::Number(ref value)) => Ok(CalcValueNode::Number(value.value)),
Ok(Token::Dimension(ref value, ref unit)) =>
Length::parse_dimension(value.value, unit).map(CalcValueNode::Length),
Ok(Token::Percentage(ref value)) =>
Ok(CalcValueNode::Percentage(value.unit_value)),
Ok(Token::ParenthesisBlock) => {
let result = try!(input.parse_nested_block(Calc::parse_sum));
Ok(CalcValueNode::Sum(box result))
},
_ => Err(())
}
}
fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> {
match node {
&CalcValueNode::Number(number) => Some(number),
&CalcValueNode::Sum(box ref sum) => Calc::simplify_sum_to_number(sum),
_ => None
}
}
fn simplify_sum_to_number(node: &CalcSumNode) -> Option<CSSFloat> {
let mut sum = 0.;
for ref product in &node.products {
match Calc::simplify_product_to_number(product) {
Some(number) => sum += number,
_ => return None
}
}
Some(sum)
}
fn simplify_product_to_number(node: &CalcProductNode) -> Option<CSSFloat> {
let mut product = 1.;
for ref value in &node.values {
match Calc::simplify_value_to_number(value) {
Some(number) => product *= number,
_ => return None
}
}
Some(product)
}
fn simplify_products_in_sum(node: &CalcSumNode) -> Result<SimplifiedValueNode, ()> {
let mut simplified = Vec::new();
for product in &node.products {
match try!(Calc::simplify_product(product)) {
SimplifiedValueNode::Sum(box sum) => simplified.push_all(&sum.values),
val => simplified.push(val),
}
}
if simplified.len() == 1 {
Ok(simplified[0].clone())
} else {
Ok(SimplifiedValueNode::Sum(box SimplifiedSumNode { values: simplified } ))
}
}
fn simplify_product(node: &CalcProductNode) -> Result<SimplifiedValueNode, ()> {
let mut multiplier = 1.;
let mut node_with_unit = None;
for node in &node.values {
match Calc::simplify_value_to_number(&node) {
Some(number) => multiplier *= number,
_ if node_with_unit.is_none() => {
node_with_unit = Some(match node {
&CalcValueNode::Sum(box ref sum) =>
try!(Calc::simplify_products_in_sum(sum)),
&CalcValueNode::Length(l) => SimplifiedValueNode::Length(l),
&CalcValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p),
_ => unreachable!("Numbers should have been handled by simplify_value_to_nubmer")
})
},
_ => return Err(()),
}
}
match node_with_unit {
None => Ok(SimplifiedValueNode::Number(multiplier)),
Some(ref value) => Ok(value * multiplier)
}
}
pub fn parse(input: &mut Parser) -> Result<Calc, ()> {
let ast = try!(Calc::parse_sum(input));
let mut simplified = Vec::new();
for ref node in ast.products {
match try!(Calc::simplify_product(node)) {
SimplifiedValueNode::Sum(sum) => simplified.push_all(&sum.values),
value => simplified.push(value),
}
}
let mut absolute = None;
let mut vw = None;
let mut vh = None;
let mut vmax = None;
let mut vmin = None;
let mut em = None;
let mut ex = None;
let mut ch = None;
let mut rem = None;
let mut percentage = None;
let mut number = None;
for value in simplified {
match value {
SimplifiedValueNode::Percentage(p) =>
percentage = Some(percentage.unwrap_or(0.) + p),
SimplifiedValueNode::Length(Length::Absolute(Au(au))) =>
absolute = Some(absolute.unwrap_or(0) + au),
SimplifiedValueNode::Length(Length::ViewportPercentage(v)) =>
match v {
ViewportPercentageLength::Vw(val) =>
vw = Some(vw.unwrap_or(0.) + val),
ViewportPercentageLength::Vh(val) =>
vh = Some(vh.unwrap_or(0.) + val),
ViewportPercentageLength::Vmin(val) =>
vmin = Some(vmin.unwrap_or(0.) + val),
ViewportPercentageLength::Vmax(val) =>
vmax = Some(vmax.unwrap_or(0.) + val),
},
SimplifiedValueNode::Length(Length::FontRelative(f)) =>
match f {
FontRelativeLength::Em(val) =>
em = Some(em.unwrap_or(0.) + val),
FontRelativeLength::Ex(val) =>
ex = Some(ex.unwrap_or(0.) + val),
FontRelativeLength::Ch(val) =>
ch = Some(ch.unwrap_or(0.) + val),
FontRelativeLength::Rem(val) =>
rem = Some(rem.unwrap_or(0.) + val),
},
SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val),
_ => unreachable!()
}
}
Ok(Calc {
absolute: absolute.map(Au),
vw: vw.map(ViewportPercentageLength::Vw),
vh: vh.map(ViewportPercentageLength::Vh),
vmax: vmax.map(ViewportPercentageLength::Vmax),
vmin: vmin.map(ViewportPercentageLength::Vmin),
em: em.map(FontRelativeLength::Em),
ex: ex.map(FontRelativeLength::Ex),
ch: ch.map(FontRelativeLength::Ch),
rem: rem.map(FontRelativeLength::Rem),
percentage: percentage.map(Percentage),
})
}
}
impl ToCss for Calc {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
macro_rules! count {
( $( $val:ident ),* ) => {
{
let mut count = 0;
$(
if let Some(_) = self.$val {
count += 1;
}
)*
count
}
};
}
macro_rules! serialize {
( $( $val:ident ),* ) => {
{
let mut first_value = true;
$(
if let Some(val) = self.$val {
if !first_value {
try!(write!(dest, " + "));
} else {
first_value = false;
}
try!(val.to_css(dest));
}
)*
}
};
}
let count = count!(ch, em, ex, absolute, rem, vh, vmax, vmin, vw, percentage);
assert!(count > 0);
if count > 1 {
try!(write!(dest, "calc("));
}
serialize!(ch, em, ex, absolute, rem, vh, vmax, vmin, vw, percentage);
if count > 1 {
try!(write!(dest, ")"));
}
Ok(())
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct Percentage(pub CSSFloat); // [0 .. 100%] maps to [0.0 .. 1.0]
impl ToCss for Percentage {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
write!(dest, "{}%", self.0 * 100.)
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentage {
Length(Length),
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Percentage(Percentage),
Calc(Calc),
}
impl ToCss for LengthOrPercentage {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&LengthOrPercentage::Length(length) => length.to_css(dest),
&LengthOrPercentage::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
&LengthOrPercentage::Percentage(percentage) => percentage.to_css(dest),
&LengthOrPercentage::Calc(calc) => calc.to_css(dest),
}
}
}
@ -386,9 +726,13 @@ pub mod specified {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit).map(LengthOrPercentage::Length),
Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
Ok(LengthOrPercentage::Percentage(value.unit_value)),
Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(Calc::parse));
Ok(LengthOrPercentage::Calc(calc))
},
_ => Err(())
}
}
@ -406,17 +750,18 @@ pub mod specified {
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentageOrAuto {
Length(Length),
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Percentage(Percentage),
Auto,
Calc(Calc),
}
impl ToCss for LengthOrPercentageOrAuto {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&LengthOrPercentageOrAuto::Length(length) => length.to_css(dest),
&LengthOrPercentageOrAuto::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
&LengthOrPercentageOrAuto::Percentage(percentage) => percentage.to_css(dest),
&LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
&LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest),
}
}
}
@ -429,11 +774,15 @@ pub mod specified {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrAuto::Length),
Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value)),
Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentageOrAuto::Length(Length::Absolute(Au(0)))),
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
Ok(LengthOrPercentageOrAuto::Auto),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(Calc::parse));
Ok(LengthOrPercentageOrAuto::Calc(calc))
},
_ => Err(())
}
}
@ -450,7 +799,7 @@ pub mod specified {
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentageOrNone {
Length(Length),
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Percentage(Percentage),
None,
}
@ -458,8 +807,7 @@ pub mod specified {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&LengthOrPercentageOrNone::Length(length) => length.to_css(dest),
&LengthOrPercentageOrNone::Percentage(percentage) =>
write!(dest, "{}%", percentage * 100.),
&LengthOrPercentageOrNone::Percentage(percentage) => percentage.to_css(dest),
&LengthOrPercentageOrNone::None => dest.write_str("none"),
}
}
@ -472,7 +820,7 @@ pub mod specified {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrNone::Length),
Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
Ok(LengthOrPercentageOrNone::Percentage(value.unit_value)),
Ok(LengthOrPercentageOrNone::Percentage(Percentage(value.unit_value))),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))),
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
@ -534,7 +882,7 @@ pub mod specified {
#[derive(Clone, PartialEq, Copy)]
pub enum PositionComponent {
Length(Length),
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Percentage(Percentage),
Center,
Left,
Right,
@ -549,7 +897,7 @@ pub mod specified {
.map(PositionComponent::Length)
}
Token::Percentage(ref value) => {
Ok(PositionComponent::Percentage(value.unit_value))
Ok(PositionComponent::Percentage(Percentage(value.unit_value)))
}
Token::Number(ref value) if value.value == 0. => {
Ok(PositionComponent::Length(Length::Absolute(Au(0))))
@ -572,11 +920,11 @@ pub mod specified {
match self {
PositionComponent::Length(x) => LengthOrPercentage::Length(x),
PositionComponent::Percentage(x) => LengthOrPercentage::Percentage(x),
PositionComponent::Center => LengthOrPercentage::Percentage(0.5),
PositionComponent::Center => LengthOrPercentage::Percentage(Percentage(0.5)),
PositionComponent::Left |
PositionComponent::Top => LengthOrPercentage::Percentage(0.0),
PositionComponent::Top => LengthOrPercentage::Percentage(Percentage(0.0)),
PositionComponent::Right |
PositionComponent::Bottom => LengthOrPercentage::Percentage(1.0),
PositionComponent::Bottom => LengthOrPercentage::Percentage(Percentage(1.0)),
}
}
}
@ -943,10 +1291,66 @@ pub mod computed {
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct Calc {
length: Option<Au>,
percentage: Option<CSSFloat>,
}
impl Calc {
pub fn length(&self) -> Au {
self.length.unwrap_or(Au(0))
}
pub fn percentage(&self) -> CSSFloat {
self.percentage.unwrap_or(0.)
}
}
impl ::cssparser::ToCss for Calc {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match (self.length, self.percentage) {
(None, Some(p)) => write!(dest, "{}%", p * 100.),
(Some(l), None) => write!(dest, "{}px", Au::to_px(l)),
(Some(l), Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.),
_ => unreachable!()
}
}
}
impl ToComputedValue for specified::Calc {
type ComputedValue = Calc;
fn to_computed_value(&self, context: &Context) -> Calc {
let mut length = None;
if let Some(absolute) = self.absolute {
length = Some(length.unwrap_or(Au(0)) + absolute);
}
for val in &[self.vw, self.vh, self.vmin, self.vmax] {
if let Some(val) = *val {
length = Some(length.unwrap_or(Au(0)) +
val.to_computed_value(context.viewport_size));
}
}
for val in &[self.ch, self.em, self.ex, self.rem] {
if let Some(val) = *val {
length = Some(length.unwrap_or(Au(0)) +
val.to_computed_value(context.font_size, context.root_font_size));
}
}
Calc { length: length, percentage: self.percentage.map(|p| p.0) }
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentage {
Length(Au),
Percentage(CSSFloat),
Calc(Calc),
}
impl LengthOrPercentage {
@ -960,6 +1364,7 @@ pub mod computed {
match self {
&LengthOrPercentage::Length(length) => write!(f, "{:?}", length),
&LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
&LengthOrPercentage::Calc(calc) => write!(f, "{:?}", calc),
}
}
}
@ -973,7 +1378,10 @@ pub mod computed {
LengthOrPercentage::Length(value.to_computed_value(context))
}
specified::LengthOrPercentage::Percentage(value) => {
LengthOrPercentage::Percentage(value)
LengthOrPercentage::Percentage(value.0)
}
specified::LengthOrPercentage::Calc(calc) => {
LengthOrPercentage::Calc(calc.to_computed_value(context))
}
}
}
@ -985,6 +1393,7 @@ pub mod computed {
&LengthOrPercentage::Length(length) => length.to_css(dest),
&LengthOrPercentage::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
&LengthOrPercentage::Calc(calc) => calc.to_css(dest),
}
}
}
@ -994,6 +1403,7 @@ pub mod computed {
Length(Au),
Percentage(CSSFloat),
Auto,
Calc(Calc),
}
impl fmt::Debug for LengthOrPercentageOrAuto {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -1001,6 +1411,7 @@ pub mod computed {
&LengthOrPercentageOrAuto::Length(length) => write!(f, "{:?}", length),
&LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
&LengthOrPercentageOrAuto::Auto => write!(f, "auto"),
&LengthOrPercentageOrAuto::Calc(calc) => write!(f, "{:?}", calc),
}
}
}
@ -1015,11 +1426,14 @@ pub mod computed {
LengthOrPercentageOrAuto::Length(value.to_computed_value(context))
}
specified::LengthOrPercentageOrAuto::Percentage(value) => {
LengthOrPercentageOrAuto::Percentage(value)
LengthOrPercentageOrAuto::Percentage(value.0)
}
specified::LengthOrPercentageOrAuto::Auto => {
LengthOrPercentageOrAuto::Auto
}
specified::LengthOrPercentageOrAuto::Calc(calc) => {
LengthOrPercentageOrAuto::Calc(calc.to_computed_value(context))
}
}
}
}
@ -1031,6 +1445,7 @@ pub mod computed {
&LengthOrPercentageOrAuto::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
&LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
&LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest),
}
}
}
@ -1061,7 +1476,7 @@ pub mod computed {
LengthOrPercentageOrNone::Length(value.to_computed_value(context))
}
specified::LengthOrPercentageOrNone::Percentage(value) => {
LengthOrPercentageOrNone::Percentage(value)
LengthOrPercentageOrNone::Percentage(value.0)
}
specified::LengthOrPercentageOrNone::None => {
LengthOrPercentageOrNone::None

View file

@ -9,7 +9,8 @@ use parser::{ParserContext, log_css_error};
use properties::longhands;
use stylesheets::Origin;
use util::geometry::{Au, PagePx, ViewportPx};
use values::specified::{AllowedNumericType, Length, LengthOrPercentageOrAuto};
use values::computed::{Context, ToComputedValue};
use values::specified::{AllowedNumericType, LengthOrPercentageOrAuto};
use std::ascii::AsciiExt;
use std::collections::hash_map::{Entry, HashMap};
@ -420,23 +421,42 @@ impl ViewportConstraints {
let initial_viewport = Size2D::new(Au::from_f32_px(initial_viewport.width.get()),
Au::from_f32_px(initial_viewport.height.get()));
let context = Context {
is_root_element: false,
viewport_size: initial_viewport,
inherited_font_weight: longhands::font_weight::get_initial_value(),
inherited_font_size: longhands::font_size::get_initial_value(),
inherited_text_decorations_in_effect: longhands::_servo_text_decorations_in_effect::get_initial_value(),
font_size: longhands::font_size::get_initial_value(),
root_font_size: longhands::font_size::get_initial_value(),
display: longhands::display::get_initial_value(),
color: longhands::color::get_initial_value(),
text_decoration: longhands::text_decoration::get_initial_value(),
overflow_x: longhands::overflow_x::get_initial_value(),
overflow_y: longhands::overflow_y::get_initial_value(),
positioned: false,
floated: false,
border_top_present: false,
border_right_present: false,
border_bottom_present: false,
border_left_present: false,
outline_style_present: false,
};
macro_rules! to_pixel_length {
($value:ident, $dimension:ident) => {
if let Some($value) = $value {
match $value {
LengthOrPercentageOrAuto::Length(ref value) => Some(match value {
&Length::Absolute(length) => length,
&Length::FontRelative(length) => {
let initial_font_size = longhands::font_size::get_initial_value();
length.to_computed_value(initial_font_size, initial_font_size)
}
&Length::ViewportPercentage(length) =>
length.to_computed_value(initial_viewport),
_ => unreachable!()
}),
LengthOrPercentageOrAuto::Length(value) =>
Some(value.to_computed_value(&context)),
LengthOrPercentageOrAuto::Percentage(value) =>
Some(initial_viewport.$dimension.scale_by(value)),
Some(initial_viewport.$dimension.scale_by(value.0)),
LengthOrPercentageOrAuto::Auto => None,
LengthOrPercentageOrAuto::Calc(calc) => {
let calc = calc.to_computed_value(&context);
Some(initial_viewport.$dimension.scale_by(calc.percentage()) + calc.length())
}
}
} else {
None