servo/components/style/values.rs
Patrick Walton 04f05349b1 layout: Check flow descendants of inline block fragments to find their
baselines when aligning inline fragments per CSS 2.1 § 10.8.1.
2016-05-04 13:20:21 -07:00

2174 lines
83 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub use cssparser::RGBA;
use app_units::Au;
use cssparser::CssStringWriter;
use std::fmt::{self, Write};
use url::Url;
// This is a re-implementation of the ToCss trait in cssparser.
// It's done here because the app_units crate shouldn't depend
// on cssparser, and it's not possible to implement a trait when
// both the trait and the type are defined in different crates.
pub trait AuExtensionMethods {
/// Serialize `self` in CSS syntax, writing to `dest`.
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write;
/// Serialize `self` in CSS syntax and return a string.
///
/// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
#[inline]
fn to_css_string(&self) -> String {
let mut s = String::new();
self.to_css(&mut s).unwrap();
s
}
}
impl AuExtensionMethods for Au {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
write!(dest, "{}px", self.to_f64_px())
}
}
macro_rules! define_numbered_css_keyword_enum {
($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
};
($name: ident: $( $css: expr => $variant: ident = $value: expr ),+) => {
#[allow(non_camel_case_types)]
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Copy, RustcEncodable, Debug, HeapSizeOf)]
#[derive(Deserialize, Serialize)]
pub enum $name {
$( $variant = $value ),+
}
impl $name {
pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
match_ignore_ascii_case! { try!(input.expect_ident()),
$( $css => Ok($name::$variant), )+
_ => Err(())
}
}
}
impl ::cssparser::ToCss for $name {
fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
where W: ::std::fmt::Write {
match *self {
$( $name::$variant => dest.write_str($css) ),+
}
}
}
}
}
/// The real ToCss trait cant be implemented for Url
/// since neither rust-url or rust-cssparser depend on the other.
pub trait LocalToCss {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write;
}
impl LocalToCss for Url {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(dest.write_str("url(\""));
try!(write!(CssStringWriter::new(dest), "{}", self));
try!(dest.write_str("\")"));
Ok(())
}
}
pub type CSSFloat = f32;
pub const FONT_MEDIUM_PX: i32 = 16;
pub mod specified {
use app_units::Au;
use cssparser::{self, CssStringWriter, Parser, ToCss, Token};
use euclid::size::Size2D;
use parser::ParserContext;
use std::ascii::AsciiExt;
use std::cmp;
use std::f32::consts::PI;
use std::fmt::{self, Write};
use std::ops::Mul;
use style_traits::values::specified::AllowedNumericType;
use super::AuExtensionMethods;
use super::computed::{TContext, ToComputedValue};
use super::{CSSFloat, FONT_MEDIUM_PX};
use url::Url;
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub struct CSSColor {
pub parsed: cssparser::Color,
pub authored: Option<String>,
}
impl CSSColor {
pub fn parse(input: &mut Parser) -> Result<CSSColor, ()> {
let start_position = input.position();
let authored = match input.next() {
Ok(Token::Ident(s)) => Some(s.into_owned()),
_ => None,
};
input.reset(start_position);
Ok(CSSColor {
parsed: try!(cssparser::Color::parse(input)),
authored: authored,
})
}
}
impl ToCss for CSSColor {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self.authored {
Some(ref s) => dest.write_str(s),
None => self.parsed.to_css(dest),
}
}
}
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub struct CSSRGBA {
pub parsed: cssparser::RGBA,
pub authored: Option<String>,
}
impl ToCss for CSSRGBA {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self.authored {
Some(ref s) => dest.write_str(s),
None => self.parsed.to_css(dest),
}
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum FontRelativeLength {
Em(CSSFloat),
Ex(CSSFloat),
Ch(CSSFloat),
Rem(CSSFloat)
}
impl ToCss for FontRelativeLength {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
FontRelativeLength::Em(length) => write!(dest, "{}em", length),
FontRelativeLength::Ex(length) => write!(dest, "{}ex", length),
FontRelativeLength::Ch(length) => write!(dest, "{}ch", length),
FontRelativeLength::Rem(length) => write!(dest, "{}rem", length)
}
}
}
impl FontRelativeLength {
pub fn to_computed_value(&self,
reference_font_size: Au,
root_font_size: Au)
-> Au
{
match *self {
FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
FontRelativeLength::Ex(length) | FontRelativeLength::Ch(length) => {
// https://github.com/servo/servo/issues/7462
let em_factor = 0.5;
reference_font_size.scale_by(length * em_factor)
},
FontRelativeLength::Rem(length) => root_font_size.scale_by(length)
}
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum ViewportPercentageLength {
Vw(CSSFloat),
Vh(CSSFloat),
Vmin(CSSFloat),
Vmax(CSSFloat)
}
impl ToCss for ViewportPercentageLength {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
ViewportPercentageLength::Vw(length) => write!(dest, "{}vw", length),
ViewportPercentageLength::Vh(length) => write!(dest, "{}vh", length),
ViewportPercentageLength::Vmin(length) => write!(dest, "{}vmin", length),
ViewportPercentageLength::Vmax(length) => write!(dest, "{}vmax", length)
}
}
}
impl ViewportPercentageLength {
pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> Au {
macro_rules! to_unit {
($viewport_dimension:expr) => {
$viewport_dimension.to_f32_px() / 100.0
}
}
let value = match *self {
ViewportPercentageLength::Vw(length) =>
length * to_unit!(viewport_size.width),
ViewportPercentageLength::Vh(length) =>
length * to_unit!(viewport_size.height),
ViewportPercentageLength::Vmin(length) =>
length * to_unit!(cmp::min(viewport_size.width, viewport_size.height)),
ViewportPercentageLength::Vmax(length) =>
length * to_unit!(cmp::max(viewport_size.width, viewport_size.height)),
};
Au::from_f32_px(value)
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct CharacterWidth(pub i32);
impl CharacterWidth {
pub fn to_computed_value(&self, reference_font_size: Au) -> Au {
// This applies the *converting a character width to pixels* algorithm as specified
// in HTML5 § 14.5.4.
//
// TODO(pcwalton): Find these from the font.
let average_advance = reference_font_size.scale_by(0.5);
let max_advance = reference_font_size;
average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum Length {
Absolute(Au), // application units
FontRelative(FontRelativeLength),
ViewportPercentage(ViewportPercentageLength),
/// HTML5 "character width", as defined in HTML5 § 14.5.4.
///
/// This cannot be specified by the user directly and is only generated by
/// `Stylist::synthesize_rules_for_legacy_attributes()`.
ServoCharacterWidth(CharacterWidth),
Calc(CalcLengthOrPercentage),
}
impl ToCss for Length {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
Length::Absolute(length) => write!(dest, "{}px", length.to_f32_px()),
Length::FontRelative(length) => length.to_css(dest),
Length::ViewportPercentage(length) => length.to_css(dest),
Length::Calc(calc) => calc.to_css(dest),
Length::ServoCharacterWidth(_)
=> panic!("internal CSS values should never be serialized"),
}
}
}
impl Mul<CSSFloat> for Length {
type Output = Length;
#[inline]
fn mul(self, scalar: CSSFloat) -> Length {
match self {
Length::Absolute(Au(v)) => Length::Absolute(Au(((v as f32) * scalar) as i32)),
Length::FontRelative(v) => Length::FontRelative(v * scalar),
Length::ViewportPercentage(v) => Length::ViewportPercentage(v * scalar),
Length::Calc(_) => panic!("Can't multiply Calc!"),
Length::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"),
}
}
}
impl Mul<CSSFloat> for FontRelativeLength {
type Output = FontRelativeLength;
#[inline]
fn mul(self, scalar: CSSFloat) -> FontRelativeLength {
match self {
FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar),
FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar),
FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar),
FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar),
}
}
}
impl Mul<CSSFloat> for ViewportPercentageLength {
type Output = ViewportPercentageLength;
#[inline]
fn mul(self, scalar: CSSFloat) -> ViewportPercentageLength {
match self {
ViewportPercentageLength::Vw(v) => ViewportPercentageLength::Vw(v * scalar),
ViewportPercentageLength::Vh(v) => ViewportPercentageLength::Vh(v * scalar),
ViewportPercentageLength::Vmin(v) => ViewportPercentageLength::Vmin(v * scalar),
ViewportPercentageLength::Vmax(v) => ViewportPercentageLength::Vmax(v * scalar),
}
}
}
const AU_PER_PX: CSSFloat = 60.;
const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
const AU_PER_MM: CSSFloat = AU_PER_IN / 25.4;
const AU_PER_Q: CSSFloat = AU_PER_MM / 4.;
const AU_PER_PT: CSSFloat = AU_PER_IN / 72.;
const AU_PER_PC: CSSFloat = AU_PER_PT * 12.;
impl Length {
// https://drafts.csswg.org/css-fonts-3/#font-size-prop
pub fn from_str(s: &str) -> Option<Length> {
Some(match_ignore_ascii_case! { s,
"xx-small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 5),
"x-small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 4),
"small" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 8 / 9),
"medium" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX)),
"large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 6 / 5),
"x-large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 2),
"xx-large" => Length::Absolute(Au::from_px(FONT_MEDIUM_PX) * 2),
// https://github.com/servo/servo/issues/3423#issuecomment-56321664
"smaller" => Length::FontRelative(FontRelativeLength::Em(0.85)),
"larger" => Length::FontRelative(FontRelativeLength::Em(1.2)),
_ => return None
})
}
#[inline]
fn parse_internal(input: &mut Parser, context: &AllowedNumericType) -> Result<Length, ()> {
match try!(input.next()) {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit),
Token::Number(ref value) if value.value == 0. =>
Ok(Length::Absolute(Au(0))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
input.parse_nested_block(CalcLengthOrPercentage::parse_length),
_ => Err(())
}
}
pub fn parse(input: &mut Parser) -> Result<Length, ()> {
Length::parse_internal(input, &AllowedNumericType::All)
}
pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> {
Length::parse_internal(input, &AllowedNumericType::NonNegative)
}
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
match_ignore_ascii_case! { unit,
"px" => Ok(Length::from_px(value)),
"in" => Ok(Length::Absolute(Au((value * AU_PER_IN) as i32))),
"cm" => Ok(Length::Absolute(Au((value * AU_PER_CM) as i32))),
"mm" => Ok(Length::Absolute(Au((value * AU_PER_MM) as i32))),
"q" => Ok(Length::Absolute(Au((value * AU_PER_Q) as i32))),
"pt" => Ok(Length::Absolute(Au((value * AU_PER_PT) as i32))),
"pc" => Ok(Length::Absolute(Au((value * AU_PER_PC) as i32))),
// font-relative
"em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))),
"ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))),
"ch" => Ok(Length::FontRelative(FontRelativeLength::Ch(value))),
"rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))),
// viewport percentages
"vw" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vw(value))),
"vh" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vh(value))),
"vmin" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmin(value))),
"vmax" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmax(value))),
_ => Err(())
}
}
#[inline]
pub fn from_px(px_value: CSSFloat) -> Length {
Length::Absolute(Au((px_value * AU_PER_PX) as i32))
}
}
#[derive(Clone, Debug)]
struct CalcSumNode {
products: Vec<CalcProductNode>,
}
#[derive(Clone, Debug)]
struct CalcProductNode {
values: Vec<CalcValueNode>
}
#[derive(Clone, Debug)]
enum CalcValueNode {
Length(Length),
Angle(Angle),
Time(Time),
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),
Angle(Angle),
Time(Time),
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::Angle(Angle(a)) => SimplifiedValueNode::Angle(Angle(a * scalar)),
SimplifiedValueNode::Time(Time(t)) => SimplifiedValueNode::Time(Time(t * scalar)),
SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar),
SimplifiedValueNode::Sum(box ref s) => {
let sum = s * scalar;
SimplifiedValueNode::Sum(box sum)
}
}
}
}
pub fn parse_integer(input: &mut Parser) -> Result<i32, ()> {
match try!(input.next()) {
Token::Number(ref value) => value.int_value.ok_or(()),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer)));
let mut result = None;
for ref node in ast.products {
match try!(CalcLengthOrPercentage::simplify_product(node)) {
SimplifiedValueNode::Number(val) =>
result = Some(result.unwrap_or(0) + val as i32),
_ => unreachable!()
}
}
match result {
Some(result) => Ok(result),
_ => Err(())
}
}
_ => Err(())
}
}
pub fn parse_number(input: &mut Parser) -> Result<f32, ()> {
match try!(input.next()) {
Token::Number(ref value) => Ok(value.value),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Number)));
let mut result = None;
for ref node in ast.products {
match try!(CalcLengthOrPercentage::simplify_product(node)) {
SimplifiedValueNode::Number(val) =>
result = Some(result.unwrap_or(0.) + val),
_ => unreachable!()
}
}
match result {
Some(result) => Ok(result),
_ => Err(())
}
}
_ => Err(())
}
}
#[derive(Clone, Copy, PartialEq)]
enum CalcUnit {
Number,
Integer,
Length,
LengthOrPercentage,
Angle,
Time,
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct CalcLengthOrPercentage {
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 CalcLengthOrPercentage {
fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> {
let mut products = Vec::new();
products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit)));
while let Ok(token) = input.next() {
match token {
Token::Delim('+') => {
products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit)));
}
Token::Delim('-') => {
let mut right = try!(CalcLengthOrPercentage::parse_product(input, expected_unit));
right.values.push(CalcValueNode::Number(-1.));
products.push(right);
}
_ => return Err(())
}
}
Ok(CalcSumNode { products: products })
}
fn parse_product(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcProductNode, ()> {
let mut values = Vec::new();
values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit)));
loop {
let position = input.position();
match input.next() {
Ok(Token::Delim('*')) => {
values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit)));
}
Ok(Token::Delim('/')) if expected_unit != CalcUnit::Integer => {
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, expected_unit: CalcUnit) -> Result<CalcValueNode, ()> {
match (try!(input.next()), expected_unit) {
(Token::Number(ref value), _) => Ok(CalcValueNode::Number(value.value)),
(Token::Dimension(ref value, ref unit), CalcUnit::Length) |
(Token::Dimension(ref value, ref unit), CalcUnit::LengthOrPercentage) => {
Length::parse_dimension(value.value, unit).map(CalcValueNode::Length)
}
(Token::Dimension(ref value, ref unit), CalcUnit::Angle) => {
Angle::parse_dimension(value.value, unit).map(CalcValueNode::Angle)
}
(Token::Dimension(ref value, ref unit), CalcUnit::Time) => {
Time::parse_dimension(value.value, unit).map(CalcValueNode::Time)
}
(Token::Percentage(ref value), CalcUnit::LengthOrPercentage) =>
Ok(CalcValueNode::Percentage(value.unit_value)),
(Token::ParenthesisBlock, _) => {
input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, expected_unit))
.map(|result| 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) => CalcLengthOrPercentage::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 CalcLengthOrPercentage::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 CalcLengthOrPercentage::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!(CalcLengthOrPercentage::simplify_product(product)) {
SimplifiedValueNode::Sum(box sum) => simplified.extend_from_slice(&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 CalcLengthOrPercentage::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!(CalcLengthOrPercentage::simplify_products_in_sum(sum)),
CalcValueNode::Length(l) => SimplifiedValueNode::Length(l),
CalcValueNode::Angle(a) => SimplifiedValueNode::Angle(a),
CalcValueNode::Time(t) => SimplifiedValueNode::Time(t),
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)
}
}
fn parse_length(input: &mut Parser) -> Result<Length, ()> {
CalcLengthOrPercentage::parse(input, CalcUnit::Length).map(Length::Calc)
}
fn parse_length_or_percentage(input: &mut Parser) -> Result<CalcLengthOrPercentage, ()> {
CalcLengthOrPercentage::parse(input, CalcUnit::LengthOrPercentage)
}
fn parse(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcLengthOrPercentage, ()> {
let ast = try!(CalcLengthOrPercentage::parse_sum(input, expected_unit));
let mut simplified = Vec::new();
for ref node in ast.products {
match try!(CalcLengthOrPercentage::simplify_product(node)) {
SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&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),
_ => return Err(()),
}
}
Ok(CalcLengthOrPercentage {
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),
})
}
pub fn parse_time(input: &mut Parser) -> Result<Time, ()> {
let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Time));
let mut simplified = Vec::new();
for ref node in ast.products {
match try!(CalcLengthOrPercentage::simplify_product(node)) {
SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values),
value => simplified.push(value),
}
}
let mut time = None;
for value in simplified {
match value {
SimplifiedValueNode::Time(Time(val)) =>
time = Some(time.unwrap_or(0.) + val),
_ => return Err(()),
}
}
match time {
Some(time) => Ok(Time(time)),
_ => Err(())
}
}
pub fn parse_angle(input: &mut Parser) -> Result<Angle, ()> {
let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Angle));
let mut simplified = Vec::new();
for ref node in ast.products {
match try!(CalcLengthOrPercentage::simplify_product(node)) {
SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values),
value => simplified.push(value),
}
}
let mut angle = None;
let mut number = None;
for value in simplified {
match value {
SimplifiedValueNode::Angle(Angle(val)) =>
angle = Some(angle.unwrap_or(0.) + val),
SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val),
_ => unreachable!()
}
}
match (angle, number) {
(Some(angle), None) => Ok(Angle(angle)),
(None, Some(value)) if value == 0. => Ok(Angle(0.)),
_ => Err(())
}
}
}
impl ToCss for CalcLengthOrPercentage {
#[allow(unused_assignments)]
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(Percentage),
Calc(CalcLengthOrPercentage),
}
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) => percentage.to_css(dest),
LengthOrPercentage::Calc(calc) => calc.to_css(dest),
}
}
}
impl LengthOrPercentage {
pub fn zero() -> LengthOrPercentage {
LengthOrPercentage::Length(Length::Absolute(Au(0)))
}
fn parse_internal(input: &mut Parser, context: &AllowedNumericType)
-> Result<LengthOrPercentage, ()>
{
match try!(input.next()) {
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(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(CalcLengthOrPercentage::parse_length_or_percentage));
Ok(LengthOrPercentage::Calc(calc))
},
_ => Err(())
}
}
#[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
LengthOrPercentage::parse_internal(input, &AllowedNumericType::All)
}
#[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
LengthOrPercentage::parse_internal(input, &AllowedNumericType::NonNegative)
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentageOrAuto {
Length(Length),
Percentage(Percentage),
Auto,
Calc(CalcLengthOrPercentage),
}
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) => percentage.to_css(dest),
LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest),
}
}
}
impl LengthOrPercentageOrAuto {
fn parse_internal(input: &mut Parser, context: &AllowedNumericType)
-> Result<LengthOrPercentageOrAuto, ()>
{
match try!(input.next()) {
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(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(CalcLengthOrPercentage::parse_length_or_percentage));
Ok(LengthOrPercentageOrAuto::Calc(calc))
},
_ => Err(())
}
}
#[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
LengthOrPercentageOrAuto::parse_internal(input, &AllowedNumericType::All)
}
#[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
LengthOrPercentageOrAuto::parse_internal(input, &AllowedNumericType::NonNegative)
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentageOrNone {
Length(Length),
Percentage(Percentage),
Calc(CalcLengthOrPercentage),
None,
}
impl ToCss for LengthOrPercentageOrNone {
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) => percentage.to_css(dest),
LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest),
LengthOrPercentageOrNone::None => dest.write_str("none"),
}
}
}
impl LengthOrPercentageOrNone {
fn parse_internal(input: &mut Parser, context: &AllowedNumericType)
-> Result<LengthOrPercentageOrNone, ()>
{
match try!(input.next()) {
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(Percentage(value.unit_value))),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage));
Ok(LengthOrPercentageOrNone::Calc(calc))
},
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
Ok(LengthOrPercentageOrNone::None),
_ => Err(())
}
}
#[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> {
LengthOrPercentageOrNone::parse_internal(input, &AllowedNumericType::All)
}
#[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> {
LengthOrPercentageOrNone::parse_internal(input, &AllowedNumericType::NonNegative)
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrNone {
Length(Length),
None,
}
impl ToCss for LengthOrNone {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
LengthOrNone::Length(length) => length.to_css(dest),
LengthOrNone::None => dest.write_str("none"),
}
}
}
impl LengthOrNone {
fn parse_internal(input: &mut Parser, context: &AllowedNumericType)
-> Result<LengthOrNone, ()>
{
match try!(input.next()) {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit).map(LengthOrNone::Length),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrNone::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
input.parse_nested_block(CalcLengthOrPercentage::parse_length).map(LengthOrNone::Length),
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
Ok(LengthOrNone::None),
_ => Err(())
}
}
#[inline]
pub fn parse(input: &mut Parser) -> Result<LengthOrNone, ()> {
LengthOrNone::parse_internal(input, &AllowedNumericType::All)
}
#[inline]
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrNone, ()> {
LengthOrNone::parse_internal(input, &AllowedNumericType::NonNegative)
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentageOrAutoOrContent {
Length(Length),
Percentage(Percentage),
Calc(CalcLengthOrPercentage),
Auto,
Content
}
impl ToCss for LengthOrPercentageOrAutoOrContent {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
LengthOrPercentageOrAutoOrContent::Length(len) => len.to_css(dest),
LengthOrPercentageOrAutoOrContent::Percentage(perc) => perc.to_css(dest),
LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"),
LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content"),
LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest),
}
}
}
impl LengthOrPercentageOrAutoOrContent {
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAutoOrContent, ()> {
let context = AllowedNumericType::NonNegative;
match try!(input.next()) {
Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
Length::parse_dimension(value.value, unit).map(LengthOrPercentageOrAutoOrContent::Length),
Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
Ok(LengthOrPercentageOrAutoOrContent::Percentage(Percentage(value.unit_value))),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentageOrAutoOrContent::Length(Length::Absolute(Au(0)))),
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
Ok(LengthOrPercentageOrAutoOrContent::Auto),
Token::Ident(ref value) if value.eq_ignore_ascii_case("content") =>
Ok(LengthOrPercentageOrAutoOrContent::Content),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage));
Ok(LengthOrPercentageOrAutoOrContent::Calc(calc))
},
_ => Err(())
}
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
impl BorderRadiusSize {
pub fn zero() -> BorderRadiusSize {
let zero = LengthOrPercentage::Length(Length::Absolute(Au(0)));
BorderRadiusSize(Size2D::new(zero, zero))
}
pub fn new(width: LengthOrPercentage, height: LengthOrPercentage) -> BorderRadiusSize {
BorderRadiusSize(Size2D::new(width, height))
}
pub fn circle(radius: LengthOrPercentage) -> BorderRadiusSize {
BorderRadiusSize(Size2D::new(radius, radius))
}
#[inline]
pub fn parse(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
let first = try!(LengthOrPercentage::parse_non_negative(input));
let second = input.try(LengthOrPercentage::parse_non_negative).unwrap_or(first);
Ok(BorderRadiusSize(Size2D::new(first, second)))
}
}
impl ToCss for BorderRadiusSize {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.0.width.to_css(dest));
try!(dest.write_str(" "));
self.0.height.to_css(dest)
}
}
// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position
#[derive(Clone, PartialEq, Copy)]
pub enum PositionComponent {
LengthOrPercentage(LengthOrPercentage),
Center,
Left,
Right,
Top,
Bottom,
}
impl PositionComponent {
pub fn parse(input: &mut Parser) -> Result<PositionComponent, ()> {
input.try(LengthOrPercentage::parse)
.map(PositionComponent::LengthOrPercentage)
.or_else(|()| {
match try!(input.next()) {
Token::Ident(value) => {
match_ignore_ascii_case! { value,
"center" => Ok(PositionComponent::Center),
"left" => Ok(PositionComponent::Left),
"right" => Ok(PositionComponent::Right),
"top" => Ok(PositionComponent::Top),
"bottom" => Ok(PositionComponent::Bottom),
_ => Err(())
}
},
_ => Err(())
}
})
}
#[inline]
pub fn to_length_or_percentage(self) -> LengthOrPercentage {
match self {
PositionComponent::LengthOrPercentage(value) => value,
PositionComponent::Center => LengthOrPercentage::Percentage(Percentage(0.5)),
PositionComponent::Left |
PositionComponent::Top => LengthOrPercentage::Percentage(Percentage(0.0)),
PositionComponent::Right |
PositionComponent::Bottom => LengthOrPercentage::Percentage(Percentage(1.0)),
}
}
}
#[derive(Clone, PartialEq, PartialOrd, Copy, Debug, HeapSizeOf, Deserialize, Serialize)]
pub struct Angle(pub CSSFloat);
impl ToCss for Angle {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
write!(dest, "{}rad", self.0)
}
}
impl Angle {
pub fn radians(self) -> f32 {
let Angle(radians) = self;
radians
}
}
const RAD_PER_DEG: CSSFloat = PI / 180.0;
const RAD_PER_GRAD: CSSFloat = PI / 200.0;
const RAD_PER_TURN: CSSFloat = PI * 2.0;
impl Angle {
/// Parses an angle according to CSS-VALUES § 6.1.
pub fn parse(input: &mut Parser) -> Result<Angle, ()> {
match try!(input.next()) {
Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit),
Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(CalcLengthOrPercentage::parse_angle)
},
_ => Err(())
}
}
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle, ()> {
match_ignore_ascii_case! { unit,
"deg" => Ok(Angle(value * RAD_PER_DEG)),
"grad" => Ok(Angle(value * RAD_PER_GRAD)),
"turn" => Ok(Angle(value * RAD_PER_TURN)),
"rad" => Ok(Angle(value)),
_ => Err(())
}
}
}
/// Specified values for an image according to CSS-IMAGES.
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub enum Image {
Url(Url),
LinearGradient(LinearGradient),
}
impl ToCss for Image {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
Image::Url(ref url) => {
try!(dest.write_str("url(\""));
try!(write!(&mut CssStringWriter::new(dest), "{}", url));
try!(dest.write_str("\")"));
Ok(())
}
Image::LinearGradient(ref gradient) => gradient.to_css(dest)
}
}
}
impl Image {
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Image, ()> {
if let Ok(url) = input.try(|input| input.expect_url()) {
Ok(Image::Url(context.parse_url(&url)))
} else {
match_ignore_ascii_case! { try!(input.expect_function()),
"linear-gradient" => {
Ok(Image::LinearGradient(try!(
input.parse_nested_block(LinearGradient::parse_function))))
},
_ => Err(())
}
}
}
}
/// Specified values for a CSS linear gradient.
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub struct LinearGradient {
/// The angle or corner of the gradient.
pub angle_or_corner: AngleOrCorner,
/// The color stops.
pub stops: Vec<ColorStop>,
}
impl ToCss for LinearGradient {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(dest.write_str("linear-gradient("));
try!(self.angle_or_corner.to_css(dest));
for stop in &self.stops {
try!(dest.write_str(", "));
try!(stop.to_css(dest));
}
try!(dest.write_str(")"));
Ok(())
}
}
/// Specified values for an angle or a corner in a linear gradient.
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum AngleOrCorner {
Angle(Angle),
Corner(HorizontalDirection, VerticalDirection),
}
impl ToCss for AngleOrCorner {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
AngleOrCorner::Angle(angle) => angle.to_css(dest),
AngleOrCorner::Corner(horizontal, vertical) => {
try!(dest.write_str("to "));
try!(horizontal.to_css(dest));
try!(dest.write_str(" "));
try!(vertical.to_css(dest));
Ok(())
}
}
}
}
/// Specified values for one color stop in a linear gradient.
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub struct ColorStop {
/// The color of this stop.
pub color: CSSColor,
/// The position of this stop. If not specified, this stop is placed halfway between the
/// point that precedes it and the point that follows it.
pub position: Option<LengthOrPercentage>,
}
impl ToCss for ColorStop {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.color.to_css(dest));
if let Some(position) = self.position {
try!(dest.write_str(" "));
try!(position.to_css(dest));
}
Ok(())
}
}
define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right);
define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom);
fn parse_one_color_stop(input: &mut Parser) -> Result<ColorStop, ()> {
Ok(ColorStop {
color: try!(CSSColor::parse(input)),
position: input.try(LengthOrPercentage::parse).ok(),
})
}
impl LinearGradient {
/// Parses a linear gradient from the given arguments.
pub fn parse_function(input: &mut Parser) -> Result<LinearGradient, ()> {
let angle_or_corner = if input.try(|input| input.expect_ident_matching("to")).is_ok() {
let (horizontal, vertical) =
if let Ok(value) = input.try(HorizontalDirection::parse) {
(Some(value), input.try(VerticalDirection::parse).ok())
} else {
let value = try!(VerticalDirection::parse(input));
(input.try(HorizontalDirection::parse).ok(), Some(value))
};
try!(input.expect_comma());
match (horizontal, vertical) {
(None, Some(VerticalDirection::Top)) => {
AngleOrCorner::Angle(Angle(0.0))
},
(Some(HorizontalDirection::Right), None) => {
AngleOrCorner::Angle(Angle(PI * 0.5))
},
(None, Some(VerticalDirection::Bottom)) => {
AngleOrCorner::Angle(Angle(PI))
},
(Some(HorizontalDirection::Left), None) => {
AngleOrCorner::Angle(Angle(PI * 1.5))
},
(Some(horizontal), Some(vertical)) => {
AngleOrCorner::Corner(horizontal, vertical)
}
(None, None) => unreachable!(),
}
} else if let Ok(angle) = input.try(Angle::parse) {
try!(input.expect_comma());
AngleOrCorner::Angle(angle)
} else {
AngleOrCorner::Angle(Angle(PI))
};
// Parse the color stops.
let stops = try!(input.parse_comma_separated(parse_one_color_stop));
if stops.len() < 2 {
return Err(())
}
Ok(LinearGradient {
angle_or_corner: angle_or_corner,
stops: stops,
})
}
}
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
input.try(BorderRadiusSize::parse).or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
"thin" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(1.)))),
"medium" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(3.)))),
"thick" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(5.)))),
_ => Err(())
}
})
}
pub fn parse_border_width(input: &mut Parser) -> Result<Length, ()> {
input.try(Length::parse_non_negative).or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
"thin" => Ok(Length::from_px(1.)),
"medium" => Ok(Length::from_px(3.)),
"thick" => Ok(Length::from_px(5.)),
_ => Err(())
}
})
}
// The integer values here correspond to the border conflict resolution rules in CSS 2.1 §
// 17.6.2.1. Higher values override lower values.
define_numbered_css_keyword_enum! { BorderStyle:
"none" => none = -1,
"solid" => solid = 6,
"double" => double = 7,
"dotted" => dotted = 4,
"dashed" => dashed = 5,
"hidden" => hidden = -2,
"groove" => groove = 1,
"ridge" => ridge = 3,
"inset" => inset = 0,
"outset" => outset = 2,
}
impl BorderStyle {
pub fn none_or_hidden(&self) -> bool {
matches!(*self, BorderStyle::none | BorderStyle::hidden)
}
}
/// A time in seconds according to CSS-VALUES § 6.2.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, HeapSizeOf)]
pub struct Time(pub CSSFloat);
impl Time {
/// Returns the time in fractional seconds.
pub fn seconds(self) -> f32 {
let Time(seconds) = self;
seconds
}
/// Parses a time according to CSS-VALUES § 6.2.
fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {
if unit.eq_ignore_ascii_case("s") {
Ok(Time(value))
} else if unit.eq_ignore_ascii_case("ms") {
Ok(Time(value / 1000.0))
} else {
Err(())
}
}
pub fn parse(input: &mut Parser) -> Result<Time, ()> {
match input.next() {
Ok(Token::Dimension(ref value, ref unit)) => {
Time::parse_dimension(value.value, &unit)
}
Ok(Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(CalcLengthOrPercentage::parse_time)
}
_ => Err(())
}
}
}
impl ToComputedValue for Time {
type ComputedValue = Time;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> Time {
*self
}
}
impl ToCss for Time {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
write!(dest, "{}s", self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, HeapSizeOf)]
pub struct Number(pub CSSFloat);
impl Number {
pub fn parse(input: &mut Parser) -> Result<Number, ()> {
parse_number(input).map(Number)
}
fn parse_with_minimum(input: &mut Parser, min: CSSFloat) -> Result<Number, ()> {
match parse_number(input) {
Ok(value) if value < min => Err(()),
value => value.map(Number),
}
}
pub fn parse_non_negative(input: &mut Parser) -> Result<Number, ()> {
Number::parse_with_minimum(input, 0.0)
}
pub fn parse_at_least_one(input: &mut Parser) -> Result<Number, ()> {
Number::parse_with_minimum(input, 1.0)
}
}
impl ToComputedValue for Number {
type ComputedValue = CSSFloat;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> CSSFloat { self.0 }
}
impl ToCss for Number {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, HeapSizeOf)]
pub struct Opacity(pub CSSFloat);
impl Opacity {
pub fn parse(input: &mut Parser) -> Result<Opacity, ()> {
parse_number(input).map(Opacity)
}
}
impl ToComputedValue for Opacity {
type ComputedValue = CSSFloat;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> CSSFloat {
if self.0 < 0.0 {
0.0
} else if self.0 > 1.0 {
1.0
} else {
self.0
}
}
}
impl ToCss for Opacity {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
}
pub mod computed {
use app_units::Au;
use euclid::size::Size2D;
use properties::ComputedValues;
use properties::style_struct_traits::Font;
use std::fmt;
use super::AuExtensionMethods;
use super::specified::AngleOrCorner;
use super::{CSSFloat, specified};
use url::Url;
pub use cssparser::Color as CSSColor;
pub use super::specified::{Angle, BorderStyle, Time};
pub trait TContext {
type ConcreteComputedValues: ComputedValues;
fn is_root_element(&self) -> bool;
fn viewport_size(&self) -> Size2D<Au>;
fn inherited_style(&self) -> &Self::ConcreteComputedValues;
fn style(&self) -> &Self::ConcreteComputedValues;
fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues;
}
pub struct Context<'a, C: ComputedValues> {
pub is_root_element: bool,
pub viewport_size: Size2D<Au>,
pub inherited_style: &'a C,
/// Values access through this need to be in the properties "computed early":
/// color, text-decoration, font-size, display, position, float, border-*-style, outline-style
pub style: C,
}
impl<'a, C: ComputedValues> TContext for Context<'a, C> {
type ConcreteComputedValues = C;
fn is_root_element(&self) -> bool { self.is_root_element }
fn viewport_size(&self) -> Size2D<Au> { self.viewport_size }
fn inherited_style(&self) -> &C { &self.inherited_style }
fn style(&self) -> &C { &self.style }
fn mutate_style(&mut self) -> &mut C { &mut self.style }
}
pub trait ToComputedValue {
type ComputedValue;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> Self::ComputedValue;
}
pub trait ComputedValueAsSpecified {}
impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone {
type ComputedValue = T;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> T {
self.clone()
}
}
impl ToComputedValue for specified::CSSColor {
type ComputedValue = CSSColor;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> CSSColor {
self.parsed
}
}
impl ComputedValueAsSpecified for specified::BorderStyle {}
impl ToComputedValue for specified::Length {
type ComputedValue = Au;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> Au {
match *self {
specified::Length::Absolute(length) => length,
specified::Length::Calc(calc) => calc.to_computed_value(context).length(),
specified::Length::FontRelative(length) =>
length.to_computed_value(context.style().get_font().clone_font_size(),
context.style().root_font_size()),
specified::Length::ViewportPercentage(length) =>
length.to_computed_value(context.viewport_size()),
specified::Length::ServoCharacterWidth(length) =>
length.to_computed_value(context.style().get_font().clone_font_size())
}
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct CalcLengthOrPercentage {
pub length: Option<Au>,
pub percentage: Option<CSSFloat>,
}
impl CalcLengthOrPercentage {
#[inline]
pub fn length(&self) -> Au {
self.length.unwrap_or(Au(0))
}
#[inline]
pub fn percentage(&self) -> CSSFloat {
self.percentage.unwrap_or(0.)
}
}
impl From<LengthOrPercentage> for CalcLengthOrPercentage {
fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage {
match len {
LengthOrPercentage::Percentage(this) => {
CalcLengthOrPercentage {
length: None,
percentage: Some(this),
}
}
LengthOrPercentage::Length(this) => {
CalcLengthOrPercentage {
length: Some(this),
percentage: None,
}
}
LengthOrPercentage::Calc(this) => {
this
}
}
}
}
impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> {
match len {
LengthOrPercentageOrAuto::Percentage(this) => {
Some(CalcLengthOrPercentage {
length: None,
percentage: Some(this),
})
}
LengthOrPercentageOrAuto::Length(this) => {
Some(CalcLengthOrPercentage {
length: Some(this),
percentage: None,
})
}
LengthOrPercentageOrAuto::Calc(this) => {
Some(this)
}
LengthOrPercentageOrAuto::Auto => {
None
}
}
}
}
impl ::cssparser::ToCss for CalcLengthOrPercentage {
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::CalcLengthOrPercentage {
type ComputedValue = CalcLengthOrPercentage;
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> CalcLengthOrPercentage {
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.style().get_font().clone_font_size(), context.style().root_font_size()));
}
}
CalcLengthOrPercentage { length: length, percentage: self.percentage.map(|p| p.0) }
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
impl BorderRadiusSize {
pub fn zero() -> BorderRadiusSize {
BorderRadiusSize(Size2D::new(LengthOrPercentage::Length(Au(0)), LengthOrPercentage::Length(Au(0))))
}
}
impl ToComputedValue for specified::BorderRadiusSize {
type ComputedValue = BorderRadiusSize;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> BorderRadiusSize {
let w = self.0.width.to_computed_value(context);
let h = self.0.height.to_computed_value(context);
BorderRadiusSize(Size2D::new(w, h))
}
}
impl ::cssparser::ToCss for BorderRadiusSize {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.0.width.to_css(dest));
try!(dest.write_str("/"));
self.0.height.to_css(dest)
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentage {
Length(Au),
Percentage(CSSFloat),
Calc(CalcLengthOrPercentage),
}
impl LengthOrPercentage {
#[inline]
pub fn zero() -> LengthOrPercentage {
LengthOrPercentage::Length(Au(0))
}
/// Returns true if the computed value is absolute 0 or 0%.
///
/// (Returns false for calc() values, even if ones that may resolve to zero.)
#[inline]
pub fn is_definitely_zero(&self) -> bool {
use self::LengthOrPercentage::*;
match *self {
Length(Au(0)) | Percentage(0.0) => true,
Length(_) | Percentage(_) | Calc(_) => false
}
}
}
impl fmt::Debug for LengthOrPercentage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentage::Length(length) => write!(f, "{:?}", length),
LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
LengthOrPercentage::Calc(calc) => write!(f, "{:?}", calc),
}
}
}
impl ToComputedValue for specified::LengthOrPercentage {
type ComputedValue = LengthOrPercentage;
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> LengthOrPercentage {
match *self {
specified::LengthOrPercentage::Length(value) => {
LengthOrPercentage::Length(value.to_computed_value(context))
}
specified::LengthOrPercentage::Percentage(value) => {
LengthOrPercentage::Percentage(value.0)
}
specified::LengthOrPercentage::Calc(calc) => {
LengthOrPercentage::Calc(calc.to_computed_value(context))
}
}
}
}
impl ::cssparser::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::Calc(calc) => calc.to_css(dest),
}
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentageOrAuto {
Length(Au),
Percentage(CSSFloat),
Auto,
Calc(CalcLengthOrPercentage),
}
impl LengthOrPercentageOrAuto {
/// Returns true if the computed value is absolute 0 or 0%.
///
/// (Returns false for calc() values, even if ones that may resolve to zero.)
#[inline]
pub fn is_definitely_zero(&self) -> bool {
use self::LengthOrPercentageOrAuto::*;
match *self {
Length(Au(0)) | Percentage(0.0) => true,
Length(_) | Percentage(_) | Calc(_) | Auto => false
}
}
}
impl fmt::Debug for LengthOrPercentageOrAuto {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentageOrAuto::Length(length) => write!(f, "{:?}", length),
LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
LengthOrPercentageOrAuto::Auto => write!(f, "auto"),
LengthOrPercentageOrAuto::Calc(calc) => write!(f, "{:?}", calc),
}
}
}
impl ToComputedValue for specified::LengthOrPercentageOrAuto {
type ComputedValue = LengthOrPercentageOrAuto;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> LengthOrPercentageOrAuto {
match *self {
specified::LengthOrPercentageOrAuto::Length(value) => {
LengthOrPercentageOrAuto::Length(value.to_computed_value(context))
}
specified::LengthOrPercentageOrAuto::Percentage(value) => {
LengthOrPercentageOrAuto::Percentage(value.0)
}
specified::LengthOrPercentageOrAuto::Auto => {
LengthOrPercentageOrAuto::Auto
}
specified::LengthOrPercentageOrAuto::Calc(calc) => {
LengthOrPercentageOrAuto::Calc(calc.to_computed_value(context))
}
}
}
}
impl ::cssparser::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::Auto => dest.write_str("auto"),
LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest),
}
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentageOrAutoOrContent {
Length(Au),
Percentage(CSSFloat),
Calc(CalcLengthOrPercentage),
Auto,
Content
}
impl fmt::Debug for LengthOrPercentageOrAutoOrContent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentageOrAutoOrContent::Length(length) => write!(f, "{:?}", length),
LengthOrPercentageOrAutoOrContent::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
LengthOrPercentageOrAutoOrContent::Calc(calc) => write!(f, "{:?}", calc),
LengthOrPercentageOrAutoOrContent::Auto => write!(f, "auto"),
LengthOrPercentageOrAutoOrContent::Content => write!(f, "content")
}
}
}
impl ToComputedValue for specified::LengthOrPercentageOrAutoOrContent {
type ComputedValue = LengthOrPercentageOrAutoOrContent;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> LengthOrPercentageOrAutoOrContent {
match *self {
specified::LengthOrPercentageOrAutoOrContent::Length(value) => {
LengthOrPercentageOrAutoOrContent::Length(value.to_computed_value(context))
},
specified::LengthOrPercentageOrAutoOrContent::Percentage(value) => {
LengthOrPercentageOrAutoOrContent::Percentage(value.0)
},
specified::LengthOrPercentageOrAutoOrContent::Calc(calc) => {
LengthOrPercentageOrAutoOrContent::Calc(calc.to_computed_value(context))
},
specified::LengthOrPercentageOrAutoOrContent::Auto => {
LengthOrPercentageOrAutoOrContent::Auto
},
specified::LengthOrPercentageOrAutoOrContent::Content => {
LengthOrPercentageOrAutoOrContent::Content
}
}
}
}
impl ::cssparser::ToCss for LengthOrPercentageOrAutoOrContent {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
LengthOrPercentageOrAutoOrContent::Length(length) => length.to_css(dest),
LengthOrPercentageOrAutoOrContent::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest),
LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"),
LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content")
}
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentageOrNone {
Length(Au),
Percentage(CSSFloat),
Calc(CalcLengthOrPercentage),
None,
}
impl fmt::Debug for LengthOrPercentageOrNone {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentageOrNone::Length(length) => write!(f, "{:?}", length),
LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
LengthOrPercentageOrNone::Calc(calc) => write!(f, "{:?}", calc),
LengthOrPercentageOrNone::None => write!(f, "none"),
}
}
}
impl ToComputedValue for specified::LengthOrPercentageOrNone {
type ComputedValue = LengthOrPercentageOrNone;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> LengthOrPercentageOrNone {
match *self {
specified::LengthOrPercentageOrNone::Length(value) => {
LengthOrPercentageOrNone::Length(value.to_computed_value(context))
}
specified::LengthOrPercentageOrNone::Percentage(value) => {
LengthOrPercentageOrNone::Percentage(value.0)
}
specified::LengthOrPercentageOrNone::Calc(calc) => {
LengthOrPercentageOrNone::Calc(calc.to_computed_value(context))
}
specified::LengthOrPercentageOrNone::None => {
LengthOrPercentageOrNone::None
}
}
}
}
impl ::cssparser::ToCss for LengthOrPercentageOrNone {
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::Calc(calc) => calc.to_css(dest),
LengthOrPercentageOrNone::None => dest.write_str("none"),
}
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrNone {
Length(Au),
None,
}
impl fmt::Debug for LengthOrNone {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrNone::Length(length) => write!(f, "{:?}", length),
LengthOrNone::None => write!(f, "none"),
}
}
}
impl ToComputedValue for specified::LengthOrNone {
type ComputedValue = LengthOrNone;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> LengthOrNone {
match *self {
specified::LengthOrNone::Length(specified::Length::Calc(calc)) => {
LengthOrNone::Length(calc.to_computed_value(context).length())
}
specified::LengthOrNone::Length(value) => {
LengthOrNone::Length(value.to_computed_value(context))
}
specified::LengthOrNone::None => {
LengthOrNone::None
}
}
}
}
impl ::cssparser::ToCss for LengthOrNone {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
LengthOrNone::Length(length) => length.to_css(dest),
LengthOrNone::None => dest.write_str("none"),
}
}
}
impl ToComputedValue for specified::Image {
type ComputedValue = Image;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> Image {
match *self {
specified::Image::Url(ref url) => Image::Url(url.clone()),
specified::Image::LinearGradient(ref linear_gradient) => {
Image::LinearGradient(linear_gradient.to_computed_value(context))
}
}
}
}
/// Computed values for an image according to CSS-IMAGES.
#[derive(Clone, PartialEq, HeapSizeOf)]
pub enum Image {
Url(Url),
LinearGradient(LinearGradient),
}
impl fmt::Debug for Image {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Image::Url(ref url) => write!(f, "url(\"{}\")", url),
Image::LinearGradient(ref grad) => write!(f, "linear-gradient({:?})", grad),
}
}
}
/// Computed values for a CSS linear gradient.
#[derive(Clone, PartialEq, HeapSizeOf)]
pub struct LinearGradient {
/// The angle or corner of the gradient.
pub angle_or_corner: AngleOrCorner,
/// The color stops.
pub stops: Vec<ColorStop>,
}
impl ::cssparser::ToCss for LinearGradient {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(dest.write_str("linear-gradient("));
try!(self.angle_or_corner.to_css(dest));
for stop in &self.stops {
try!(dest.write_str(", "));
try!(stop.to_css(dest));
}
try!(dest.write_str(")"));
Ok(())
}
}
impl fmt::Debug for LinearGradient {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f, "{:?}", self.angle_or_corner);
for stop in &self.stops {
let _ = write!(f, ", {:?}", stop);
}
Ok(())
}
}
/// Computed values for one color stop in a linear gradient.
#[derive(Clone, PartialEq, Copy, HeapSizeOf)]
pub struct ColorStop {
/// The color of this stop.
pub color: CSSColor,
/// The position of this stop. If not specified, this stop is placed halfway between the
/// point that precedes it and the point that follows it per CSS-IMAGES § 3.4.
pub position: Option<LengthOrPercentage>,
}
impl ::cssparser::ToCss for ColorStop {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.color.to_css(dest));
if let Some(position) = self.position {
try!(dest.write_str(" "));
try!(position.to_css(dest));
}
Ok(())
}
}
impl fmt::Debug for ColorStop {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f, "{:?}", self.color);
self.position.map(|pos| {
let _ = write!(f, " {:?}", pos);
});
Ok(())
}
}
impl ToComputedValue for specified::LinearGradient {
type ComputedValue = LinearGradient;
#[inline]
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> LinearGradient {
let specified::LinearGradient {
angle_or_corner,
ref stops
} = *self;
LinearGradient {
angle_or_corner: angle_or_corner,
stops: stops.iter().map(|stop| {
ColorStop {
color: stop.color.parsed,
position: match stop.position {
None => None,
Some(value) => Some(value.to_computed_value(context)),
},
}
}).collect()
}
}
}
pub type Length = Au;
pub type Number = CSSFloat;
pub type Opacity = CSSFloat;
}