mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01: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
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue