mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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:
commit
a547ae6826
21 changed files with 873 additions and 63 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,6 +167,7 @@ impl FlexFlow {
|
|||
}
|
||||
(LengthOrPercentageOrAuto::Percentage(_), None) |
|
||||
(LengthOrPercentageOrAuto::Auto, _) => None,
|
||||
(LengthOrPercentageOrAuto::Calc(_), _) => None,
|
||||
(LengthOrPercentageOrAuto::Length(length), _) => Some(length),
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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))));
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#![feature(arc_unique)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(custom_attribute)]
|
||||
#![feature(custom_derive)]
|
||||
|
|
|
@ -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))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
19
tests/ref/calc-basic-ref.html
Normal file
19
tests/ref/calc-basic-ref.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<div id="outer">
|
||||
<div id="inner">
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
#outer {
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#inner {
|
||||
height: 100%;
|
||||
width: 110px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
19
tests/ref/calc-basic.html
Normal file
19
tests/ref/calc-basic.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<div id="outer">
|
||||
<div id="inner">
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
#outer {
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#inner {
|
||||
height: 100%;
|
||||
width: calc(50px + 30%);
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<title>font-size (issues #1435, #3417)</title>
|
||||
<style>p { margin: .5em }</style>
|
||||
<body style="font-size: 20px">
|
||||
<p style="font-size: 24pt">24pt is 32px.
|
||||
<p style="font-size: 2em">2em is 40px.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<title>font-size (issues #1435, #3417)</title>
|
||||
<style>p { margin: .5em }</style>
|
||||
<body>
|
||||
<p style="font-size: 32px">24pt is 32px.
|
||||
<p style="font-size: 40px">2em is 40px.
|
||||
|
|
|
@ -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",
|
||||
|
|
128
tests/wpt/mozilla/meta/mozilla/calc.html.ini
Normal file
128
tests/wpt/mozilla/meta/mozilla/calc.html.ini
Normal file
|
@ -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
|
||||
|
153
tests/wpt/mozilla/tests/mozilla/calc.html
Normal file
153
tests/wpt/mozilla/tests/mozilla/calc.html
Normal file
|
@ -0,0 +1,153 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
#outer {
|
||||
width: 1000px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="outer">FOO<div id="inner"></div></div>
|
||||
<script>
|
||||
|
||||
var div = document.getElementById('inner');
|
||||
|
||||
var widthTests = [
|
||||
['calc(10px)', '10px', '10px'],
|
||||
|
||||
// Basic Arithmetic
|
||||
['calc(10px + 10px)', '20px', '20px'],
|
||||
['calc(10px - 5px)', '5px', '5px'],
|
||||
['calc(2 * 10px)', '20px', '20px'],
|
||||
['calc(10px / 2)', '5px', '5px'],
|
||||
|
||||
// Parse ok
|
||||
['calc(20px/2)', '10px', '10px'],
|
||||
['calc(10px*2)', '20px', '20px'],
|
||||
|
||||
// Parse errors - value left over from previous test
|
||||
['calc(10px-10px)', '20px', '20px'],
|
||||
['calc(5px+5px)', '20px', '20px'],
|
||||
|
||||
// Combining units
|
||||
['calc(10px + 10em)', 'calc(10em + 10px)', '170px'],
|
||||
['calc(10px + 10em - 10px)', 'calc(10em + 0px)', '160px'],
|
||||
|
||||
// Fold absolute units
|
||||
['calc(1px + 1pt + 1pc + 1in + 1cm + 1mm)', '155.88333333333333px', '155.88333333333333px'],
|
||||
|
||||
// Alphabetical order
|
||||
['calc(0px + 0pt + 0pc + 0in + 0cm + 0mm + 0rem + 0em + 0ex + 0% + 0vw + 0vh + 0vmin + 0vmax)',
|
||||
'calc(0em + 0ex + 0px + 0rem + 0vh + 0vmax + 0vmin + 0vw + 0%)',
|
||||
'0px'],
|
||||
|
||||
// Simplification
|
||||
['calc((2 - 1) * 10px)', '10px', '10px'],
|
||||
['calc(((3 - 1) * (8 + 4)) * 10px)', '240px', '240px'],
|
||||
['calc(5 * (20px / 2 + 7 * (3em + 12px/4 + (8 - 2) * 2rem)))', 'calc(105em + 155px + 420rem)', '8555px'],
|
||||
|
||||
];
|
||||
|
||||
widthTests.forEach(function(item) {
|
||||
test(function() {
|
||||
div.style.setProperty('width', item[0]);
|
||||
assert_equals(div.style.getPropertyValue('width'), item[1]);
|
||||
assert_equals(window.getComputedStyle(div).getPropertyValue('width'), item[2]);
|
||||
}, item[0]);
|
||||
});
|
||||
|
||||
var lengthProperties = [
|
||||
'border-width',
|
||||
'border-top-width',
|
||||
'border-left-width',
|
||||
'border-right-width',
|
||||
'border-bottom-width',
|
||||
'outline-width',
|
||||
'outline-offset',
|
||||
'letter-spacing',
|
||||
'word-spacing',
|
||||
'border-spacing',
|
||||
'column-width',
|
||||
'column-gap',
|
||||
'perspective',
|
||||
];
|
||||
|
||||
lengthProperties.forEach(function(prop) {
|
||||
test(function() {
|
||||
div.style.setProperty(prop, 'calc(1px)');
|
||||
assert_equals(div.style.getPropertyValue(prop), '1px');
|
||||
assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1px');
|
||||
}, 'calc for ' + prop);
|
||||
});
|
||||
|
||||
var lengthOrPercentageProperties = [
|
||||
'border-top-left-radius',
|
||||
'border-bottom-left-radius',
|
||||
'border-top-right-radius',
|
||||
'border-bottom-right-radius',
|
||||
'top',
|
||||
'left',
|
||||
'bottom',
|
||||
'right',
|
||||
'width',
|
||||
'height',
|
||||
'min-width',
|
||||
'min-height',
|
||||
'max-width',
|
||||
'max-height',
|
||||
'line-height',
|
||||
'vertical-align',
|
||||
'background-position',
|
||||
'background-size',
|
||||
'font-size',
|
||||
'text-indent',
|
||||
'transform-origin',
|
||||
'perspective-origin',
|
||||
];
|
||||
|
||||
lengthOrPercentageProperties.forEach(function(prop) {
|
||||
test(function() {
|
||||
div.style.setProperty(prop, 'calc(1px + 0%)');
|
||||
assert_equals(div.style.getPropertyValue(prop), 'calc(1px + 0%)');
|
||||
assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1px');
|
||||
}, 'calc for ' + prop);
|
||||
});
|
||||
|
||||
var timeProperties = [
|
||||
'transition-delay',
|
||||
];
|
||||
|
||||
timeProperties.forEach(function(prop) {
|
||||
test(function() {
|
||||
div.style.setProperty(prop, 'calc(1s)');
|
||||
assert_equals(div.style.getPropertyValue(prop), '1s');
|
||||
assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1s');
|
||||
}, 'calc for ' + prop);
|
||||
});
|
||||
|
||||
var numberProperties = [
|
||||
'z-index',
|
||||
'column-count',
|
||||
'opacity',
|
||||
'transition-duration',
|
||||
];
|
||||
|
||||
numberProperties.forEach(function(prop) {
|
||||
test(function() {
|
||||
div.style.setProperty(prop, 'calc(1)');
|
||||
assert_equals(div.style.getPropertyValue(prop), '1');
|
||||
assert_equals(window.getComputedStyle(div).getPropertyValue(prop), '1');
|
||||
}, 'calc for ' + prop);
|
||||
});
|
||||
|
||||
|
||||
/* TODO: test these:
|
||||
counter-increment, counter-reset,
|
||||
color, box-shadow, clip, text-shadow, transform
|
||||
transition-timing-function
|
||||
*/
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue