aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/values.rs2336
-rw-r--r--components/style/values/computed/mod.rs634
-rw-r--r--components/style/values/mod.rs98
-rw-r--r--components/style/values/specified/mod.rs1609
4 files changed, 2341 insertions, 2336 deletions
diff --git a/components/style/values.rs b/components/style/values.rs
deleted file mode 100644
index f3e7682de08..00000000000
--- a/components/style/values.rs
+++ /dev/null
@@ -1,2336 +0,0 @@
-/* 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/. */
-
-//! Common [values][values] used in CSS.
-//!
-//! [values]: https://drafts.csswg.org/css-values/
-
-pub use cssparser::RGBA;
-
-use app_units::Au;
-use cssparser::CssStringWriter;
-use std::fmt::{self, Write};
-use url::Url;
-
-
-/// The real ToCss trait can't be implemented for types in crates that don't
-/// depend on each other.
-pub trait LocalToCss {
- /// 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 LocalToCss for Au {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- write!(dest, "{}px", self.to_f64_px())
- }
-}
-
-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(())
- }
-}
-
-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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf, 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) ),+
- }
- }
- }
- }
-}
-
-pub type CSSFloat = f32;
-
-pub const FONT_MEDIUM_PX: i32 = 16;
-
-pub trait HasViewportPercentage {
- fn has_viewport_percentage(&self) -> bool;
-}
-
-pub trait NoViewportPercentage {}
-
-impl<T> HasViewportPercentage for T where T: NoViewportPercentage {
- fn has_viewport_percentage(&self) -> bool {
- false
- }
-}
-
-pub mod specified {
- use app_units::Au;
- use cssparser::{self, Parser, ToCss, Token};
- use euclid::size::Size2D;
- #[cfg(feature = "gecko")]
- use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
- use parser::{ParserContext, ParserContextExtraData};
- use std::ascii::AsciiExt;
- use std::cmp;
- use std::f32::consts::PI;
- use std::fmt;
- use std::ops::Mul;
- use style_traits::values::specified::AllowedNumericType;
- use super::computed::{Context, ToComputedValue};
- use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewportPercentage};
- use url::Url;
-
- impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order
-
- #[derive(Clone, PartialEq, Debug)]
- #[cfg_attr(feature = "servo", derive(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 NoViewportPercentage for CSSColor {}
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub struct CSSRGBA {
- pub parsed: cssparser::RGBA,
- pub authored: Option<String>,
- }
-
- impl NoViewportPercentage for CSSRGBA {}
-
- 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)]
- #[cfg_attr(feature = "servo", derive(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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum ViewportPercentageLength {
- Vw(CSSFloat),
- Vh(CSSFloat),
- Vmin(CSSFloat),
- Vmax(CSSFloat)
- }
-
- impl HasViewportPercentage for ViewportPercentageLength {
- fn has_viewport_percentage(&self) -> bool {
- true
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(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)]
- #[cfg_attr(feature = "servo", derive(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 HasViewportPercentage for Length {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- Length::ViewportPercentage(_) => true,
- _ => false
- }
- }
- }
-
- 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(ref s) => {
- let sum = &**s * scalar;
- SimplifiedValueNode::Sum(Box::new(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)]
- #[cfg_attr(feature = "servo", derive(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::new(result)))
- },
- _ => Err(())
- }
- }
-
- fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> {
- match *node {
- CalcValueNode::Number(number) => Some(number),
- CalcValueNode::Sum(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(ref sum) => simplified.extend_from_slice(&sum.values),
- val => simplified.push(val),
- }
- }
-
- if simplified.len() == 1 {
- Ok(simplified[0].clone())
- } else {
- Ok(SimplifiedValueNode::Sum(Box::new(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(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)]
- #[cfg_attr(feature = "servo", derive(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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum LengthOrPercentage {
- Length(Length),
- Percentage(Percentage),
- Calc(CalcLengthOrPercentage),
- }
-
- impl HasViewportPercentage for LengthOrPercentage {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- LengthOrPercentage::Length(length) => length.has_viewport_percentage(),
- _ => false
- }
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum LengthOrPercentageOrAuto {
- Length(Length),
- Percentage(Percentage),
- Auto,
- Calc(CalcLengthOrPercentage),
- }
-
- impl HasViewportPercentage for LengthOrPercentageOrAuto {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- LengthOrPercentageOrAuto::Length(length) => length.has_viewport_percentage(),
- _ => false
- }
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum LengthOrPercentageOrNone {
- Length(Length),
- Percentage(Percentage),
- Calc(CalcLengthOrPercentage),
- None,
- }
-
- impl HasViewportPercentage for LengthOrPercentageOrNone {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- LengthOrPercentageOrNone::Length(length) => length.has_viewport_percentage(),
- _ => false
- }
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum LengthOrNone {
- Length(Length),
- None,
- }
-
- impl HasViewportPercentage for LengthOrNone {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- LengthOrNone::Length(length) => length.has_viewport_percentage(),
- _ => false
- }
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum LengthOrPercentageOrAutoOrContent {
- Length(Length),
- Percentage(Percentage),
- Calc(CalcLengthOrPercentage),
- Auto,
- Content
- }
-
- impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- LengthOrPercentageOrAutoOrContent::Length(length) => length.has_viewport_percentage(),
- _ => false
- }
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
-
- impl NoViewportPercentage for BorderRadiusSize {}
-
- 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 HasViewportPercentage for PositionComponent {
- fn has_viewport_percentage(&self) -> bool {
- match *self {
- PositionComponent::LengthOrPercentage(length) => length.has_viewport_percentage(),
- _ => false
- }
- }
- }
-
- 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
- /// An angle, normalized to radians.
- 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 {
- #[inline]
- pub fn radians(self) -> f32 {
- self.0
- }
-
- #[inline]
- pub fn from_radians(r: f32) -> Self {
- Angle(r)
- }
- }
-
- 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(())
- }
- }
- }
-
- #[derive(PartialEq, Clone, Debug)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub struct UrlExtraData {
- #[cfg(feature = "gecko")]
- pub base: GeckoArcURI,
- #[cfg(feature = "gecko")]
- pub referrer: GeckoArcURI,
- #[cfg(feature = "gecko")]
- pub principal: GeckoArcPrincipal,
- }
-
- impl UrlExtraData {
- #[cfg(feature = "servo")]
- pub fn make_from(content: &ParserContext) -> Option<UrlExtraData> {
- Some(UrlExtraData { })
- }
-
- #[cfg(feature = "gecko")]
- pub fn make_from(context: &ParserContext) -> Option<UrlExtraData> {
- match context.extra_data {
- ParserContextExtraData {
- base: Some(ref base),
- referrer: Some(ref referrer),
- principal: Some(ref principal),
- } => {
- Some(UrlExtraData {
- base: base.clone(),
- referrer: referrer.clone(),
- principal: principal.clone(),
- })
- },
- _ => None,
- }
- }
- }
-
- /// Specified values for an image according to CSS-IMAGES.
- #[derive(Clone, PartialEq, Debug)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum Image {
- Url(Url, UrlExtraData),
- LinearGradient(LinearGradient),
- }
-
- impl ToCss for Image {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- use values::LocalToCss;
- match *self {
- Image::Url(ref url, ref _extra_data) => {
- url.to_css(dest)
- }
- 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()) {
- match UrlExtraData::make_from(context) {
- Some(extra_data) => {
- Ok(Image::Url(context.parse_url(&url), extra_data))
- },
- None => {
- // FIXME(heycam) should ensure we always have a principal, etc., when
- // parsing style attributes and re-parsing due to CSS Variables.
- println!("stylo: skipping declaration without ParserContextExtraData");
- Err(())
- },
- }
- } 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)]
- #[cfg_attr(feature = "servo", derive(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)]
- #[cfg_attr(feature = "servo", derive(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)]
- #[cfg_attr(feature = "servo", derive(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 NoViewportPercentage for BorderStyle {}
-
- 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, _: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub struct Number(pub CSSFloat);
-
- impl NoViewportPercentage for Number {}
-
- 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(&self, _: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub struct Opacity(pub CSSFloat);
-
- impl NoViewportPercentage for Opacity {}
-
- 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(&self, _: &Context) -> 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 std::fmt;
- use super::LocalToCss;
- use super::specified::AngleOrCorner;
- use super::{CSSFloat, specified};
- use url::Url;
- pub use cssparser::Color as CSSColor;
- pub use super::specified::{Angle, BorderStyle, Time, UrlExtraData};
-
- pub struct Context<'a> {
- pub is_root_element: bool,
- pub viewport_size: Size2D<Au>,
- pub inherited_style: &'a ComputedValues,
-
- /// 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: ComputedValues,
- }
-
- impl<'a> Context<'a> {
- pub fn is_root_element(&self) -> bool { self.is_root_element }
- pub fn viewport_size(&self) -> Size2D<Au> { self.viewport_size }
- pub fn inherited_style(&self) -> &ComputedValues { &self.inherited_style }
- pub fn style(&self) -> &ComputedValues { &self.style }
- pub fn mutate_style(&mut self) -> &mut ComputedValues { &mut self.style }
- }
-
- pub trait ToComputedValue {
- type ComputedValue;
-
- #[inline]
- fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue;
- }
-
- pub trait ComputedValueAsSpecified {}
-
- impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone {
- type ComputedValue = T;
-
- #[inline]
- fn to_computed_value(&self, _context: &Context) -> T {
- self.clone()
- }
- }
-
- impl ToComputedValue for specified::CSSColor {
- type ComputedValue = CSSColor;
-
- #[inline]
- fn to_computed_value(&self, _context: &Context) -> CSSColor {
- self.parsed
- }
- }
-
- impl ComputedValueAsSpecified for specified::BorderStyle {}
-
- impl ToComputedValue for specified::Length {
- type ComputedValue = Au;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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(Debug, PartialEq, Clone, Copy)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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(&self, context: &Context) -> Image {
- match *self {
- specified::Image::Url(ref url, ref extra_data) => {
- Image::Url(url.clone(), extra_data.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)]
- #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
- pub enum Image {
- Url(Url, UrlExtraData),
- LinearGradient(LinearGradient),
- }
-
- impl fmt::Debug for Image {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Image::Url(ref url, ref _extra_data) => write!(f, "url(\"{}\")", url),
- Image::LinearGradient(ref grad) => write!(f, "linear-gradient({:?})", grad),
- }
- }
- }
-
- /// Computed values for a CSS linear gradient.
- #[derive(Clone, PartialEq)]
- #[cfg_attr(feature = "servo", derive(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)]
- #[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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;
-}
diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs
new file mode 100644
index 00000000000..cefe1c7d6ac
--- /dev/null
+++ b/components/style/values/computed/mod.rs
@@ -0,0 +1,634 @@
+/* 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/. */
+
+use app_units::Au;
+use euclid::size::Size2D;
+use properties::ComputedValues;
+use std::fmt;
+use super::LocalToCss;
+use super::specified::AngleOrCorner;
+use super::{CSSFloat, specified};
+use url::Url;
+pub use cssparser::Color as CSSColor;
+pub use super::specified::{Angle, BorderStyle, Time, UrlExtraData};
+
+pub struct Context<'a> {
+ pub is_root_element: bool,
+ pub viewport_size: Size2D<Au>,
+ pub inherited_style: &'a ComputedValues,
+
+ /// 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: ComputedValues,
+}
+
+impl<'a> Context<'a> {
+ pub fn is_root_element(&self) -> bool { self.is_root_element }
+ pub fn viewport_size(&self) -> Size2D<Au> { self.viewport_size }
+ pub fn inherited_style(&self) -> &ComputedValues { &self.inherited_style }
+ pub fn style(&self) -> &ComputedValues { &self.style }
+ pub fn mutate_style(&mut self) -> &mut ComputedValues { &mut self.style }
+}
+
+pub trait ToComputedValue {
+ type ComputedValue;
+
+ #[inline]
+ fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue;
+}
+
+pub trait ComputedValueAsSpecified {}
+
+impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone {
+ type ComputedValue = T;
+
+ #[inline]
+ fn to_computed_value(&self, _context: &Context) -> T {
+ self.clone()
+ }
+}
+
+impl ToComputedValue for specified::CSSColor {
+ type ComputedValue = CSSColor;
+
+ #[inline]
+ fn to_computed_value(&self, _context: &Context) -> CSSColor {
+ self.parsed
+ }
+}
+
+impl ComputedValueAsSpecified for specified::BorderStyle {}
+
+impl ToComputedValue for specified::Length {
+ type ComputedValue = Au;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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(Debug, PartialEq, Clone, Copy)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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(&self, context: &Context) -> Image {
+ match *self {
+ specified::Image::Url(ref url, ref extra_data) => {
+ Image::Url(url.clone(), extra_data.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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum Image {
+ Url(Url, UrlExtraData),
+ LinearGradient(LinearGradient),
+}
+
+impl fmt::Debug for Image {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Image::Url(ref url, ref _extra_data) => write!(f, "url(\"{}\")", url),
+ Image::LinearGradient(ref grad) => write!(f, "linear-gradient({:?})", grad),
+ }
+ }
+}
+
+/// Computed values for a CSS linear gradient.
+#[derive(Clone, PartialEq)]
+#[cfg_attr(feature = "servo", derive(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)]
+#[cfg_attr(feature = "servo", derive(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(&self, context: &Context) -> 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;
diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs
new file mode 100644
index 00000000000..e926685f11b
--- /dev/null
+++ b/components/style/values/mod.rs
@@ -0,0 +1,98 @@
+/* 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/. */
+
+//! Common [values][values] used in CSS.
+//!
+//! [values]: https://drafts.csswg.org/css-values/
+
+pub use cssparser::RGBA;
+
+use app_units::Au;
+use cssparser::CssStringWriter;
+use std::fmt::{self, Write};
+use url::Url;
+
+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)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf, 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) ),+
+ }
+ }
+ }
+ }
+}
+
+pub mod computed;
+pub mod specified;
+
+/// The real ToCss trait can't be implemented for types in crates that don't
+/// depend on each other.
+pub trait LocalToCss {
+ /// 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 LocalToCss for Au {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ write!(dest, "{}px", self.to_f64_px())
+ }
+}
+
+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 trait HasViewportPercentage {
+ fn has_viewport_percentage(&self) -> bool;
+}
+
+pub trait NoViewportPercentage {}
+
+impl<T> HasViewportPercentage for T where T: NoViewportPercentage {
+ fn has_viewport_percentage(&self) -> bool {
+ false
+ }
+}
+
diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs
new file mode 100644
index 00000000000..f8775c4d9e1
--- /dev/null
+++ b/components/style/values/specified/mod.rs
@@ -0,0 +1,1609 @@
+/* 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/. */
+
+use app_units::Au;
+use cssparser::{self, Parser, ToCss, Token};
+use euclid::size::Size2D;
+#[cfg(feature = "gecko")]
+use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
+use parser::{ParserContext, ParserContextExtraData};
+use std::ascii::AsciiExt;
+use std::cmp;
+use std::f32::consts::PI;
+use std::fmt;
+use std::ops::Mul;
+use style_traits::values::specified::AllowedNumericType;
+use super::computed::{Context, ToComputedValue};
+use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewportPercentage};
+use url::Url;
+
+impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order
+
+#[derive(Clone, PartialEq, Debug)]
+#[cfg_attr(feature = "servo", derive(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 NoViewportPercentage for CSSColor {}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct CSSRGBA {
+ pub parsed: cssparser::RGBA,
+ pub authored: Option<String>,
+}
+
+impl NoViewportPercentage for CSSRGBA {}
+
+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)]
+#[cfg_attr(feature = "servo", derive(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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum ViewportPercentageLength {
+ Vw(CSSFloat),
+ Vh(CSSFloat),
+ Vmin(CSSFloat),
+ Vmax(CSSFloat)
+}
+
+impl HasViewportPercentage for ViewportPercentageLength {
+ fn has_viewport_percentage(&self) -> bool {
+ true
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(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)]
+#[cfg_attr(feature = "servo", derive(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 HasViewportPercentage for Length {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ Length::ViewportPercentage(_) => true,
+ _ => false
+ }
+ }
+}
+
+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(ref s) => {
+ let sum = &**s * scalar;
+ SimplifiedValueNode::Sum(Box::new(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)]
+#[cfg_attr(feature = "servo", derive(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::new(result)))
+ },
+ _ => Err(())
+ }
+ }
+
+ fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> {
+ match *node {
+ CalcValueNode::Number(number) => Some(number),
+ CalcValueNode::Sum(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(ref sum) => simplified.extend_from_slice(&sum.values),
+ val => simplified.push(val),
+ }
+ }
+
+ if simplified.len() == 1 {
+ Ok(simplified[0].clone())
+ } else {
+ Ok(SimplifiedValueNode::Sum(Box::new(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(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)]
+#[cfg_attr(feature = "servo", derive(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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum LengthOrPercentage {
+ Length(Length),
+ Percentage(Percentage),
+ Calc(CalcLengthOrPercentage),
+}
+
+impl HasViewportPercentage for LengthOrPercentage {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ LengthOrPercentage::Length(length) => length.has_viewport_percentage(),
+ _ => false
+ }
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum LengthOrPercentageOrAuto {
+ Length(Length),
+ Percentage(Percentage),
+ Auto,
+ Calc(CalcLengthOrPercentage),
+}
+
+impl HasViewportPercentage for LengthOrPercentageOrAuto {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ LengthOrPercentageOrAuto::Length(length) => length.has_viewport_percentage(),
+ _ => false
+ }
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum LengthOrPercentageOrNone {
+ Length(Length),
+ Percentage(Percentage),
+ Calc(CalcLengthOrPercentage),
+ None,
+}
+
+impl HasViewportPercentage for LengthOrPercentageOrNone {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ LengthOrPercentageOrNone::Length(length) => length.has_viewport_percentage(),
+ _ => false
+ }
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum LengthOrNone {
+ Length(Length),
+ None,
+}
+
+impl HasViewportPercentage for LengthOrNone {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ LengthOrNone::Length(length) => length.has_viewport_percentage(),
+ _ => false
+ }
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum LengthOrPercentageOrAutoOrContent {
+ Length(Length),
+ Percentage(Percentage),
+ Calc(CalcLengthOrPercentage),
+ Auto,
+ Content
+}
+
+impl HasViewportPercentage for LengthOrPercentageOrAutoOrContent {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ LengthOrPercentageOrAutoOrContent::Length(length) => length.has_viewport_percentage(),
+ _ => false
+ }
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
+
+impl NoViewportPercentage for BorderRadiusSize {}
+
+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 HasViewportPercentage for PositionComponent {
+ fn has_viewport_percentage(&self) -> bool {
+ match *self {
+ PositionComponent::LengthOrPercentage(length) => length.has_viewport_percentage(),
+ _ => false
+ }
+ }
+}
+
+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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
+/// An angle, normalized to radians.
+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 {
+ #[inline]
+ pub fn radians(self) -> f32 {
+ self.0
+ }
+
+ #[inline]
+ pub fn from_radians(r: f32) -> Self {
+ Angle(r)
+ }
+}
+
+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(())
+ }
+ }
+}
+
+#[derive(PartialEq, Clone, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct UrlExtraData {
+ #[cfg(feature = "gecko")]
+ pub base: GeckoArcURI,
+ #[cfg(feature = "gecko")]
+ pub referrer: GeckoArcURI,
+ #[cfg(feature = "gecko")]
+ pub principal: GeckoArcPrincipal,
+}
+
+impl UrlExtraData {
+ #[cfg(feature = "servo")]
+ pub fn make_from(content: &ParserContext) -> Option<UrlExtraData> {
+ Some(UrlExtraData { })
+ }
+
+ #[cfg(feature = "gecko")]
+ pub fn make_from(context: &ParserContext) -> Option<UrlExtraData> {
+ match context.extra_data {
+ ParserContextExtraData {
+ base: Some(ref base),
+ referrer: Some(ref referrer),
+ principal: Some(ref principal),
+ } => {
+ Some(UrlExtraData {
+ base: base.clone(),
+ referrer: referrer.clone(),
+ principal: principal.clone(),
+ })
+ },
+ _ => None,
+ }
+ }
+}
+
+/// Specified values for an image according to CSS-IMAGES.
+#[derive(Clone, PartialEq, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub enum Image {
+ Url(Url, UrlExtraData),
+ LinearGradient(LinearGradient),
+}
+
+impl ToCss for Image {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ use values::LocalToCss;
+ match *self {
+ Image::Url(ref url, ref _extra_data) => {
+ url.to_css(dest)
+ }
+ 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()) {
+ match UrlExtraData::make_from(context) {
+ Some(extra_data) => {
+ Ok(Image::Url(context.parse_url(&url), extra_data))
+ },
+ None => {
+ // FIXME(heycam) should ensure we always have a principal, etc., when
+ // parsing style attributes and re-parsing due to CSS Variables.
+ println!("stylo: skipping declaration without ParserContextExtraData");
+ Err(())
+ },
+ }
+ } 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)]
+#[cfg_attr(feature = "servo", derive(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)]
+#[cfg_attr(feature = "servo", derive(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)]
+#[cfg_attr(feature = "servo", derive(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 NoViewportPercentage for BorderStyle {}
+
+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)]
+#[cfg_attr(feature = "servo", derive(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(&self, _: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Number(pub CSSFloat);
+
+impl NoViewportPercentage for Number {}
+
+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(&self, _: &Context) -> 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)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Opacity(pub CSSFloat);
+
+impl NoViewportPercentage for Opacity {}
+
+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(&self, _: &Context) -> 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)
+ }
+}