diff --git a/components/layout/block.rs b/components/layout/block.rs index 3926384379a..7288a11c902 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -327,7 +327,12 @@ impl CandidateBSizeIterator { (LengthOrPercentageOrAuto::Percentage(percent), Some(block_container_block_size)) => { MaybeAuto::Specified(block_container_block_size.scale_by(percent)) } - (LengthOrPercentageOrAuto::Percentage(_), None) | (LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto, + (LengthOrPercentageOrAuto::Calc(calc), Some(block_container_block_size)) => { + MaybeAuto::Specified(calc.length() + block_container_block_size.scale_by(calc.percentage())) + } + (LengthOrPercentageOrAuto::Percentage(_), None) | + (LengthOrPercentageOrAuto::Auto, _) | + (LengthOrPercentageOrAuto::Calc(_), _) => MaybeAuto::Auto, (LengthOrPercentageOrAuto::Length(length), _) => MaybeAuto::Specified(length), }; let max_block_size = match (fragment.style.max_block_size(), block_container_block_size) { @@ -342,6 +347,10 @@ impl CandidateBSizeIterator { (LengthOrPercentage::Percentage(percent), Some(block_container_block_size)) => { block_container_block_size.scale_by(percent) } + (LengthOrPercentage::Calc(calc), Some(block_container_block_size)) => { + calc.length() + block_container_block_size.scale_by(calc.percentage()) + } + (LengthOrPercentage::Calc(calc), None) => calc.length(), (LengthOrPercentage::Percentage(_), None) => Au(0), (LengthOrPercentage::Length(length), _) => length, }; @@ -1128,13 +1137,15 @@ impl BlockFlow { let content_block_size = self.fragment.style().content_block_size(); match (content_block_size, containing_block_size) { + (LengthOrPercentageOrAuto::Calc(calc), Some(container_size)) => { + Some(container_size.scale_by(calc.percentage()) + calc.length()) + } (LengthOrPercentageOrAuto::Length(length), _) => Some(length), (LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => { Some(container_size.scale_by(percent)) } - (LengthOrPercentageOrAuto::Percentage(_), None) => { - None - } + (LengthOrPercentageOrAuto::Percentage(_), None) | + (LengthOrPercentageOrAuto::Calc(_), None) | (LengthOrPercentageOrAuto::Auto, None) => { None } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 19c466ee528..d2dd57f0e21 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1959,6 +1959,8 @@ fn position_to_offset(position: LengthOrPercentage, Au(total_length): Au) -> f32 fmin(1.0, (length as f32) / (total_length as f32)) } LengthOrPercentage::Percentage(percentage) => percentage as f32, + LengthOrPercentage::Calc(calc) => + fmin(1.0, calc.percentage() + (calc.length().0 as f32) / (total_length as f32)), } } diff --git a/components/layout/flex.rs b/components/layout/flex.rs index 88fe929a9d3..2cc2424a854 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -167,6 +167,7 @@ impl FlexFlow { } (LengthOrPercentageOrAuto::Percentage(_), None) | (LengthOrPercentageOrAuto::Auto, _) => None, + (LengthOrPercentageOrAuto::Calc(_), _) => None, (LengthOrPercentageOrAuto::Length(length), _) => Some(length), }; diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index faa2dd4d610..f03e0cc7f7a 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -463,6 +463,10 @@ impl ReplacedImageFragmentInfo { MaybeAuto::Specified(container_size.scale_by(pc)) } (LengthOrPercentageOrAuto::Percentage(_), _, None) => MaybeAuto::Auto, + (LengthOrPercentageOrAuto::Calc(calc), _, Some(container_size)) => { + MaybeAuto::Specified(calc.length() + container_size.scale_by(calc.percentage())) + } + (LengthOrPercentageOrAuto::Calc(_), _, None) => MaybeAuto::Auto, (LengthOrPercentageOrAuto::Auto, Some(dom_length), _) => MaybeAuto::Specified(dom_length), (LengthOrPercentageOrAuto::Auto, None, _) => MaybeAuto::Auto, } @@ -616,6 +620,10 @@ impl IframeFragmentInfo { let computed_size = match (content_size, containing_size) { (LengthOrPercentageOrAuto::Length(length), _) => length, (LengthOrPercentageOrAuto::Percentage(pc), Some(container_size)) => container_size.scale_by(pc), + (LengthOrPercentageOrAuto::Calc(calc), Some(container_size)) => { + container_size.scale_by(calc.percentage()) + calc.length() + }, + (LengthOrPercentageOrAuto::Calc(calc), None) => calc.length(), (LengthOrPercentageOrAuto::Percentage(_), None) => default_size, (LengthOrPercentageOrAuto::Auto, _) => default_size, }; @@ -1285,6 +1293,7 @@ impl Fragment { } (Some(dom_inline_size), _) => dom_inline_size, (None, LengthOrPercentageOrAuto::Length(length)) => length, + (None, LengthOrPercentageOrAuto::Calc(calc)) => calc.length(), }; result.union_block(&IntrinsicISizes { minimum_inline_size: image_inline_size, diff --git a/components/layout/inline.rs b/components/layout/inline.rs index ec69db2f622..51739ede4c8 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -988,6 +988,11 @@ impl InlineFlow { let percent_offset = line_height.scale_by(p); offset_from_baseline = offset_from_baseline - percent_offset } + vertical_align::T::Calc(calc) => { + let line_height = fragment.calculate_line_height(layout_context); + let percent_offset = line_height.scale_by(calc.percentage()); + offset_from_baseline = offset_from_baseline - percent_offset - calc.length() + } } } (offset_from_baseline - ascent, largest_size_updated) diff --git a/components/layout/model.rs b/components/layout/model.rs index 6b7fdcbb58e..45efadf1246 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -379,6 +379,9 @@ impl MaybeAuto { LengthOrPercentageOrAuto::Percentage(percent) => { MaybeAuto::Specified(containing_length.scale_by(percent)) } + LengthOrPercentageOrAuto::Calc(calc) => { + MaybeAuto::Specified(calc.length() + containing_length.scale_by(calc.percentage())) + } LengthOrPercentageOrAuto::Length(length) => MaybeAuto::Specified(length) } } @@ -416,7 +419,9 @@ pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au pub fn specified(length: LengthOrPercentage, containing_length: Au) -> Au { match length { LengthOrPercentage::Length(length) => length, - LengthOrPercentage::Percentage(p) => containing_length.scale_by(p) + LengthOrPercentage::Percentage(p) => containing_length.scale_by(p), + LengthOrPercentage::Calc(calc) => + containing_length.scale_by(calc.percentage()) + calc.length(), } } diff --git a/components/layout/table.rs b/components/layout/table.rs index 54e3f090b59..50ce06c8959 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -267,11 +267,13 @@ impl Flow for TableFlow { self.column_intrinsic_inline_sizes.push(ColumnIntrinsicInlineSize { minimum_length: match *specified_inline_size { LengthOrPercentageOrAuto::Auto | + LengthOrPercentageOrAuto::Calc(_) | LengthOrPercentageOrAuto::Percentage(_) => Au(0), LengthOrPercentageOrAuto::Length(length) => length, }, percentage: match *specified_inline_size { LengthOrPercentageOrAuto::Auto | + LengthOrPercentageOrAuto::Calc(_) | LengthOrPercentageOrAuto::Length(_) => 0.0, LengthOrPercentageOrAuto::Percentage(percentage) => percentage, }, diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 4043363a23a..315f5bc3347 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -280,6 +280,7 @@ impl Flow for TableRowFlow { let child_column_inline_size = ColumnIntrinsicInlineSize { minimum_length: match child_specified_inline_size { LengthOrPercentageOrAuto::Auto | + LengthOrPercentageOrAuto::Calc(_) | LengthOrPercentageOrAuto::Percentage(_) => { child_base.intrinsic_inline_sizes.minimum_inline_size } @@ -287,6 +288,7 @@ impl Flow for TableRowFlow { }, percentage: match child_specified_inline_size { LengthOrPercentageOrAuto::Auto | + LengthOrPercentageOrAuto::Calc(_) | LengthOrPercentageOrAuto::Length(_) => 0.0, LengthOrPercentageOrAuto::Percentage(percentage) => percentage, }, @@ -294,6 +296,7 @@ impl Flow for TableRowFlow { constrained: match child_specified_inline_size { LengthOrPercentageOrAuto::Length(_) => true, LengthOrPercentageOrAuto::Auto | + LengthOrPercentageOrAuto::Calc(_) | LengthOrPercentageOrAuto::Percentage(_) => false, }, }; diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d8d429cdeab..c17a1e0d00e 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -365,7 +365,8 @@ impl RawLayoutElementHelpers for Element { match width { LengthOrPercentageOrAuto::Auto => {} LengthOrPercentageOrAuto::Percentage(percentage) => { - let width_value = specified::LengthOrPercentageOrAuto::Percentage(percentage); + let width_value = + specified::LengthOrPercentageOrAuto::Percentage(specified::Percentage(percentage)); hints.push(from_declaration( PropertyDeclaration::Width(SpecifiedValue(width_value)))); } @@ -388,7 +389,8 @@ impl RawLayoutElementHelpers for Element { match height { LengthOrPercentageOrAuto::Auto => {} LengthOrPercentageOrAuto::Percentage(percentage) => { - let height_value = specified::LengthOrPercentageOrAuto::Percentage(percentage); + let height_value = + specified::LengthOrPercentageOrAuto::Percentage(specified::Percentage(percentage)); hints.push(from_declaration( PropertyDeclaration::Height(SpecifiedValue(height_value)))); } diff --git a/components/style/lib.rs b/components/style/lib.rs index a6a53464c26..99390a4bc3a 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -4,6 +4,7 @@ #![feature(arc_unique)] #![feature(box_syntax)] +#![feature(box_patterns)] #![feature(core_intrinsics)] #![feature(custom_attribute)] #![feature(custom_derive)] diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 3364091766b..857718e7a44 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -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 { /// | | | pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { 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 { + 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 { 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))), }) } } diff --git a/components/style/values.rs b/components/style/values.rs index 2a5de980ed9..bc5be3ed2fa 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -358,19 +358,359 @@ pub mod specified { } } + #[derive(Clone, Debug)] + struct CalcSumNode { + products: Vec, + } + + #[derive(Clone, Debug)] + struct CalcProductNode { + values: Vec + } + + #[derive(Clone, Debug)] + enum CalcValueNode { + Length(Length), + Percentage(CSSFloat), + Number(CSSFloat), + Sum(Box), + } + + #[derive(Clone, Debug)] + struct SimplifiedSumNode { + values: Vec, + } + impl<'a> Mul 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), + } + impl<'a> Mul 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, + pub vw: Option, + pub vh: Option, + pub vmin: Option, + pub vmax: Option, + pub em: Option, + pub ex: Option, + pub ch: Option, + pub rem: Option, + pub percentage: Option, + } + impl Calc { + fn parse_sum(input: &mut Parser) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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(&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(&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(&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(&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(&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, + percentage: Option, + } + + 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(&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 diff --git a/components/style/viewport.rs b/components/style/viewport.rs index 15dcac2eefe..247cd6400ae 100644 --- a/components/style/viewport.rs +++ b/components/style/viewport.rs @@ -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 diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 5dda6a91fda..c7aeb3a1de8 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -82,6 +82,7 @@ flaky_cpu == append_style_a.html append_style_b.html == box_sizing_border_box_a.html box_sizing_border_box_ref.html == box_sizing_sanity_check_a.html box_sizing_sanity_check_ref.html == br.html br-ref.html +== calc-basic.html calc-basic-ref.html == canvas_as_block_element_a.html canvas_as_block_element_ref.html == canvas_linear_gradient_a.html canvas_linear_gradient_ref.html == canvas_radial_gradient_a.html canvas_radial_gradient_ref.html diff --git a/tests/ref/calc-basic-ref.html b/tests/ref/calc-basic-ref.html new file mode 100644 index 00000000000..f2120ffdd68 --- /dev/null +++ b/tests/ref/calc-basic-ref.html @@ -0,0 +1,19 @@ + +
+
+
+
+ + + diff --git a/tests/ref/calc-basic.html b/tests/ref/calc-basic.html new file mode 100644 index 00000000000..ae972fff38e --- /dev/null +++ b/tests/ref/calc-basic.html @@ -0,0 +1,19 @@ + +
+
+
+
+ + + diff --git a/tests/ref/font_size.html b/tests/ref/font_size.html index 6d534378724..7c500513bcb 100644 --- a/tests/ref/font_size.html +++ b/tests/ref/font_size.html @@ -1,5 +1,6 @@ font-size (issues #1435, #3417) +

24pt is 32px.

2em is 40px. diff --git a/tests/ref/font_size_ref.html b/tests/ref/font_size_ref.html index 0c5d50fb8b7..62adacf79f3 100644 --- a/tests/ref/font_size_ref.html +++ b/tests/ref/font_size_ref.html @@ -1,5 +1,6 @@ font-size (issues #1435, #3417) +

24pt is 32px.

2em is 40px. diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index b0719a029b5..772bceec7d2 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -299,6 +299,12 @@ "url": "/_mozilla/mozilla/body_listener.html" } ], + "mozilla/calc.html": [ + { + "path": "mozilla/calc.html", + "url": "/_mozilla/mozilla/calc.html" + } + ], "mozilla/caption.html": [ { "path": "mozilla/caption.html", diff --git a/tests/wpt/mozilla/meta/mozilla/calc.html.ini b/tests/wpt/mozilla/meta/mozilla/calc.html.ini new file mode 100644 index 00000000000..ad1ebaf3617 --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/calc.html.ini @@ -0,0 +1,128 @@ +[calc.html] + type: testharness + [calc(1px + 1pt + 1pc + 1in + 1cm + 1mm)] + expected: FAIL + + [calc(0px + 0pt + 0pc + 0in + 0cm + 0mm + 0rem + 0em + 0ex + 0% + 0vw + 0vh + 0vmin + 0vmax)] + expected: FAIL + + [calc for border-width] + expected: FAIL + + [calc for border-top-width] + expected: FAIL + + [calc for border-left-width] + expected: FAIL + + [calc for border-right-width] + expected: FAIL + + [calc for border-bottom-width] + expected: FAIL + + [calc for outline-width] + expected: FAIL + + [calc for outline-offset] + expected: FAIL + + [calc for letter-spacing] + expected: FAIL + + [calc for word-spacing] + expected: FAIL + + [calc for border-spacing] + expected: FAIL + + [calc for column-width] + expected: FAIL + + [calc for column-gap] + expected: FAIL + + [calc for perspective] + expected: FAIL + + [calc for border-top-left-radius] + expected: FAIL + + [calc for border-bottom-left-radius] + expected: FAIL + + [calc for border-top-right-radius] + expected: FAIL + + [calc for border-bottom-right-radius] + expected: FAIL + + [calc for top] + expected: FAIL + + [calc for left] + expected: FAIL + + [calc for bottom] + expected: FAIL + + [calc for right] + expected: FAIL + + [calc for width] + expected: FAIL + + [calc for height] + expected: FAIL + + [calc for min-width] + expected: FAIL + + [calc for min-height] + expected: FAIL + + [calc for max-width] + expected: FAIL + + [calc for max-height] + expected: FAIL + + [calc for line-height] + expected: FAIL + + [calc for vertical-align] + expected: FAIL + + [calc for background-position] + expected: FAIL + + [calc for background-size] + expected: FAIL + + [calc for font-size] + expected: FAIL + + [calc for text-indent] + expected: FAIL + + [calc for transform-origin] + expected: FAIL + + [calc for perspective-origin] + expected: FAIL + + [calc for transition-delay] + expected: FAIL + + [calc for z-index] + expected: FAIL + + [calc for column-count] + expected: FAIL + + [calc for opacity] + expected: FAIL + + [calc for transition-duration] + expected: FAIL + diff --git a/tests/wpt/mozilla/tests/mozilla/calc.html b/tests/wpt/mozilla/tests/mozilla/calc.html new file mode 100644 index 00000000000..b69c28e67d0 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/calc.html @@ -0,0 +1,153 @@ + + + + + + + +

FOO
+ + +