servo/components/style/properties.mako.rs
Patrick Walton 4b9cd4e65d layout: Implement <table width> and <center>.
Improves Hacker News.
2015-05-11 12:53:45 -07:00

5858 lines
226 KiB
Rust

/* 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/. */
// This file is a Mako template: http://www.makotemplates.org/
#![macro_use]
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::default::Default;
use std::fmt;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use util::fnv::FnvHasher;
use util::logical_geometry::{LogicalMargin, PhysicalSide, WritingMode};
use util::geometry::Au;
use url::Url;
use cssparser::{Parser, Color, RGBA, AtRuleParser, DeclarationParser,
DeclarationListParser, parse_important, ToCss};
use geom::SideOffsets2D;
use geom::size::Size2D;
use values::specified::{Length, BorderStyle};
use values::computed::{self, ToComputedValue};
use selectors::matching::DeclarationBlock;
use parser::{ParserContext, log_css_error};
use stylesheets::Origin;
use computed_values;
use self::property_bit_field::PropertyBitField;
<%!
import re
def to_rust_ident(name):
name = name.replace("-", "_")
if name in ["static", "super", "box", "move"]: # Rust keywords
name += "_"
return name
def to_camel_case(ident):
return re.sub("_([a-z])", lambda m: m.group(1).upper(), ident.strip("_").capitalize())
class Longhand(object):
def __init__(self, name, derived_from=None, experimental=False):
self.name = name
self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
self.style_struct = THIS_STYLE_STRUCT
self.experimental = experimental
if derived_from is None:
self.derived_from = None
else:
self.derived_from = [ to_rust_ident(name) for name in derived_from ]
class Shorthand(object):
def __init__(self, name, sub_properties, experimental=False):
self.name = name
self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
self.derived_from = None
self.experimental = experimental
self.sub_properties = [LONGHANDS_BY_NAME[s] for s in sub_properties]
class StyleStruct(object):
def __init__(self, name, inherited):
self.name = name
self.ident = to_rust_ident(name.lower())
self.longhands = []
self.inherited = inherited
STYLE_STRUCTS = []
THIS_STYLE_STRUCT = None
LONGHANDS = []
LONGHANDS_BY_NAME = {}
DERIVED_LONGHANDS = {}
SHORTHANDS = []
def new_style_struct(name, is_inherited):
global THIS_STYLE_STRUCT
style_struct = StyleStruct(name, is_inherited)
STYLE_STRUCTS.append(style_struct)
THIS_STYLE_STRUCT = style_struct
return ""
def switch_to_style_struct(name):
global THIS_STYLE_STRUCT
for style_struct in STYLE_STRUCTS:
if style_struct.name == name:
THIS_STYLE_STRUCT = style_struct
return ""
fail()
%>
pub mod longhands {
<%def name="raw_longhand(name, derived_from=None, experimental=False)">
<%
if derived_from is not None:
derived_from = derived_from.split()
property = Longhand(name, derived_from=derived_from, experimental=experimental)
THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property
if derived_from is not None:
for name in derived_from:
DERIVED_LONGHANDS.setdefault(name, []).append(property)
%>
pub mod ${property.ident} {
% if derived_from is None:
use cssparser::Parser;
use parser::ParserContext;
use properties::{CSSWideKeyword, DeclaredValue};
% endif
#[allow(unused_imports)]
use values::{computed, specified};
${caller.body()}
% if derived_from is None:
pub fn parse_declared(context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
match input.try(CSSWideKeyword::parse) {
Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit),
Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial),
Ok(CSSWideKeyword::UnsetKeyword) => Ok(DeclaredValue::${
"Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"}),
Err(()) => parse_specified(context, input),
}
}
% endif
}
</%def>
<%def name="longhand(name, derived_from=None, experimental=False)">
<%self:raw_longhand name="${name}" derived_from="${derived_from}"
experimental="${experimental}">
${caller.body()}
% if derived_from is None:
pub fn parse_specified(context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
parse(context, input).map(DeclaredValue::SpecifiedValue)
}
% endif
</%self:raw_longhand>
</%def>
<%def name="single_keyword_computed(name, values, experimental=False)">
<%self:longhand name="${name}" experimental="${experimental}">
pub use self::computed_value::T as SpecifiedValue;
${caller.body()}
pub mod computed_value {
define_css_keyword_enum! { T:
% for value in values.split():
"${value}" => ${to_rust_ident(value)},
% endfor
}
}
#[inline] pub fn get_initial_value() -> computed_value::T {
computed_value::T::${to_rust_ident(values.split()[0])}
}
pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
computed_value::T::parse(input)
}
</%self:longhand>
</%def>
<%def name="single_keyword(name, values, experimental=False)">
<%self:single_keyword_computed name="${name}"
values="${values}"
experimental="${experimental}">
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
</%self:single_keyword_computed>
</%def>
<%def name="predefined_type(name, type, initial_value, parse_method='parse')">
<%self:longhand name="${name}">
#[allow(unused_imports)]
use util::geometry::Au;
pub type SpecifiedValue = specified::${type};
pub mod computed_value {
pub use values::computed::${type} as T;
}
#[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
#[inline] pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
specified::${type}::${parse_method}(input)
}
</%self:longhand>
</%def>
// CSS 2.1, Section 8 - Box model
${new_style_struct("Margin", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Length(Au(0))")}
% endfor
${new_style_struct("Padding", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("padding-" + side, "LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
% endfor
${new_style_struct("Border", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("border-%s-color" % side, "CSSColor", "::cssparser::Color::CurrentColor")}
% endfor
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("border-%s-style" % side, "BorderStyle", "specified::BorderStyle::none")}
% endfor
% for side in ["top", "right", "bottom", "left"]:
<%self:longhand name="border-${side}-width">
use values::computed::{ToComputedValue, Context};
use util::geometry::Au;
use cssparser::ToCss;
use std::fmt;
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
#[inline]
pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
specified::parse_border_width(input).map(SpecifiedValue)
}
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(pub specified::Length);
pub mod computed_value {
use util::geometry::Au;
pub type T = Au;
}
#[inline] pub fn get_initial_value() -> computed_value::T {
Au::from_px(3) // medium
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
if !context.border_${side}_present {
Au(0)
} else {
self.0.to_computed_value(context)
}
}
}
</%self:longhand>
% endfor
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
${predefined_type("border-" + corner + "-radius", "LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
% endfor
${new_style_struct("Outline", is_inherited=False)}
// TODO(pcwalton): `invert`
${predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor")}
<%self:longhand name="outline-style">
pub use values::specified::BorderStyle as SpecifiedValue;
pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none }
pub mod computed_value {
pub use values::specified::BorderStyle as T;
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
match SpecifiedValue::parse(input) {
Ok(SpecifiedValue::hidden) => Err(()),
result => result
}
}
</%self:longhand>
<%self:longhand name="outline-width">
pub use super::border_top_width::get_initial_value;
pub type SpecifiedValue = specified::Length;
pub mod computed_value {
pub use util::geometry::Au as T;
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
specified::parse_border_width(input)
}
</%self:longhand>
${predefined_type("outline-offset", "Length", "Au(0)")}
${new_style_struct("PositionOffsets", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type(side, "LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto")}
% endfor
// CSS 2.1, Section 9 - Visual formatting model
${new_style_struct("Box", is_inherited=False)}
// TODO(SimonSapin): don't parse `inline-table`, since we don't support it
<%self:single_keyword_computed name="display"
values="inline block inline-block
table inline-table table-row-group table-header-group table-footer-group
table-row table-column-group table-column table-cell table-caption
list-item
none">
use values::computed::{ToComputedValue, Context};
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
use self::computed_value::T;
// if context.is_root_element && value == list_item {
// return block
// }
if context.positioned || context.floated || context.is_root_element {
match *self {
T::inline_table => T::table,
T::inline | T::inline_block |
T::table_row_group | T::table_column |
T::table_column_group | T::table_header_group |
T::table_footer_group | T::table_row | T::table_cell |
T::table_caption
=> T::block,
_ => *self,
}
} else {
*self
}
}
}
</%self:single_keyword_computed>
${single_keyword("position", "static absolute relative fixed")}
${single_keyword("float", "none left right")}
${single_keyword("clear", "none left right both")}
<%self:longhand name="-servo-display-for-hypothetical-box" derived_from="display">
pub use super::display::{SpecifiedValue, get_initial_value};
pub use super::display::{parse};
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn derive_from_display(_: super::display::computed_value::T,
context: &computed::Context)
-> computed_value::T {
context.display
}
</%self:longhand>
<%self:longhand name="z-index">
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
pub type SpecifiedValue = computed_value::T;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
#[derive(PartialEq, Clone, Eq, Copy, Debug)]
pub enum T {
Auto,
Number(i32),
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&T::Auto => dest.write_str("auto"),
&T::Number(number) => write!(dest, "{}", number),
}
}
}
impl T {
pub fn number_or_zero(self) -> i32 {
match self {
T::Auto => 0,
T::Number(value) => value,
}
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::Auto
}
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
Ok(computed_value::T::Auto)
} else {
Ok(computed_value::T::Number(try!(input.expect_integer()) as i32))
}
}
</%self:longhand>
${new_style_struct("InheritedBox", is_inherited=True)}
${single_keyword("direction", "ltr rtl", experimental=True)}
// CSS 2.1, Section 10 - Visual formatting model details
${switch_to_style_struct("Box")}
${predefined_type("width", "LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto",
"parse_non_negative")}
<%self:longhand name="height">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(pub specified::LengthOrPercentageOrAuto);
pub mod computed_value {
pub use values::computed::LengthOrPercentageOrAuto as T;
}
#[inline]
pub fn get_initial_value() -> computed_value::T { computed::LengthOrPercentageOrAuto::Auto }
#[inline]
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
specified::LengthOrPercentageOrAuto::parse_non_negative(input).map(SpecifiedValue)
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match (self.0, context.inherited_height) {
(specified::LengthOrPercentageOrAuto::Percentage(_),
computed::LengthOrPercentageOrAuto::Auto)
if !context.is_root_element && !context.positioned => {
computed::LengthOrPercentageOrAuto::Auto
},
_ => self.0.to_computed_value(context)
}
}
}
</%self:longhand>
${predefined_type("min-width", "LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
${predefined_type("max-width", "LengthOrPercentageOrNone",
"computed::LengthOrPercentageOrNone::None",
"parse_non_negative")}
${predefined_type("min-height", "LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
${predefined_type("max-height", "LengthOrPercentageOrNone",
"computed::LengthOrPercentageOrNone::None",
"parse_non_negative")}
${switch_to_style_struct("InheritedBox")}
<%self:longhand name="line-height">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
use values::CSSFloat;
#[derive(Clone, PartialEq, Copy)]
pub enum SpecifiedValue {
Normal,
Length(specified::Length),
Number(CSSFloat),
Percentage(CSSFloat),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&SpecifiedValue::Normal => dest.write_str("normal"),
&SpecifiedValue::Length(length) => length.to_css(dest),
&SpecifiedValue::Number(number) => write!(dest, "{}", number),
&SpecifiedValue::Percentage(number) => write!(dest, "{}%", number * 100.),
}
}
}
/// normal | <number> | <length> | <percentage>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
use std::ascii::AsciiExt;
use cssparser::Token;
match try!(input.next()) {
Token::Number(ref value) if value.value >= 0. => {
Ok(SpecifiedValue::Number(value.value))
}
Token::Percentage(ref value) if value.unit_value >= 0. => {
Ok(SpecifiedValue::Percentage(value.unit_value))
}
Token::Dimension(ref value, ref unit) if value.value >= 0. => {
specified::Length::parse_dimension(value.value, unit)
.map(SpecifiedValue::Length)
}
Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => {
Ok(SpecifiedValue::Normal)
}
_ => Err(()),
}
}
pub mod computed_value {
use values::CSSFloat;
use util::geometry::Au;
use std::fmt;
#[derive(PartialEq, Copy, Clone)]
pub enum T {
Normal,
Length(Au),
Number(CSSFloat),
}
impl fmt::Debug for T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&T::Normal => write!(f, "normal"),
&T::Length(length) => write!(f, "{:?}%", length),
&T::Number(number) => write!(f, "{}", number),
}
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T { computed_value::T::Normal }
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::Normal => computed_value::T::Normal,
SpecifiedValue::Length(value) => {
computed_value::T::Length(value.to_computed_value(context))
}
SpecifiedValue::Number(value) => computed_value::T::Number(value),
SpecifiedValue::Percentage(value) => {
computed_value::T::Length(
specified::Length::FontRelative(specified::FontRelativeLength::Em(value)).to_computed_value(context))
}
}
}
}
</%self:longhand>
${switch_to_style_struct("Box")}
<%self:longhand name="vertical-align">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
<% vertical_align_keywords = (
"baseline sub super top text-top middle bottom text-bottom".split()) %>
#[allow(non_camel_case_types)]
#[derive(Clone, PartialEq, Copy)]
pub enum SpecifiedValue {
% for keyword in vertical_align_keywords:
${to_rust_ident(keyword)},
% endfor
LengthOrPercentage(specified::LengthOrPercentage),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
% for keyword in vertical_align_keywords:
&SpecifiedValue::${to_rust_ident(keyword)} => dest.write_str("${keyword}"),
% endfor
&SpecifiedValue::LengthOrPercentage(value) => value.to_css(dest),
}
}
}
/// baseline | sub | super | top | text-top | middle | bottom | text-bottom
/// | <percentage> | <length>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
input.try(specified::LengthOrPercentage::parse)
.map(SpecifiedValue::LengthOrPercentage)
.or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
% for keyword in vertical_align_keywords[:-1]:
"${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}),
% endfor
// Hack to work around quirks of macro_rules parsing in match_ignore_ascii_case!
% for keyword in vertical_align_keywords[-1:]:
"${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)})
% endfor
_ => Err(())
}
})
}
pub mod computed_value {
use values::CSSFloat;
use util::geometry::Au;
use std::fmt;
#[allow(non_camel_case_types)]
#[derive(PartialEq, Copy, Clone)]
pub enum T {
% for keyword in vertical_align_keywords:
${to_rust_ident(keyword)},
% endfor
Length(Au),
Percentage(CSSFloat),
}
impl fmt::Debug for T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
% for keyword in vertical_align_keywords:
&T::${to_rust_ident(keyword)} => write!(f, "${keyword}"),
% endfor
&T::Length(length) => write!(f, "{:?}", length),
&T::Percentage(number) => write!(f, "{}%", number),
}
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T { computed_value::T::baseline }
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
% for keyword in vertical_align_keywords:
SpecifiedValue::${to_rust_ident(keyword)} => {
computed_value::T::${to_rust_ident(keyword)}
}
% endfor
SpecifiedValue::LengthOrPercentage(value) => {
match value.to_computed_value(context) {
computed::LengthOrPercentage::Length(value) => {
computed_value::T::Length(value)
}
computed::LengthOrPercentage::Percentage(value) => {
computed_value::T::Percentage(value)
}
}
}
}
}
}
</%self:longhand>
// CSS 2.1, Section 11 - Visual effects
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
<%self:single_keyword_computed name="overflow-x" values="visible hidden scroll auto">
use values::computed::{Context, ToComputedValue};
pub fn compute_with_other_overflow_direction(value: SpecifiedValue,
other_direction: SpecifiedValue)
-> computed_value::T {
// CSS-OVERFLOW 3 states "Otherwise, if one cascaded values is one of the scrolling
// values and the other is `visible`, then computed values are the cascaded values with
// `visible` changed to `auto`."
match (value, other_direction) {
(SpecifiedValue::visible, SpecifiedValue::hidden) |
(SpecifiedValue::visible, SpecifiedValue::scroll) |
(SpecifiedValue::visible, SpecifiedValue::auto) => computed_value::T::auto,
_ => value,
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
compute_with_other_overflow_direction(*self, context.overflow_y.0)
}
}
</%self:single_keyword_computed>
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
<%self:longhand name="overflow-y">
use super::overflow_x;
use values::computed::{Context, ToComputedValue};
use cssparser::ToCss;
use std::fmt;
pub use self::computed_value::T as SpecifiedValue;
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
pub mod computed_value {
#[derive(Clone, Copy, PartialEq)]
pub struct T(pub super::super::overflow_x::computed_value::T);
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
let computed_value::T(this) = *self;
computed_value::T(overflow_x::compute_with_other_overflow_direction(
this,
context.overflow_x))
}
}
pub fn get_initial_value() -> computed_value::T {
computed_value::T(overflow_x::get_initial_value())
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
overflow_x::parse(context, input).map(|value| SpecifiedValue(value))
}
</%self:longhand>
${switch_to_style_struct("InheritedBox")}
// TODO: collapse. Well, do tables first.
${single_keyword("visibility", "visible hidden")}
// CSS 2.1, Section 12 - Generated content, automatic numbering, and lists
${switch_to_style_struct("Box")}
<%self:longhand name="content">
use cssparser::Token;
use std::ascii::AsciiExt;
use values::computed::ComputedValueAsSpecified;
use super::list_style_type;
pub use self::computed_value::T as SpecifiedValue;
pub use self::computed_value::ContentItem;
impl ComputedValueAsSpecified for SpecifiedValue {}
pub mod computed_value {
use super::super::list_style_type;
use cssparser::{self, ToCss};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub enum ContentItem {
/// Literal string content.
String(String),
/// `counter(name, style)`.
Counter(String, list_style_type::computed_value::T),
/// `counters(name, separator, style)`.
Counters(String, String, list_style_type::computed_value::T),
/// `open-quote`.
OpenQuote,
/// `close-quote`.
CloseQuote,
/// `no-open-quote`.
NoOpenQuote,
/// `no-close-quote`.
NoCloseQuote,
}
impl ToCss for ContentItem {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&ContentItem::String(ref s) => {
cssparser::serialize_string(&**s, dest)
}
&ContentItem::Counter(ref s, ref list_style_type) => {
try!(dest.write_str("counter("));
try!(cssparser::serialize_identifier(&**s, dest));
try!(dest.write_str(", "));
try!(list_style_type.to_css(dest));
dest.write_str(")")
}
&ContentItem::Counters(ref s, ref separator, ref list_style_type) => {
try!(dest.write_str("counter("));
try!(cssparser::serialize_identifier(&**s, dest));
try!(dest.write_str(", "));
try!(cssparser::serialize_string(&**separator, dest));
try!(dest.write_str(", "));
try!(list_style_type.to_css(dest));
dest.write_str(")")
}
&ContentItem::OpenQuote => dest.write_str("open-quote"),
&ContentItem::CloseQuote => dest.write_str("close-quote"),
&ContentItem::NoOpenQuote => dest.write_str("no-open-quote"),
&ContentItem::NoCloseQuote => dest.write_str("no-close-quote"),
}
}
}
#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq, Clone)]
pub enum T {
normal,
none,
Content(Vec<ContentItem>),
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&T::normal => dest.write_str("normal"),
&T::none => dest.write_str("none"),
&T::Content(ref content) => {
let mut iter = content.iter();
try!(iter.next().unwrap().to_css(dest));
for c in iter {
try!(dest.write_str(" "));
try!(c.to_css(dest));
}
Ok(())
}
}
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::normal
}
pub fn counter_name_is_illegal(name: &str) -> bool {
name.eq_ignore_ascii_case("none") || name.eq_ignore_ascii_case("inherit") ||
name.eq_ignore_ascii_case("initial")
}
// normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
// no-close-quote ]+
// TODO: <uri>, attr(<identifier>)
pub fn parse(context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
return Ok(SpecifiedValue::normal)
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue::none)
}
let mut content = vec![];
loop {
match input.next() {
Ok(Token::QuotedString(value)) => {
content.push(ContentItem::String(value.into_owned()))
}
Ok(Token::Function(name)) => {
content.push(try!(match_ignore_ascii_case! { name,
"counter" => input.parse_nested_block(|input| {
let name = try!(input.expect_ident()).into_owned();
let style = input.try(|input| {
try!(input.expect_comma());
list_style_type::parse(context, input)
}).unwrap_or(list_style_type::computed_value::T::decimal);
Ok(ContentItem::Counter(name, style))
}),
"counters" => input.parse_nested_block(|input| {
let name = try!(input.expect_ident()).into_owned();
try!(input.expect_comma());
let separator = try!(input.expect_string()).into_owned();
let style = input.try(|input| {
try!(input.expect_comma());
list_style_type::parse(context, input)
}).unwrap_or(list_style_type::computed_value::T::decimal);
Ok(ContentItem::Counters(name, separator, style))
})
_ => return Err(())
}));
}
Ok(Token::Ident(ident)) => {
match_ignore_ascii_case! { ident,
"open-quote" => content.push(ContentItem::OpenQuote),
"close-quote" => content.push(ContentItem::CloseQuote),
"no-open-quote" => content.push(ContentItem::NoOpenQuote),
"no-close-quote" => content.push(ContentItem::NoCloseQuote)
_ => return Err(())
}
}
Err(_) => break,
_ => return Err(())
}
}
if !content.is_empty() {
Ok(SpecifiedValue::Content(content))
} else {
Err(())
}
}
</%self:longhand>
${new_style_struct("List", is_inherited=True)}
${single_keyword("list-style-position", "outside inside")}
// TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
//
// decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
// upper-roman
//
// [1]: http://dev.w3.org/csswg/css-counter-styles/
${single_keyword("list-style-type", """
disc none circle square decimal arabic-indic bengali cambodian cjk-decimal devanagari
gujarati gurmukhi kannada khmer lao malayalam mongolian myanmar oriya persian telugu thai
tibetan lower-alpha upper-alpha cjk-earthly-branch cjk-heavenly-stem lower-greek hiragana
hiragana-iroha katakana katakana-iroha disclosure-open disclosure-closed
""")}
<%self:longhand name="list-style-image">
use url::Url;
use cssparser::{ToCss, Token};
use std::fmt;
use values::computed::{ToComputedValue, Context};
#[derive(Clone, PartialEq, Eq)]
pub enum SpecifiedValue {
None,
Url(Url),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::None => dest.write_str("none"),
SpecifiedValue::Url(ref url) => {
Token::Url(url.to_string().into()).to_css(dest)
}
}
}
}
pub mod computed_value {
use url::Url;
pub type T = Option<Url>;
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::None => None,
SpecifiedValue::Url(ref url) => Some(url.clone()),
}
}
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
Ok(SpecifiedValue::None)
} else {
Ok(SpecifiedValue::Url(context.parse_url(&*try!(input.expect_url()))))
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
</%self:longhand>
<%self:longhand name="quotes">
use std::fmt;
use values::computed::ComputedValueAsSpecified;
use std::borrow::Cow;
use cssparser::{ToCss, Token};
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
#[derive(Clone, PartialEq)]
pub struct T(pub Vec<(String,String)>);
}
impl ComputedValueAsSpecified for SpecifiedValue {}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut first = true;
for pair in self.0.iter() {
if !first {
try!(dest.write_str(" "));
}
first = false;
try!(Token::QuotedString(Cow::from(&*pair.0)).to_css(dest));
try!(dest.write_str(" "));
try!(Token::QuotedString(Cow::from(&*pair.1)).to_css(dest));
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![
("\u{201c}".to_string(), "\u{201d}".to_string()),
("\u{2018}".to_string(), "\u{2019}".to_string()),
])
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue(Vec::new()))
}
let mut quotes = Vec::new();
loop {
let first = match input.next() {
Ok(Token::QuotedString(value)) => value.into_owned(),
Ok(_) => return Err(()),
Err(()) => break,
};
let second = match input.next() {
Ok(Token::QuotedString(value)) => value.into_owned(),
_ => return Err(()),
};
quotes.push((first, second))
}
if !quotes.is_empty() {
Ok(SpecifiedValue(quotes))
} else {
Err(())
}
}
</%self:longhand>
${new_style_struct("Counters", is_inherited=False)}
<%self:longhand name="counter-increment">
use super::content;
use std::fmt;
use values::computed::ComputedValueAsSpecified;
use cssparser::{ToCss, Token};
use std::borrow::{Cow, ToOwned};
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
#[derive(Clone, PartialEq)]
pub struct T(pub Vec<(String,i32)>);
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(Vec::new())
}
impl ComputedValueAsSpecified for SpecifiedValue {}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut first = true;
for pair in self.0.iter() {
if !first {
try!(dest.write_str(" "));
}
first = false;
try!(Token::QuotedString(Cow::from(&*pair.0)).to_css(dest));
try!(write!(dest, " {}", pair.1));
}
Ok(())
}
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue(Vec::new()))
}
let mut counters = Vec::new();
loop {
let counter_name = match input.next() {
Ok(Token::Ident(ident)) => (*ident).to_owned(),
Ok(_) => return Err(()),
Err(_) => break,
};
if content::counter_name_is_illegal(&counter_name) {
return Err(())
}
let counter_delta = input.try(|input| input.expect_integer()).unwrap_or(1) as i32;
counters.push((counter_name, counter_delta))
}
if !counters.is_empty() {
Ok(SpecifiedValue(counters))
} else {
Err(())
}
}
</%self:longhand>
<%self:longhand name="counter-reset">
pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
pub use super::counter_increment::{parse};
</%self:longhand>
// CSS 2.1, Section 13 - Paged media
// CSS 2.1, Section 14 - Colors and Backgrounds
${new_style_struct("Background", is_inherited=False)}
${predefined_type("background-color", "CSSColor",
"::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")}
<%self:longhand name="background-image">
use values::specified::Image;
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
pub mod computed_value {
use values::computed;
pub type T = Option<computed::Image>;
}
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(pub Option<Image>);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue(Some(ref image)) => image.to_css(dest),
SpecifiedValue(None) => dest.write_str("none"),
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
Ok(SpecifiedValue(None))
} else {
Ok(SpecifiedValue(Some(try!(Image::parse(context, input)))))
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
SpecifiedValue(None) => None,
SpecifiedValue(Some(ref image)) => Some(image.to_computed_value(context)),
}
}
}
</%self:longhand>
<%self:longhand name="background-position">
use cssparser::ToCss;
use std::fmt;
use values::computed::{ToComputedValue, Context};
pub mod computed_value {
use values::computed::LengthOrPercentage;
#[derive(PartialEq, Copy, Clone, Debug)]
pub struct T {
pub horizontal: LengthOrPercentage,
pub vertical: LengthOrPercentage,
}
}
#[derive(Clone, PartialEq, Copy)]
pub struct SpecifiedValue {
pub horizontal: specified::LengthOrPercentage,
pub vertical: specified::LengthOrPercentage,
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.horizontal.to_css(dest));
try!(dest.write_str(" "));
try!(self.vertical.to_css(dest));
Ok(())
}
}
impl SpecifiedValue {
fn new(first: specified::PositionComponent, second: specified::PositionComponent)
-> Result<SpecifiedValue, ()> {
let (horiz, vert) = match (category(first), category(second)) {
// Don't allow two vertical keywords or two horizontal keywords.
(PositionCategory::HorizontalKeyword, PositionCategory::HorizontalKeyword) |
(PositionCategory::VerticalKeyword, PositionCategory::VerticalKeyword) => return Err(()),
// Swap if both are keywords and vertical precedes horizontal.
(PositionCategory::VerticalKeyword, PositionCategory::HorizontalKeyword) |
(PositionCategory::VerticalKeyword, PositionCategory::OtherKeyword) |
(PositionCategory::OtherKeyword, PositionCategory::HorizontalKeyword) => (second, first),
// By default, horizontal is first.
_ => (first, second),
};
Ok(SpecifiedValue {
horizontal: horiz.to_length_or_percentage(),
vertical: vert.to_length_or_percentage(),
})
}
}
// Collapse `Position` into a few categories to simplify the above `match` expression.
enum PositionCategory {
HorizontalKeyword,
VerticalKeyword,
OtherKeyword,
LengthOrPercentage,
}
fn category(p: specified::PositionComponent) -> PositionCategory {
match p {
specified::PositionComponent::Left |
specified::PositionComponent::Right =>
PositionCategory::HorizontalKeyword,
specified::PositionComponent::Top |
specified::PositionComponent::Bottom =>
PositionCategory::VerticalKeyword,
specified::PositionComponent::Center =>
PositionCategory::OtherKeyword,
specified::PositionComponent::Length(_) |
specified::PositionComponent::Percentage(_) =>
PositionCategory::LengthOrPercentage,
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
computed_value::T {
horizontal: self.horizontal.to_computed_value(context),
vertical: self.vertical.to_computed_value(context),
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T {
horizontal: computed::LengthOrPercentage::Percentage(0.0),
vertical: computed::LengthOrPercentage::Percentage(0.0),
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
let first = try!(specified::PositionComponent::parse(input));
let second = input.try(specified::PositionComponent::parse)
.unwrap_or(specified::PositionComponent::Center);
SpecifiedValue::new(first, second)
}
</%self:longhand>
${single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")}
${single_keyword("background-attachment", "scroll fixed")}
<%self:longhand name="background-size">
use cssparser::{ToCss, Token};
use std::ascii::AsciiExt;
use std::fmt;
use values::computed::{Context, ToComputedValue};
pub mod computed_value {
use values::computed::LengthOrPercentageOrAuto;
#[derive(PartialEq, Clone, Debug)]
pub struct ExplicitSize {
pub width: LengthOrPercentageOrAuto,
pub height: LengthOrPercentageOrAuto,
}
#[derive(PartialEq, Clone, Debug)]
pub enum T {
Explicit(ExplicitSize),
Cover,
Contain,
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct SpecifiedExplicitSize {
pub width: specified::LengthOrPercentageOrAuto,
pub height: specified::LengthOrPercentageOrAuto,
}
impl ToCss for SpecifiedExplicitSize {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.width.to_css(dest));
try!(dest.write_str(" "));
self.height.to_css(dest)
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum SpecifiedValue {
Explicit(SpecifiedExplicitSize),
Cover,
Contain,
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::Explicit(ref size) => size.to_css(dest),
SpecifiedValue::Cover => dest.write_str("cover"),
SpecifiedValue::Contain => dest.write_str("contain"),
}
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &computed::Context) -> computed_value::T {
match *self {
SpecifiedValue::Explicit(ref size) => {
computed_value::T::Explicit(computed_value::ExplicitSize {
width: size.width.to_computed_value(context),
height: size.height.to_computed_value(context),
})
}
SpecifiedValue::Cover => computed_value::T::Cover,
SpecifiedValue::Contain => computed_value::T::Contain,
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::Explicit(computed_value::ExplicitSize {
width: computed::LengthOrPercentageOrAuto::Auto,
height: computed::LengthOrPercentageOrAuto::Auto,
})
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
let width;
if let Ok(value) = input.try(|input| {
match input.next() {
Err(_) => Err(()),
Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("cover") => {
Ok(SpecifiedValue::Cover)
}
Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("contain") => {
Ok(SpecifiedValue::Contain)
}
Ok(_) => Err(()),
}
}) {
return Ok(value)
} else {
width = try!(specified::LengthOrPercentageOrAuto::parse(input))
}
let height;
if let Ok(value) = input.try(|input| {
match input.next() {
Err(_) => Ok(specified::LengthOrPercentageOrAuto::Auto),
Ok(_) => Err(()),
}
}) {
height = value
} else {
height = try!(specified::LengthOrPercentageOrAuto::parse(input));
}
Ok(SpecifiedValue::Explicit(SpecifiedExplicitSize {
width: width,
height: height,
}))
}
</%self:longhand>
${new_style_struct("Color", is_inherited=True)}
<%self:raw_longhand name="color">
use cssparser::{Color, RGBA};
use values::specified::{CSSColor, CSSRGBA};
use values::computed::{ToComputedValue, Context};
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T {
self.parsed
}
}
pub type SpecifiedValue = CSSRGBA;
pub mod computed_value {
use cssparser;
pub type T = cssparser::RGBA;
}
#[inline] pub fn get_initial_value() -> computed_value::T {
RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */
}
pub fn parse_specified(_context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
let value = try!(CSSColor::parse(input));
let rgba = match value.parsed {
Color::RGBA(rgba) => rgba,
Color::CurrentColor => return Ok(DeclaredValue::Inherit)
};
Ok(DeclaredValue::SpecifiedValue(CSSRGBA {
parsed: rgba,
authored: value.authored,
}))
}
</%self:raw_longhand>
// CSS 2.1, Section 15 - Fonts
${new_style_struct("Font", is_inherited=True)}
<%self:longhand name="font-family">
use self::computed_value::FontFamily;
use string_cache::Atom;
use values::computed::ComputedValueAsSpecified;
pub use self::computed_value::T as SpecifiedValue;
impl ComputedValueAsSpecified for SpecifiedValue {}
pub mod computed_value {
use cssparser::ToCss;
use string_cache::Atom;
use std::fmt;
#[derive(PartialEq, Eq, Clone, Hash)]
pub enum FontFamily {
FamilyName(Atom),
// Generic
// Serif,
// SansSerif,
// Cursive,
// Fantasy,
// Monospace,
}
impl FontFamily {
#[inline]
pub fn name(&self) -> &str {
match *self {
FontFamily::FamilyName(ref name) => name.as_slice(),
}
}
}
impl ToCss for FontFamily {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&FontFamily::FamilyName(ref name) => dest.write_str(name.as_slice()),
}
}
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
try!(iter.next().unwrap().to_css(dest));
for family in iter {
try!(dest.write_str(", "));
try!(family.to_css(dest));
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct T(pub Vec<FontFamily>);
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![FontFamily::FamilyName(Atom::from_slice("serif"))])
}
/// <family-name>#
/// <family-name> = <string> | [ <ident>+ ]
/// TODO: <generic-family>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
input.parse_comma_separated(parse_one_family).map(SpecifiedValue)
}
pub fn parse_one_family(input: &mut Parser) -> Result<FontFamily, ()> {
if let Ok(value) = input.try(|input| input.expect_string()) {
return Ok(FontFamily::FamilyName(Atom::from_slice(&value)))
}
let first_ident = try!(input.expect_ident());
// match_ignore_ascii_case! { first_ident,
// "serif" => return Ok(Serif),
// "sans-serif" => return Ok(SansSerif),
// "cursive" => return Ok(Cursive),
// "fantasy" => return Ok(Fantasy),
// "monospace" => return Ok(Monospace)
// _ => {}
// }
let mut value = first_ident.into_owned();
while let Ok(ident) = input.try(|input| input.expect_ident()) {
value.push_str(" ");
value.push_str(&ident);
}
Ok(FontFamily::FamilyName(Atom::from_slice(&value)))
}
</%self:longhand>
${single_keyword("font-style", "normal italic oblique")}
${single_keyword("font-variant", "normal small-caps")}
<%self:longhand name="font-weight">
use cssparser::ToCss;
use std::fmt;
use values::computed::{ToComputedValue, Context};
#[derive(Clone, PartialEq, Eq, Copy)]
pub enum SpecifiedValue {
Bolder,
Lighter,
% for weight in range(100, 901, 100):
Weight${weight},
% endfor
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&SpecifiedValue::Bolder => dest.write_str("bolder"),
&SpecifiedValue::Lighter => dest.write_str("lighter"),
% for weight in range(100, 901, 100):
&SpecifiedValue::Weight${weight} => dest.write_str("${weight}"),
% endfor
}
}
}
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
input.try(|input| {
match_ignore_ascii_case! { try!(input.expect_ident()),
"bold" => Ok(SpecifiedValue::Weight700),
"normal" => Ok(SpecifiedValue::Weight400),
"bolder" => Ok(SpecifiedValue::Bolder),
"lighter" => Ok(SpecifiedValue::Lighter)
_ => Err(())
}
}).or_else(|()| {
match try!(input.expect_integer()) {
100 => Ok(SpecifiedValue::Weight100),
200 => Ok(SpecifiedValue::Weight200),
300 => Ok(SpecifiedValue::Weight300),
400 => Ok(SpecifiedValue::Weight400),
500 => Ok(SpecifiedValue::Weight500),
600 => Ok(SpecifiedValue::Weight600),
700 => Ok(SpecifiedValue::Weight700),
800 => Ok(SpecifiedValue::Weight800),
900 => Ok(SpecifiedValue::Weight900),
_ => Err(())
}
})
}
pub mod computed_value {
use std::fmt;
#[derive(PartialEq, Eq, Copy, Clone, Hash)]
pub enum T {
% for weight in range(100, 901, 100):
Weight${weight} = ${weight},
% endfor
}
impl fmt::Debug for T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
% for weight in range(100, 901, 100):
&T::Weight${weight} => write!(f, "{}", ${weight}),
% endfor
}
}
}
impl T {
#[inline]
pub fn is_bold(self) -> bool {
match self {
T::Weight900 | T::Weight800 |
T::Weight700 | T::Weight600 => true,
_ => false
}
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::Weight400 // normal
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
% for weight in range(100, 901, 100):
SpecifiedValue::Weight${weight} => computed_value::T::Weight${weight},
% endfor
SpecifiedValue::Bolder => match context.inherited_font_weight {
computed_value::T::Weight100 => computed_value::T::Weight400,
computed_value::T::Weight200 => computed_value::T::Weight400,
computed_value::T::Weight300 => computed_value::T::Weight400,
computed_value::T::Weight400 => computed_value::T::Weight700,
computed_value::T::Weight500 => computed_value::T::Weight700,
computed_value::T::Weight600 => computed_value::T::Weight900,
computed_value::T::Weight700 => computed_value::T::Weight900,
computed_value::T::Weight800 => computed_value::T::Weight900,
computed_value::T::Weight900 => computed_value::T::Weight900,
},
SpecifiedValue::Lighter => match context.inherited_font_weight {
computed_value::T::Weight100 => computed_value::T::Weight100,
computed_value::T::Weight200 => computed_value::T::Weight100,
computed_value::T::Weight300 => computed_value::T::Weight100,
computed_value::T::Weight400 => computed_value::T::Weight100,
computed_value::T::Weight500 => computed_value::T::Weight100,
computed_value::T::Weight600 => computed_value::T::Weight400,
computed_value::T::Weight700 => computed_value::T::Weight400,
computed_value::T::Weight800 => computed_value::T::Weight700,
computed_value::T::Weight900 => computed_value::T::Weight700,
},
}
}
}
</%self:longhand>
<%self:longhand name="font-size">
use util::geometry::Au;
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(pub specified::Length); // Percentages are the same as em.
pub mod computed_value {
use util::geometry::Au;
pub type T = Au;
}
const MEDIUM_PX: i32 = 16;
#[inline] pub fn get_initial_value() -> computed_value::T {
Au::from_px(MEDIUM_PX)
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
// We already computed this element's font size; no need to compute it again.
return context.font_size
}
}
/// <length> | <percentage> | <absolute-size> | <relative-size>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
input.try(specified::LengthOrPercentage::parse_non_negative)
.map(|value| match value {
specified::LengthOrPercentage::Length(value) => value,
specified::LengthOrPercentage::Percentage(value) => specified::Length::FontRelative(specified::FontRelativeLength::Em(value))
})
.or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
"xx-small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 5)),
"x-small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 4)),
"small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 8 / 9)),
"medium" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX))),
"large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 6 / 5)),
"x-large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 2)),
"xx-large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 2)),
// https://github.com/servo/servo/issues/3423#issuecomment-56321664
"smaller" => Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(0.85))),
"larger" => Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(1.2)))
_ => Err(())
}
})
.map(SpecifiedValue)
}
</%self:longhand>
${single_keyword("font-stretch",
"normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded expanded extra-expanded ultra-expanded")}
// CSS 2.1, Section 16 - Text
${new_style_struct("InheritedText", is_inherited=True)}
<%self:longhand name="text-align">
pub use self::computed_value::T as SpecifiedValue;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
pub mod computed_value {
macro_rules! define_text_align {
( $( $name: ident ( $string: expr ) => $discriminant: expr, )+ ) => {
define_css_keyword_enum! { T:
$(
$string => $name,
)+
}
impl T {
pub fn to_u32(self) -> u32 {
match self {
$(
T::$name => $discriminant,
)+
}
}
pub fn from_u32(discriminant: u32) -> Option<T> {
match discriminant {
$(
$discriminant => Some(T::$name),
)+
_ => None
}
}
}
}
}
define_text_align! {
start("start") => 0,
end("end") => 1,
left("left") => 2,
right("right") => 3,
center("center") => 4,
justify("justify") => 5,
servo_center("-servo-center") => 6,
}
}
#[inline] pub fn get_initial_value() -> computed_value::T {
computed_value::T::start
}
pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
computed_value::T::parse(input)
}
</%self:longhand>
<%self:longhand name="letter-spacing">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
#[derive(Clone, Copy, PartialEq)]
pub enum SpecifiedValue {
Normal,
Specified(specified::Length),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::Normal => dest.write_str("normal"),
SpecifiedValue::Specified(l) => l.to_css(dest),
}
}
}
pub mod computed_value {
use util::geometry::Au;
pub type T = Option<Au>;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::Normal => None,
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
}
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
Ok(SpecifiedValue::Normal)
} else {
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
}
}
</%self:longhand>
<%self:longhand name="word-spacing">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
#[derive(Clone, Copy, PartialEq)]
pub enum SpecifiedValue {
Normal,
Specified(specified::Length), // FIXME(SimonSapin) support percentages
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::Normal => dest.write_str("normal"),
SpecifiedValue::Specified(l) => l.to_css(dest),
}
}
}
pub mod computed_value {
use util::geometry::Au;
pub type T = Option<Au>;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::Normal => None,
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
}
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
Ok(SpecifiedValue::Normal)
} else {
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
}
}
</%self:longhand>
${predefined_type("text-indent", "LengthOrPercentage", "computed::LengthOrPercentage::Length(Au(0))")}
// Also known as "word-wrap" (which is more popular because of IE), but this is the preferred
// name per CSS-TEXT 6.2.
${single_keyword("overflow-wrap", "normal break-word")}
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
${single_keyword("word-break", "normal break-all")}
${single_keyword("text-overflow", "clip ellipsis")}
// TODO(pcwalton): Support `text-justify: distribute`.
${single_keyword("text-justify", "auto none inter-word")}
${new_style_struct("Text", is_inherited=False)}
<%self:longhand name="text-decoration">
use cssparser::ToCss;
use std::fmt;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct SpecifiedValue {
pub underline: bool,
pub overline: bool,
pub line_through: bool,
// 'blink' is accepted in the parser but ignored.
// Just not blinking the text is a conforming implementation per CSS 2.1.
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut space = false;
if self.underline {
try!(dest.write_str("underline"));
space = true;
}
if self.overline {
if space {
try!(dest.write_str(" "));
}
try!(dest.write_str("overline"));
space = true;
}
if self.line_through {
if space {
try!(dest.write_str(" "));
}
try!(dest.write_str("line-through"));
}
Ok(())
}
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
#[allow(non_upper_case_globals)]
pub const none: T = super::SpecifiedValue {
underline: false, overline: false, line_through: false
};
}
#[inline] pub fn get_initial_value() -> computed_value::T {
computed_value::none
}
/// none | [ underline || overline || line-through || blink ]
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut result = SpecifiedValue {
underline: false, overline: false, line_through: false,
};
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(result)
}
let mut blink = false;
let mut empty = true;
while let Ok(ident) = input.expect_ident() {
match_ignore_ascii_case! { ident,
"underline" => if result.underline { return Err(()) }
else { empty = false; result.underline = true },
"overline" => if result.overline { return Err(()) }
else { empty = false; result.overline = true },
"line-through" => if result.line_through { return Err(()) }
else { empty = false; result.line_through = true },
"blink" => if blink { return Err(()) }
else { empty = false; blink = true }
_ => break
}
}
if !empty { Ok(result) } else { Err(()) }
}
</%self:longhand>
${switch_to_style_struct("InheritedText")}
<%self:longhand name="-servo-text-decorations-in-effect"
derived_from="display text-decoration">
use cssparser::RGBA;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
#[derive(Clone, PartialEq, Copy, Debug)]
pub struct SpecifiedValue {
pub underline: Option<RGBA>,
pub overline: Option<RGBA>,
pub line_through: Option<RGBA>,
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
SpecifiedValue {
underline: None,
overline: None,
line_through: None,
}
}
fn maybe(flag: bool, context: &computed::Context) -> Option<RGBA> {
if flag {
Some(context.color)
} else {
None
}
}
fn derive(context: &computed::Context) -> computed_value::T {
// Start with no declarations if this is a block; otherwise, start with the
// declarations in effect and add in the text decorations that this inline specifies.
let mut result = match context.display {
super::display::computed_value::T::inline => {
context.inherited_text_decorations_in_effect
}
_ => {
SpecifiedValue {
underline: None,
overline: None,
line_through: None,
}
}
};
if result.underline.is_none() {
result.underline = maybe(context.text_decoration.underline, context)
}
if result.overline.is_none() {
result.overline = maybe(context.text_decoration.overline, context)
}
if result.line_through.is_none() {
result.line_through = maybe(context.text_decoration.line_through, context)
}
result
}
#[inline]
pub fn derive_from_text_decoration(_: super::text_decoration::computed_value::T,
context: &computed::Context)
-> computed_value::T {
derive(context)
}
#[inline]
pub fn derive_from_display(_: super::display::computed_value::T,
context: &computed::Context)
-> computed_value::T {
derive(context)
}
</%self:longhand>
${single_keyword("white-space", "normal pre nowrap")}
// TODO(pcwalton): `full-width`
${single_keyword("text-transform", "none capitalize uppercase lowercase")}
${single_keyword("text-rendering", "auto optimizespeed optimizelegibility geometricprecision")}
// CSS 2.1, Section 17 - Tables
${new_style_struct("Table", is_inherited=False)}
${single_keyword("table-layout", "auto fixed")}
${new_style_struct("InheritedTable", is_inherited=True)}
${single_keyword("border-collapse", "separate collapse")}
${single_keyword("empty-cells", "show hide")}
${single_keyword("caption-side", "top bottom")}
<%self:longhand name="border-spacing">
use values::computed::{Context, ToComputedValue};
use cssparser::ToCss;
use std::fmt;
use util::geometry::Au;
pub mod computed_value {
use util::geometry::Au;
#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable)]
pub struct T {
pub horizontal: Au,
pub vertical: Au,
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SpecifiedValue {
pub horizontal: specified::Length,
pub vertical: specified::Length,
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T {
horizontal: Au(0),
vertical: Au(0),
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.horizontal.to_css(dest));
try!(dest.write_str(" "));
self.vertical.to_css(dest)
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
computed_value::T {
horizontal: self.horizontal.to_computed_value(context),
vertical: self.vertical.to_computed_value(context),
}
}
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
let mut lengths = [ None, None ];
for i in 0..2 {
match specified::Length::parse_non_negative(input) {
Err(()) => break,
Ok(length) => lengths[i] = Some(length),
}
}
if input.next().is_ok() {
return Err(())
}
match (lengths[0], lengths[1]) {
(None, None) => Err(()),
(Some(length), None) => {
Ok(SpecifiedValue {
horizontal: length,
vertical: length,
})
}
(Some(horizontal), Some(vertical)) => {
Ok(SpecifiedValue {
horizontal: horizontal,
vertical: vertical,
})
}
(None, Some(_)) => panic!("shouldn't happen"),
}
}
</%self:longhand>
// CSS 2.1, Section 18 - User interface
// CSS Writing Modes Level 3
// http://dev.w3.org/csswg/css-writing-modes/
${switch_to_style_struct("InheritedBox")}
${single_keyword("writing-mode", "horizontal-tb vertical-rl vertical-lr", experimental=True)}
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
${single_keyword("text-orientation", "sideways sideways-left sideways-right", experimental=True)}
// CSS Basic User Interface Module Level 3
// http://dev.w3.org/csswg/css-ui/
${switch_to_style_struct("Box")}
${single_keyword("box-sizing", "content-box border-box")}
${new_style_struct("Pointing", is_inherited=True)}
<%self:longhand name="cursor">
use util::cursor as util_cursor;
pub use self::computed_value::T as SpecifiedValue;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
use util::cursor::Cursor;
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
pub enum T {
AutoCursor,
SpecifiedCursor(Cursor),
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
T::AutoCursor => dest.write_str("auto"),
T::SpecifiedCursor(c) => c.to_css(dest),
}
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::AutoCursor
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
use std::ascii::AsciiExt;
let ident = try!(input.expect_ident());
if ident.eq_ignore_ascii_case("auto") {
Ok(SpecifiedValue::AutoCursor)
} else {
util_cursor::Cursor::from_css_keyword(&ident)
.map(SpecifiedValue::SpecifiedCursor)
}
}
</%self:longhand>
// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
// is nonstandard, slated for CSS4-UI.
// TODO(pcwalton): SVG-only values.
${single_keyword("pointer-events", "auto none")}
${new_style_struct("Column", is_inherited=False)}
<%self:longhand name="column-width" experimental="True">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
#[derive(Clone, Copy, PartialEq)]
pub enum SpecifiedValue {
Auto,
Specified(specified::Length),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::Auto => dest.write_str("auto"),
SpecifiedValue::Specified(l) => l.to_css(dest),
}
}
}
pub mod computed_value {
use util::geometry::Au;
pub type T = Option<Au>;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::Auto => None,
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
}
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
Ok(SpecifiedValue::Auto)
} else {
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
}
}
</%self:longhand>
<%self:longhand name="column-count" experimental="True">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
#[derive(Clone, Copy, PartialEq)]
pub enum SpecifiedValue {
Auto,
Specified(u32),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::Auto => dest.write_str("auto"),
SpecifiedValue::Specified(count) => write!(dest, "{}", count),
}
}
}
pub mod computed_value {
pub type T = Option<u32>;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::Auto => None,
SpecifiedValue::Specified(count) => Some(count)
}
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
Ok(SpecifiedValue::Auto)
} else {
let count = try!(input.expect_integer());
// Zero is invalid
if count <= 0 {
return Err(())
}
Ok(SpecifiedValue::Specified(count as u32))
}
}
</%self:longhand>
<%self:longhand name="column-gap" experimental="True">
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
#[derive(Clone, Copy, PartialEq)]
pub enum SpecifiedValue {
Normal,
Specified(specified::Length),
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedValue::Normal => dest.write_str("normal"),
SpecifiedValue::Specified(l) => l.to_css(dest),
}
}
}
pub mod computed_value {
use util::geometry::Au;
pub type T = Option<Au>;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
SpecifiedValue::Normal => None,
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
}
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
Ok(SpecifiedValue::Normal)
} else {
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
}
}
</%self:longhand>
// Box-shadow, etc.
${new_style_struct("Effects", is_inherited=False)}
<%self:longhand name="opacity">
use values::CSSFloat;
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::fmt;
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(pub CSSFloat);
pub mod computed_value {
use values::CSSFloat;
pub type T = CSSFloat;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
1.0
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T {
if self.0 < 0.0 {
0.0
} else if self.0 > 1.0 {
1.0
} else {
self.0
}
}
}
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
input.expect_number().map(SpecifiedValue)
}
</%self:longhand>
<%self:longhand name="box-shadow">
use cssparser::{self, ToCss};
use std::fmt;
use values::computed::{ToComputedValue, Context};
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(Vec<SpecifiedBoxShadow>);
#[derive(Clone, PartialEq)]
pub struct SpecifiedBoxShadow {
pub offset_x: specified::Length,
pub offset_y: specified::Length,
pub blur_radius: specified::Length,
pub spread_radius: specified::Length,
pub color: Option<specified::CSSColor>,
pub inset: bool,
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
if let Some(shadow) = iter.next() {
try!(shadow.to_css(dest));
} else {
try!(dest.write_str("none"));
return Ok(())
}
for shadow in iter {
try!(dest.write_str(", "));
try!(shadow.to_css(dest));
}
Ok(())
}
}
impl ToCss for SpecifiedBoxShadow {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.inset {
try!(dest.write_str("inset "));
}
try!(self.blur_radius.to_css(dest));
try!(dest.write_str(" "));
try!(self.spread_radius.to_css(dest));
try!(dest.write_str(" "));
try!(self.offset_x.to_css(dest));
try!(dest.write_str(" "));
try!(self.offset_y.to_css(dest));
if let Some(ref color) = self.color {
try!(dest.write_str(" "));
try!(color.to_css(dest));
}
Ok(())
}
}
pub mod computed_value {
use util::geometry::Au;
use values::computed;
use std::fmt;
pub type T = Vec<BoxShadow>;
#[derive(Clone, PartialEq, Copy)]
pub struct BoxShadow {
pub offset_x: Au,
pub offset_y: Au,
pub blur_radius: Au,
pub spread_radius: Au,
pub color: computed::CSSColor,
pub inset: bool,
}
impl fmt::Debug for BoxShadow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.inset {
let _ = write!(f, "inset ");
}
let _ = write!(f, "{:?} {:?} {:?} {:?} {:?}", self.offset_x, self.offset_y,
self.blur_radius, self.spread_radius, self.color);
Ok(())
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
Vec::new()
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
Ok(SpecifiedValue(Vec::new()))
} else {
input.parse_comma_separated(parse_one_box_shadow).map(SpecifiedValue)
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
self.0.iter().map(|value| compute_one_box_shadow(value, context)).collect()
}
}
pub fn compute_one_box_shadow(value: &SpecifiedBoxShadow, context: &computed::Context)
-> computed_value::BoxShadow {
computed_value::BoxShadow {
offset_x: value.offset_x.to_computed_value(context),
offset_y: value.offset_y.to_computed_value(context),
blur_radius: value.blur_radius.to_computed_value(context),
spread_radius: value.spread_radius.to_computed_value(context),
color: value.color
.as_ref()
.map(|color| color.parsed)
.unwrap_or(cssparser::Color::CurrentColor),
inset: value.inset,
}
}
pub fn parse_one_box_shadow(input: &mut Parser) -> Result<SpecifiedBoxShadow, ()> {
use util::geometry::Au;
let mut lengths = [specified::Length::Absolute(Au(0)); 4];
let mut lengths_parsed = false;
let mut color = None;
let mut inset = false;
loop {
if !inset {
if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
inset = true;
continue
}
}
if !lengths_parsed {
if let Ok(value) = input.try(specified::Length::parse) {
lengths[0] = value;
let mut length_parsed_count = 1;
while length_parsed_count < 4 {
if let Ok(value) = input.try(specified::Length::parse) {
lengths[length_parsed_count] = value
} else {
break
}
length_parsed_count += 1;
}
// The first two lengths must be specified.
if length_parsed_count < 2 {
return Err(())
}
lengths_parsed = true;
continue
}
}
if color.is_none() {
if let Ok(value) = input.try(specified::CSSColor::parse) {
color = Some(value);
continue
}
}
break
}
// Lengths must be specified.
if !lengths_parsed {
return Err(())
}
Ok(SpecifiedBoxShadow {
offset_x: lengths[0],
offset_y: lengths[1],
blur_radius: lengths[2],
spread_radius: lengths[3],
color: color,
inset: inset,
})
}
</%self:longhand>
<%self:longhand name="clip">
use cssparser::ToCss;
use std::fmt;
// NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2.
use values::computed::{ToComputedValue, Context};
pub mod computed_value {
use util::geometry::Au;
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
pub struct ClipRect {
pub top: Au,
pub right: Option<Au>,
pub bottom: Option<Au>,
pub left: Au,
}
pub type T = Option<ClipRect>;
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct SpecifiedClipRect {
pub top: specified::Length,
pub right: Option<specified::Length>,
pub bottom: Option<specified::Length>,
pub left: specified::Length,
}
#[derive(Clone, Debug, PartialEq, Copy)]
pub struct SpecifiedValue(Option<SpecifiedClipRect>);
impl ToCss for SpecifiedClipRect {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(dest.write_str("rect("));
try!(self.top.to_css(dest));
try!(dest.write_str(", "));
if let Some(right) = self.right {
try!(right.to_css(dest));
try!(dest.write_str(", "));
} else {
try!(dest.write_str("auto, "));
}
if let Some(bottom) = self.right {
try!(bottom.to_css(dest));
try!(dest.write_str(", "));
} else {
try!(dest.write_str("auto, "));
}
try!(self.left.to_css(dest));
try!(dest.write_str(")"));
Ok(())
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if let Some(ref rect) = self.0 {
rect.to_css(dest)
} else {
dest.write_str("auto")
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
self.0.map(|value| computed_value::ClipRect {
top: value.top.to_computed_value(context),
right: value.right.map(|right| right.to_computed_value(context)),
bottom: value.bottom.map(|bottom| bottom.to_computed_value(context)),
left: value.left.to_computed_value(context),
})
}
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
use std::ascii::AsciiExt;
use util::geometry::Au;
use values::specified::Length;
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
return Ok(SpecifiedValue(None))
}
if !try!(input.expect_function()).eq_ignore_ascii_case("rect") {
return Err(())
}
let sides = try!(input.parse_nested_block(|input| {
input.parse_comma_separated(|input| {
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
Ok(None)
} else {
Length::parse(input).map(Some)
}
})
}));
if sides.len() == 4 {
Ok(SpecifiedValue(Some(SpecifiedClipRect {
top: sides[0].unwrap_or(Length::Absolute(Au(0))),
right: sides[1],
bottom: sides[2],
left: sides[3].unwrap_or(Length::Absolute(Au(0))),
})))
} else {
Err(())
}
}
</%self:longhand>
<%self:longhand name="text-shadow">
use cssparser::{self, ToCss};
use std::fmt;
use values::computed::{Context, ToComputedValue};
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(Vec<SpecifiedTextShadow>);
#[derive(Clone, PartialEq)]
pub struct SpecifiedTextShadow {
pub offset_x: specified::Length,
pub offset_y: specified::Length,
pub blur_radius: specified::Length,
pub color: Option<specified::CSSColor>,
}
impl fmt::Debug for SpecifiedTextShadow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f,
"{:?} {:?} {:?}",
self.offset_x,
self.offset_y,
self.blur_radius);
if let Some(ref color) = self.color {
let _ = write!(f, "{:?}", color);
}
Ok(())
}
}
pub mod computed_value {
use cssparser::Color;
use util::geometry::Au;
#[derive(Clone, PartialEq, Debug)]
pub struct T(pub Vec<TextShadow>);
#[derive(Clone, PartialEq, Debug)]
pub struct TextShadow {
pub offset_x: Au,
pub offset_y: Au,
pub blur_radius: Au,
pub color: Color,
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
if let Some(shadow) = iter.next() {
try!(shadow.to_css(dest));
} else {
try!(dest.write_str("none"));
return Ok(())
}
for shadow in iter {
try!(dest.write_str(", "));
try!(shadow.to_css(dest));
}
Ok(())
}
}
impl ToCss for SpecifiedTextShadow {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.offset_x.to_css(dest));
try!(dest.write_str(" "));
try!(self.offset_y.to_css(dest));
try!(dest.write_str(" "));
try!(self.blur_radius.to_css(dest));
if let Some(ref color) = self.color {
try!(dest.write_str(" "));
try!(color.to_css(dest));
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(Vec::new())
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
Ok(SpecifiedValue(Vec::new()))
} else {
input.parse_comma_separated(parse_one_text_shadow).map(|shadows| {
SpecifiedValue(shadows)
})
}
}
fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> {
use util::geometry::Au;
let mut lengths = [specified::Length::Absolute(Au(0)); 3];
let mut lengths_parsed = false;
let mut color = None;
loop {
if !lengths_parsed {
if let Ok(value) = input.try(specified::Length::parse) {
lengths[0] = value;
let mut length_parsed_count = 1;
while length_parsed_count < 3 {
if let Ok(value) = input.try(specified::Length::parse) {
lengths[length_parsed_count] = value
} else {
break
}
length_parsed_count += 1;
}
// The first two lengths must be specified.
if length_parsed_count < 2 {
return Err(())
}
lengths_parsed = true;
continue
}
}
if color.is_none() {
if let Ok(value) = input.try(specified::CSSColor::parse) {
color = Some(value);
continue
}
}
break
}
// Lengths must be specified.
if !lengths_parsed {
return Err(())
}
Ok(SpecifiedTextShadow {
offset_x: lengths[0],
offset_y: lengths[1],
blur_radius: lengths[2],
color: color,
})
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
fn to_computed_value(&self, context: &computed::Context) -> computed_value::T {
computed_value::T(self.0.iter().map(|value| {
computed_value::TextShadow {
offset_x: value.offset_x.to_computed_value(context),
offset_y: value.offset_y.to_computed_value(context),
blur_radius: value.blur_radius.to_computed_value(context),
color: value.color
.as_ref()
.map(|color| color.parsed)
.unwrap_or(cssparser::Color::CurrentColor),
}
}).collect())
}
}
</%self:longhand>
<%self:longhand name="filter">
//pub use self::computed_value::T as SpecifiedValue;
use values::computed::{Context, ToComputedValue};
use values::specified::{Angle, Length};
use values::CSSFloat;
use cssparser::ToCss;
use std::fmt;
#[derive(Clone, PartialEq)]
pub struct SpecifiedValue(Vec<SpecifiedFilter>);
// TODO(pcwalton): `drop-shadow`
#[derive(Clone, PartialEq, Debug)]
pub enum SpecifiedFilter {
Blur(Length),
Brightness(CSSFloat),
Contrast(CSSFloat),
Grayscale(CSSFloat),
HueRotate(Angle),
Invert(CSSFloat),
Opacity(CSSFloat),
Saturate(CSSFloat),
Sepia(CSSFloat),
}
pub mod computed_value {
use util::geometry::Au;
use values::CSSFloat;
use values::specified::{Angle};
#[derive(Clone, PartialEq, Debug)]
pub enum Filter {
Blur(Au),
Brightness(CSSFloat),
Contrast(CSSFloat),
Grayscale(CSSFloat),
HueRotate(Angle),
Invert(CSSFloat),
Opacity(CSSFloat),
Saturate(CSSFloat),
Sepia(CSSFloat),
}
#[derive(Clone, PartialEq, Debug)]
pub struct T { pub filters: Vec<Filter> }
impl T {
/// Creates a new filter pipeline.
#[inline]
pub fn new(filters: Vec<Filter>) -> T {
T
{
filters: filters,
}
}
/// Adds a new filter to the filter pipeline.
#[inline]
pub fn push(&mut self, filter: Filter) {
self.filters.push(filter)
}
/// Returns true if this filter pipeline is empty and false otherwise.
#[inline]
pub fn is_empty(&self) -> bool {
self.filters.is_empty()
}
/// Returns the resulting opacity of this filter pipeline.
#[inline]
pub fn opacity(&self) -> CSSFloat {
let mut opacity = 1.0;
for filter in self.filters.iter() {
if let Filter::Opacity(ref opacity_value) = *filter {
opacity *= *opacity_value
}
}
opacity
}
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
if let Some(filter) = iter.next() {
try!(filter.to_css(dest));
} else {
try!(dest.write_str("none"));
return Ok(())
}
for filter in iter {
try!(dest.write_str(" "));
try!(filter.to_css(dest));
}
Ok(())
}
}
impl ToCss for SpecifiedFilter {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
SpecifiedFilter::Blur(value) => {
try!(dest.write_str("blur("));
try!(value.to_css(dest));
try!(dest.write_str(")"));
}
SpecifiedFilter::Brightness(value) => try!(write!(dest, "brightness({})", value)),
SpecifiedFilter::Contrast(value) => try!(write!(dest, "contrast({})", value)),
SpecifiedFilter::Grayscale(value) => try!(write!(dest, "grayscale({})", value)),
SpecifiedFilter::HueRotate(value) => {
try!(dest.write_str("hue-rotate("));
try!(value.to_css(dest));
try!(dest.write_str(")"));
}
SpecifiedFilter::Invert(value) => try!(write!(dest, "invert({})", value)),
SpecifiedFilter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
SpecifiedFilter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
SpecifiedFilter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::new(Vec::new())
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut filters = Vec::new();
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue(filters))
}
loop {
if let Ok(function_name) = input.try(|input| input.expect_function()) {
filters.push(try!(input.parse_nested_block(|input| {
match_ignore_ascii_case! { function_name,
"blur" => specified::Length::parse_non_negative(input).map(SpecifiedFilter::Blur),
"brightness" => parse_factor(input).map(SpecifiedFilter::Brightness),
"contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
"grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
"hue-rotate" => Angle::parse(input).map(SpecifiedFilter::HueRotate),
"invert" => parse_factor(input).map(SpecifiedFilter::Invert),
"opacity" => parse_factor(input).map(SpecifiedFilter::Opacity),
"saturate" => parse_factor(input).map(SpecifiedFilter::Saturate),
"sepia" => parse_factor(input).map(SpecifiedFilter::Sepia)
_ => Err(())
}
})));
} else if filters.is_empty() {
return Err(())
} else {
return Ok(SpecifiedValue(filters))
}
}
}
fn parse_factor(input: &mut Parser) -> Result<::values::CSSFloat, ()> {
use cssparser::Token;
match input.next() {
Ok(Token::Number(value)) => Ok(value.value),
Ok(Token::Percentage(value)) => Ok(value.unit_value),
_ => Err(())
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
fn to_computed_value(&self, context: &computed::Context) -> computed_value::T {
computed_value::T{ filters: self.0.iter().map(|value| {
match value {
&SpecifiedFilter::Blur(factor) => computed_value::Filter::Blur(factor.to_computed_value(context)),
&SpecifiedFilter::Brightness(factor) => computed_value::Filter::Brightness(factor),
&SpecifiedFilter::Contrast(factor) => computed_value::Filter::Contrast(factor),
&SpecifiedFilter::Grayscale(factor) => computed_value::Filter::Grayscale(factor),
&SpecifiedFilter::HueRotate(factor) => computed_value::Filter::HueRotate(factor),
&SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
&SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
&SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
&SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
}
}).collect() }
}
}
</%self:longhand>
<%self:longhand name="transform">
use values::CSSFloat;
use values::computed::{ToComputedValue, Context};
use cssparser::ToCss;
use std::f32;
use std::ops::Mul;
use std::fmt;
use util::geometry::Au;
pub mod computed_value {
use values::CSSFloat;
use values::computed;
use std::ops::Mul;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ComputedMatrix {
pub m11: CSSFloat, pub m12: CSSFloat,
pub m21: CSSFloat, pub m22: CSSFloat,
pub m31: computed::LengthAndPercentage, pub m32: computed::LengthAndPercentage,
}
impl Mul<ComputedMatrix> for ComputedMatrix {
type Output = ComputedMatrix;
fn mul(self, other: ComputedMatrix) -> ComputedMatrix {
ComputedMatrix {
m11: self.m11*other.m11 + self.m12*other.m21,
m12: self.m11*other.m12 + self.m12*other.m22,
m21: self.m21*other.m11 + self.m22*other.m21,
m22: self.m21*other.m12 + self.m22*other.m22,
m31: self.m31.clone()*other.m11 + self.m32.clone()*other.m21 + other.m31,
m32: self.m31*other.m12 + self.m32*other.m22 + other.m32,
}
}
}
impl ComputedMatrix {
#[inline]
fn new(m11: CSSFloat,
m12: CSSFloat,
m21: CSSFloat,
m22: CSSFloat,
m31: computed::LengthAndPercentage,
m32: computed::LengthAndPercentage)
-> ComputedMatrix {
ComputedMatrix {
m11: m11, m12: m12,
m21: m21, m22: m22,
m31: m31, m32: m32,
}
}
#[inline]
pub fn identity() -> ComputedMatrix {
ComputedMatrix {
m11: 1.0, m12: 0.0,
m21: 0.0, m22: 1.0,
m31: computed::LengthAndPercentage::zero(),
m32: computed::LengthAndPercentage::zero(),
}
}
pub fn scale(&mut self, sx: CSSFloat, sy: CSSFloat) {
*self = ComputedMatrix::new(sx, 0.0,
0.0, sy,
computed::LengthAndPercentage::zero(),
computed::LengthAndPercentage::zero()) *
(*self).clone()
}
pub fn skew(&mut self, sx: CSSFloat, sy: CSSFloat) {
*self = ComputedMatrix::new(1.0, sx,
sy, 1.0,
computed::LengthAndPercentage::zero(),
computed::LengthAndPercentage::zero()) *
(*self).clone()
}
pub fn translate(&mut self,
tx: computed::LengthAndPercentage,
ty: computed::LengthAndPercentage) {
*self = ComputedMatrix::new(1.0, 0.0, 0.0, 1.0, tx, ty) * (*self).clone()
}
pub fn rotate(&mut self, theta: CSSFloat) {
*self = ComputedMatrix::new(theta.cos(), -theta.sin(),
theta.sin(), theta.cos(),
computed::LengthAndPercentage::zero(),
computed::LengthAndPercentage::zero()) *
(*self).clone()
}
}
pub type T = Option<ComputedMatrix>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct SpecifiedMatrix {
m11: CSSFloat, m12: CSSFloat,
m21: CSSFloat, m22: CSSFloat,
m31: specified::LengthAndPercentage, m32: specified::LengthAndPercentage,
}
impl ToCss for SpecifiedMatrix {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
// TODO(pcwalton)
Ok(())
}
}
impl Mul<SpecifiedMatrix> for SpecifiedMatrix {
type Output = SpecifiedMatrix;
fn mul(self, other: SpecifiedMatrix) -> SpecifiedMatrix {
SpecifiedMatrix {
m11: self.m11*other.m11 + self.m12*other.m21,
m12: self.m11*other.m12 + self.m12*other.m22,
m21: self.m21*other.m11 + self.m22*other.m21,
m22: self.m21*other.m12 + self.m22*other.m22,
m31: self.m31.clone()*other.m11 + self.m32.clone()*other.m21 + other.m31,
m32: self.m31*other.m12 + self.m32*other.m22 + other.m32,
}
}
}
impl SpecifiedMatrix {
#[inline]
fn new(m11: CSSFloat,
m12: CSSFloat,
m21: CSSFloat,
m22: CSSFloat,
m31: specified::LengthAndPercentage,
m32: specified::LengthAndPercentage)
-> SpecifiedMatrix {
SpecifiedMatrix {
m11: m11, m12: m12,
m21: m21, m22: m22,
m31: m31, m32: m32,
}
}
}
fn parse_two_lengths_or_percentages(input: &mut Parser)
-> Result<(specified::LengthAndPercentage,
specified::LengthAndPercentage),()> {
let first = try!(specified::LengthAndPercentage::parse(input));
let second = input.try(|input| {
try!(input.expect_comma());
specified::LengthAndPercentage::parse(input)
}).unwrap_or(specified::LengthAndPercentage::zero());
Ok((first, second))
}
fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> {
let first = try!(input.expect_number());
let second = input.try(|input| {
try!(input.expect_comma());
input.expect_number()
}).unwrap_or(first);
Ok((first, second))
}
#[derive(Clone, Debug, PartialEq)]
enum SpecifiedOperation {
Matrix(SpecifiedMatrix),
Translate(specified::LengthAndPercentage, specified::LengthAndPercentage),
Scale(CSSFloat, CSSFloat),
Rotate(specified::Angle),
Skew(CSSFloat, CSSFloat),
}
impl ToCss for SpecifiedOperation {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
// TODO(pcwalton)
Ok(())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SpecifiedValue(Vec<SpecifiedOperation>);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut first = true;
for operation in self.0.iter() {
if !first {
try!(dest.write_str(" "));
}
first = false;
try!(operation.to_css(dest))
}
Ok(())
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(SpecifiedValue(Vec::new()))
}
let mut result = Vec::new();
loop {
let name = match input.expect_function() {
Ok(name) => name,
Err(_) => break,
};
match_ignore_ascii_case! {
name,
"matrix" => {
try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| {
input.expect_number()
}));
if values.len() != 6 {
return Err(())
}
let (tx, ty) =
(specified::Length::Absolute(Au::from_f32_px(values[4])),
specified::Length::Absolute(Au::from_f32_px(values[5])));
let (tx, ty) =
(specified::LengthAndPercentage::from_length(tx),
specified::LengthAndPercentage::from_length(ty));
result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix::new(values[0], values[1],
values[2], values[3],
tx, ty)));
Ok(())
}))
},
"translate" => {
try!(input.parse_nested_block(|input| {
let (tx, ty) = try!(parse_two_lengths_or_percentages(input));
result.push(SpecifiedOperation::Translate(tx, ty));
Ok(())
}))
},
"translatex" => {
try!(input.parse_nested_block(|input| {
let tx = try!(specified::LengthOrPercentage::parse(input));
result.push(SpecifiedOperation::Translate(
specified::LengthAndPercentage::from_length_or_percentage(
&tx),
specified::LengthAndPercentage::zero()));
Ok(())
}))
},
"translatey" => {
try!(input.parse_nested_block(|input| {
let ty = try!(specified::LengthOrPercentage::parse(input));
result.push(SpecifiedOperation::Translate(
specified::LengthAndPercentage::zero(),
specified::LengthAndPercentage::from_length_or_percentage(
&ty)));
Ok(())
}))
},
"scale" => {
try!(input.parse_nested_block(|input| {
let (sx, sy) = try!(parse_two_floats(input));
result.push(SpecifiedOperation::Scale(sx, sy));
Ok(())
}))
},
"scalex" => {
try!(input.parse_nested_block(|input| {
let sx = try!(input.expect_number());
result.push(SpecifiedOperation::Scale(sx, 1.0));
Ok(())
}))
},
"scaley" => {
try!(input.parse_nested_block(|input| {
let sy = try!(input.expect_number());
result.push(SpecifiedOperation::Scale(1.0, sy));
Ok(())
}))
},
"rotate" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(theta));
Ok(())
}))
},
"skew" => {
try!(input.parse_nested_block(|input| {
let (sx, sy) = try!(parse_two_floats(input));
result.push(SpecifiedOperation::Skew(sx, sy));
Ok(())
}))
},
"skewx" => {
try!(input.parse_nested_block(|input| {
let sx = try!(input.expect_number());
result.push(SpecifiedOperation::Skew(sx, 1.0));
Ok(())
}))
},
"skewy" => {
try!(input.parse_nested_block(|input| {
let sy = try!(input.expect_number());
result.push(SpecifiedOperation::Skew(1.0, sy));
Ok(())
}))
}
_ => return Err(())
}
}
if !result.is_empty() {
Ok(SpecifiedValue(result))
} else {
Err(())
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
if self.0.is_empty() {
return None
}
let mut result = computed_value::ComputedMatrix::identity();
for operation in self.0.iter() {
match *operation {
SpecifiedOperation::Matrix(ref matrix) => {
result = computed_value::ComputedMatrix {
m11: matrix.m11, m12: matrix.m12,
m21: matrix.m21, m22: matrix.m22,
m31: matrix.m31.to_computed_value(context),
m32: matrix.m32.to_computed_value(context),
} * result
}
SpecifiedOperation::Translate(ref tx, ref ty) => {
result.translate(tx.to_computed_value(context),
ty.to_computed_value(context))
}
SpecifiedOperation::Scale(sx, sy) => {
result.scale(sx, sy)
}
SpecifiedOperation::Rotate(ref theta) => {
result.rotate(f32::consts::PI_2 - theta.radians());
}
SpecifiedOperation::Skew(sx, sy) => {
result.skew(sx, sy)
}
}
}
Some(result)
}
}
</%self:longhand>
<%self:longhand name="transform-origin">
use values::computed::{ToComputedValue, Context};
use values::specified::LengthOrPercentage;
use cssparser::ToCss;
use std::fmt;
pub mod computed_value {
use values::computed::LengthOrPercentage;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct T {
pub horizontal: LengthOrPercentage,
pub vertical: LengthOrPercentage,
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SpecifiedValue {
horizontal: LengthOrPercentage,
vertical: LengthOrPercentage,
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.horizontal.to_css(dest));
try!(dest.write_str(" "));
self.vertical.to_css(dest)
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T {
horizontal: computed::LengthOrPercentage::Percentage(0.5),
vertical: computed::LengthOrPercentage::Percentage(0.5),
}
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
let (mut horizontal, mut vertical) = (None, None);
loop {
if let Err(_) = input.try(|input| {
let token = try!(input.expect_ident());
match_ignore_ascii_case! {
token,
"left" => {
if horizontal.is_none() {
horizontal = Some(LengthOrPercentage::Percentage(0.0))
} else {
return Err(())
}
},
"center" => {
if horizontal.is_none() {
horizontal = Some(LengthOrPercentage::Percentage(0.5))
} else if vertical.is_none() {
vertical = Some(LengthOrPercentage::Percentage(0.5))
} else {
return Err(())
}
},
"right" => {
if horizontal.is_none() {
horizontal = Some(LengthOrPercentage::Percentage(1.0))
} else {
return Err(())
}
},
"top" => {
if vertical.is_none() {
vertical = Some(LengthOrPercentage::Percentage(0.0))
} else {
return Err(())
}
},
"bottom" => {
if vertical.is_none() {
vertical = Some(LengthOrPercentage::Percentage(1.0))
} else {
return Err(())
}
}
_ => return Err(())
}
Ok(())
}) {
match LengthOrPercentage::parse(input) {
Ok(value) if horizontal.is_none() => horizontal = Some(value),
Ok(value) if vertical.is_none() => vertical = Some(value),
_ => break,
}
}
}
if horizontal.is_some() || vertical.is_some() {
Ok(SpecifiedValue {
horizontal: horizontal.unwrap_or(LengthOrPercentage::Percentage(0.5)),
vertical: vertical.unwrap_or(LengthOrPercentage::Percentage(0.5)),
})
} else {
Err(())
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
computed_value::T {
horizontal: self.horizontal.to_computed_value(context),
vertical: self.vertical.to_computed_value(context),
}
}
}
</%self:longhand>
${single_keyword("mix-blend-mode",
"""normal multiply screen overlay darken lighten color-dodge
color-burn hard-light soft-light difference exclusion hue
saturation color luminosity""")}
<%self:longhand name="image-rendering">
use values::computed::{Context, ToComputedValue};
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum T {
Auto,
CrispEdges,
Pixelated,
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
T::Auto => dest.write_str("auto"),
T::CrispEdges => dest.write_str("crisp-edges"),
T::Pixelated => dest.write_str("pixelated"),
}
}
}
}
pub type SpecifiedValue = computed_value::T;
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::Auto
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for
// `auto`.
match_ignore_ascii_case! {
try!(input.expect_ident()),
"auto" => Ok(computed_value::T::Auto),
"optimizespeed" => Ok(computed_value::T::Auto),
"optimizequality" => Ok(computed_value::T::Auto),
"crisp-edges" => Ok(computed_value::T::CrispEdges),
"pixelated" => Ok(computed_value::T::Pixelated)
_ => Err(())
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _: &Context) -> computed_value::T {
*self
}
}
</%self:longhand>
${new_style_struct("Animation", is_inherited=False)}
// TODO(pcwalton): Multiple transitions.
<%self:longhand name="transition-duration">
use values::specified::Time;
pub use self::computed_value::T as SpecifiedValue;
pub use values::specified::Time as SingleSpecifiedValue;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
use values::computed::{Context, ToComputedValue};
pub use values::computed::Time as SingleComputedValue;
#[derive(Clone, PartialEq)]
pub struct T(pub Vec<SingleComputedValue>);
impl ToComputedValue for T {
type ComputedValue = T;
#[inline]
fn to_computed_value(&self, _: &Context) -> T {
(*self).clone()
}
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.0.is_empty() {
return dest.write_str("none")
}
for (i, value) in self.0.iter().enumerate() {
if i != 0 {
try!(dest.write_str(", "))
}
try!(value.to_css(dest))
}
Ok(())
}
}
}
#[inline]
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
Time::parse(input)
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![get_initial_single_value()])
}
#[inline]
pub fn get_initial_single_value() -> Time {
Time(0.0)
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
}
</%self:longhand>
// TODO(pcwalton): Lots more timing functions.
// TODO(pcwalton): Multiple transitions.
<%self:longhand name="transition-timing-function">
use self::computed_value::{StartEnd, TransitionTimingFunction};
use values::computed::{Context, ToComputedValue};
use geom::point::Point2D;
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
pub use self::computed_value::T as SpecifiedValue;
static EASE: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
x: 0.25,
y: 0.1,
}, Point2D {
x: 0.25,
y: 1.0,
});
static LINEAR: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
x: 0.0,
y: 0.0,
}, Point2D {
x: 1.0,
y: 1.0,
});
static EASE_IN: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
x: 0.42,
y: 0.0,
}, Point2D {
x: 1.0,
y: 1.0,
});
static EASE_OUT: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
x: 0.0,
y: 0.0,
}, Point2D {
x: 0.58,
y: 1.0,
});
static EASE_IN_OUT: TransitionTimingFunction =
TransitionTimingFunction::CubicBezier(Point2D {
x: 0.42,
y: 0.0,
}, Point2D {
x: 0.58,
y: 1.0,
});
static STEP_START: TransitionTimingFunction =
TransitionTimingFunction::Steps(1, StartEnd::Start);
static STEP_END: TransitionTimingFunction =
TransitionTimingFunction::Steps(1, StartEnd::End);
pub mod computed_value {
use cssparser::ToCss;
use geom::point::Point2D;
use std::fmt;
pub use self::TransitionTimingFunction as SingleComputedValue;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TransitionTimingFunction {
CubicBezier(Point2D<f32>, Point2D<f32>),
Steps(u32, StartEnd),
}
impl ToCss for TransitionTimingFunction {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
TransitionTimingFunction::CubicBezier(p1, p2) => {
try!(dest.write_str("cubic-bezier("));
try!(p1.x.to_css(dest));
try!(dest.write_str(", "));
try!(p1.y.to_css(dest));
try!(dest.write_str(", "));
try!(p2.x.to_css(dest));
try!(dest.write_str(", "));
try!(p2.y.to_css(dest));
dest.write_str(")")
}
TransitionTimingFunction::Steps(steps, start_end) => {
try!(dest.write_str("steps("));
try!(steps.to_css(dest));
try!(dest.write_str(", "));
try!(start_end.to_css(dest));
dest.write_str(")")
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum StartEnd {
Start,
End,
}
impl ToCss for StartEnd {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
StartEnd::Start => dest.write_str("start"),
StartEnd::End => dest.write_str("end"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct T(pub Vec<TransitionTimingFunction>);
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.0.is_empty() {
return dest.write_str("none")
}
for (i, value) in self.0.iter().enumerate() {
if i != 0 {
try!(dest.write_str(", "))
}
try!(value.to_css(dest))
}
Ok(())
}
}
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _: &Context) -> computed_value::T {
(*self).clone()
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![get_initial_single_value()])
}
#[inline]
pub fn get_initial_single_value() -> TransitionTimingFunction {
EASE
}
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
if let Ok(function_name) = input.try(|input| input.expect_function()) {
return match_ignore_ascii_case! {
function_name,
"cubic-bezier" => {
let (mut p1x, mut p1y, mut p2x, mut p2y) = (0.0, 0.0, 0.0, 0.0);
try!(input.parse_nested_block(|input| {
p1x = try!(input.expect_number());
try!(input.expect_comma());
p1y = try!(input.expect_number());
try!(input.expect_comma());
p2x = try!(input.expect_number());
try!(input.expect_comma());
p2y = try!(input.expect_number());
Ok(())
}));
let (p1, p2) = (Point2D(p1x, p1y), Point2D(p2x, p2y));
Ok(TransitionTimingFunction::CubicBezier(p1, p2))
},
"steps" => {
let (mut step_count, mut start_end) = (0, computed_value::StartEnd::Start);
try!(input.parse_nested_block(|input| {
step_count = try!(input.expect_integer());
try!(input.expect_comma());
start_end = try!(match_ignore_ascii_case! {
try!(input.expect_ident()),
"start" => Ok(computed_value::StartEnd::Start),
"end" => Ok(computed_value::StartEnd::End)
_ => Err(())
});
Ok(())
}));
Ok(TransitionTimingFunction::Steps(step_count as u32, start_end))
}
_ => Err(())
}
}
match_ignore_ascii_case! {
try!(input.expect_ident()),
"ease" => Ok(EASE),
"linear" => Ok(LINEAR),
"ease-in" => Ok(EASE_IN),
"ease-out" => Ok(EASE_OUT),
"ease-in-out" => Ok(EASE_IN_OUT),
"step-start" => Ok(STEP_START),
"step-end" => Ok(STEP_END)
_ => Err(())
}
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
}
</%self:longhand>
// TODO(pcwalton): Lots more properties.
<%self:longhand name="transition-property">
use self::computed_value::TransitionProperty;
use values::computed::{ToComputedValue, Context};
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
pub use self::TransitionProperty as SingleComputedValue;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TransitionProperty {
All,
BackgroundColor,
BackgroundPosition,
BorderBottomColor,
BorderBottomWidth,
BorderLeftColor,
BorderLeftWidth,
BorderRightColor,
BorderRightWidth,
BorderSpacing,
BorderTopColor,
BorderTopWidth,
Bottom,
Color,
Clip,
FontSize,
FontWeight,
Height,
Left,
LetterSpacing,
LineHeight,
MarginBottom,
MarginLeft,
MarginRight,
MarginTop,
MaxHeight,
MaxWidth,
MinHeight,
MinWidth,
Opacity,
OutlineColor,
OutlineWidth,
PaddingBottom,
PaddingLeft,
PaddingRight,
PaddingTop,
Right,
TextIndent,
TextShadow,
Top,
VerticalAlign,
Visibility,
Width,
WordSpacing,
ZIndex,
}
pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 44] = [
TransitionProperty::BackgroundColor,
TransitionProperty::BackgroundPosition,
TransitionProperty::BorderBottomColor,
TransitionProperty::BorderBottomWidth,
TransitionProperty::BorderLeftColor,
TransitionProperty::BorderLeftWidth,
TransitionProperty::BorderRightColor,
TransitionProperty::BorderRightWidth,
TransitionProperty::BorderSpacing,
TransitionProperty::BorderTopColor,
TransitionProperty::BorderTopWidth,
TransitionProperty::Bottom,
TransitionProperty::Color,
TransitionProperty::Clip,
TransitionProperty::FontSize,
TransitionProperty::FontWeight,
TransitionProperty::Height,
TransitionProperty::Left,
TransitionProperty::LetterSpacing,
TransitionProperty::LineHeight,
TransitionProperty::MarginBottom,
TransitionProperty::MarginLeft,
TransitionProperty::MarginRight,
TransitionProperty::MarginTop,
TransitionProperty::MaxHeight,
TransitionProperty::MaxWidth,
TransitionProperty::MinHeight,
TransitionProperty::MinWidth,
TransitionProperty::Opacity,
TransitionProperty::OutlineColor,
TransitionProperty::OutlineWidth,
TransitionProperty::PaddingBottom,
TransitionProperty::PaddingLeft,
TransitionProperty::PaddingRight,
TransitionProperty::PaddingTop,
TransitionProperty::Right,
TransitionProperty::TextIndent,
TransitionProperty::TextShadow,
TransitionProperty::Top,
TransitionProperty::VerticalAlign,
TransitionProperty::Visibility,
TransitionProperty::Width,
TransitionProperty::WordSpacing,
TransitionProperty::ZIndex,
];
impl ToCss for TransitionProperty {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
TransitionProperty::All => dest.write_str("all"),
TransitionProperty::BackgroundColor => dest.write_str("background-color"),
TransitionProperty::BackgroundPosition => dest.write_str("background-position"),
TransitionProperty::BorderBottomColor => dest.write_str("border-bottom-color"),
TransitionProperty::BorderBottomWidth => dest.write_str("border-bottom-width"),
TransitionProperty::BorderLeftColor => dest.write_str("border-left-color"),
TransitionProperty::BorderLeftWidth => dest.write_str("border-left-width"),
TransitionProperty::BorderRightColor => dest.write_str("border-right-color"),
TransitionProperty::BorderRightWidth => dest.write_str("border-right-width"),
TransitionProperty::BorderSpacing => dest.write_str("border-spacing"),
TransitionProperty::BorderTopColor => dest.write_str("border-top-color"),
TransitionProperty::BorderTopWidth => dest.write_str("border-top-width"),
TransitionProperty::Bottom => dest.write_str("bottom"),
TransitionProperty::Color => dest.write_str("color"),
TransitionProperty::Clip => dest.write_str("clip"),
TransitionProperty::FontSize => dest.write_str("font-size"),
TransitionProperty::FontWeight => dest.write_str("font-weight"),
TransitionProperty::Height => dest.write_str("height"),
TransitionProperty::Left => dest.write_str("left"),
TransitionProperty::LetterSpacing => dest.write_str("letter-spacing"),
TransitionProperty::LineHeight => dest.write_str("line-height"),
TransitionProperty::MarginBottom => dest.write_str("margin-bottom"),
TransitionProperty::MarginLeft => dest.write_str("margin-left"),
TransitionProperty::MarginRight => dest.write_str("margin-right"),
TransitionProperty::MarginTop => dest.write_str("margin-top"),
TransitionProperty::MaxHeight => dest.write_str("max-height"),
TransitionProperty::MaxWidth => dest.write_str("max-width"),
TransitionProperty::MinHeight => dest.write_str("min-height"),
TransitionProperty::MinWidth => dest.write_str("min-width"),
TransitionProperty::Opacity => dest.write_str("opacity"),
TransitionProperty::OutlineColor => dest.write_str("outline-color"),
TransitionProperty::OutlineWidth => dest.write_str("outline-width"),
TransitionProperty::PaddingBottom => dest.write_str("padding-bottom"),
TransitionProperty::PaddingLeft => dest.write_str("padding-left"),
TransitionProperty::PaddingRight => dest.write_str("padding-right"),
TransitionProperty::PaddingTop => dest.write_str("padding-top"),
TransitionProperty::Right => dest.write_str("right"),
TransitionProperty::TextIndent => dest.write_str("text-indent"),
TransitionProperty::TextShadow => dest.write_str("text-shadow"),
TransitionProperty::Top => dest.write_str("top"),
TransitionProperty::VerticalAlign => dest.write_str("vertical-align"),
TransitionProperty::Visibility => dest.write_str("visibility"),
TransitionProperty::Width => dest.write_str("width"),
TransitionProperty::WordSpacing => dest.write_str("word-spacing"),
TransitionProperty::ZIndex => dest.write_str("z-index"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct T(pub Vec<SingleComputedValue>);
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.0.is_empty() {
return dest.write_str("none")
}
for (i, value) in self.0.iter().enumerate() {
if i != 0 {
try!(dest.write_str(", "))
}
try!(value.to_css(dest))
}
Ok(())
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(Vec::new())
}
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
match_ignore_ascii_case! {
try!(input.expect_ident()),
"all" => Ok(TransitionProperty::All),
"background-color" => Ok(TransitionProperty::BackgroundColor),
"background-position" => Ok(TransitionProperty::BackgroundPosition),
"border-bottom-color" => Ok(TransitionProperty::BorderBottomColor),
"border-bottom-width" => Ok(TransitionProperty::BorderBottomWidth),
"border-left-color" => Ok(TransitionProperty::BorderLeftColor),
"border-left-width" => Ok(TransitionProperty::BorderLeftWidth),
"border-right-color" => Ok(TransitionProperty::BorderRightColor),
"border-right-width" => Ok(TransitionProperty::BorderRightWidth),
"border-spacing" => Ok(TransitionProperty::BorderSpacing),
"border-top-color" => Ok(TransitionProperty::BorderTopColor),
"border-top-width" => Ok(TransitionProperty::BorderTopWidth),
"bottom" => Ok(TransitionProperty::Bottom),
"color" => Ok(TransitionProperty::Color),
"clip" => Ok(TransitionProperty::Clip),
"font-size" => Ok(TransitionProperty::FontSize),
"font-weight" => Ok(TransitionProperty::FontWeight),
"height" => Ok(TransitionProperty::Height),
"left" => Ok(TransitionProperty::Left),
"letter-spacing" => Ok(TransitionProperty::LetterSpacing),
"line-height" => Ok(TransitionProperty::LineHeight),
"margin-bottom" => Ok(TransitionProperty::MarginBottom),
"margin-left" => Ok(TransitionProperty::MarginLeft),
"margin-right" => Ok(TransitionProperty::MarginRight),
"margin-top" => Ok(TransitionProperty::MarginTop),
"max-height" => Ok(TransitionProperty::MaxHeight),
"max-width" => Ok(TransitionProperty::MaxWidth),
"min-height" => Ok(TransitionProperty::MinHeight),
"min-width" => Ok(TransitionProperty::MinWidth),
"opacity" => Ok(TransitionProperty::Opacity),
"outline-color" => Ok(TransitionProperty::OutlineColor),
"outline-width" => Ok(TransitionProperty::OutlineWidth),
"padding-bottom" => Ok(TransitionProperty::PaddingBottom),
"padding-left" => Ok(TransitionProperty::PaddingLeft),
"padding-right" => Ok(TransitionProperty::PaddingRight),
"padding-top" => Ok(TransitionProperty::PaddingTop),
"right" => Ok(TransitionProperty::Right),
"text-indent" => Ok(TransitionProperty::TextIndent),
"text-shadow" => Ok(TransitionProperty::TextShadow),
"top" => Ok(TransitionProperty::Top),
"vertical-align" => Ok(TransitionProperty::VerticalAlign),
"visibility" => Ok(TransitionProperty::Visibility),
"width" => Ok(TransitionProperty::Width),
"word-spacing" => Ok(TransitionProperty::WordSpacing),
"z-index" => Ok(TransitionProperty::ZIndex)
_ => Err(())
}
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
}
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _: &Context) -> computed_value::T {
(*self).clone()
}
}
</%self:longhand>
<%self:longhand name="transition-delay">
pub use properties::longhands::transition_duration::{SingleSpecifiedValue, SpecifiedValue};
pub use properties::longhands::transition_duration::{computed_value};
pub use properties::longhands::transition_duration::{get_initial_single_value};
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
</%self:longhand>
}
pub mod shorthands {
use cssparser::Parser;
use parser::ParserContext;
use values::specified;
<%def name="shorthand(name, sub_properties, experimental=False)">
<%
shorthand = Shorthand(name, sub_properties.split(), experimental=experimental)
SHORTHANDS.append(shorthand)
%>
pub mod ${shorthand.ident} {
use cssparser::Parser;
use parser::ParserContext;
use properties::longhands;
pub struct Longhands {
% for sub_property in shorthand.sub_properties:
pub ${sub_property.ident}:
Option<longhands::${sub_property.ident}::SpecifiedValue>,
% endfor
}
#[allow(unused_variables)]
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
${caller.body()}
}
}
</%def>
fn parse_four_sides<F, T>(input: &mut Parser, parse_one: F) -> Result<(T, T, T, T), ()>
where F: Fn(&mut Parser) -> Result<T, ()>, F: Copy, T: Clone {
// zero or more than four values is invalid.
// one value sets them all
// two values set (top, bottom) and (left, right)
// three values set top, (left, right) and bottom
// four values set them in order
let top = try!(parse_one(input));
let right;
let bottom;
let left;
match input.try(parse_one) {
Err(()) => {
right = top.clone();
bottom = top.clone();
left = top.clone();
}
Ok(value) => {
right = value;
match input.try(parse_one) {
Err(()) => {
bottom = top.clone();
left = right.clone();
}
Ok(value) => {
bottom = value;
match input.try(parse_one) {
Err(()) => {
left = right.clone();
}
Ok(value) => {
left = value;
}
}
}
}
}
}
Ok((top, right, bottom, left))
}
<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)">
<%self:shorthand name="${name}" sub_properties="${
' '.join(sub_property_pattern % side
for side in ['top', 'right', 'bottom', 'left'])}">
use values::specified;
use super::parse_four_sides;
let _unused = context;
let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function}));
Ok(Longhands {
% for side in ["top", "right", "bottom", "left"]:
${to_rust_ident(sub_property_pattern % side)}: Some(${side}),
% endfor
})
</%self:shorthand>
</%def>
// TODO: other background-* properties
<%self:shorthand name="background"
sub_properties="background-color background-position background-repeat background-attachment background-image background-size">
use properties::longhands::{background_color, background_position, background_repeat};
use properties::longhands::{background_attachment, background_image, background_size};
let mut color = None;
let mut image = None;
let mut position = None;
let mut repeat = None;
let mut size = None;
let mut attachment = None;
let mut any = false;
loop {
if position.is_none() {
if let Ok(value) = input.try(|input| background_position::parse(context, input)) {
position = Some(value);
any = true;
// Parse background size, if applicable.
size = input.try(|input| {
try!(input.expect_delim('/'));
background_size::parse(context, input)
}).ok();
continue
}
}
if color.is_none() {
if let Ok(value) = input.try(|input| background_color::parse(context, input)) {
color = Some(value);
any = true;
continue
}
}
if image.is_none() {
if let Ok(value) = input.try(|input| background_image::parse(context, input)) {
image = Some(value);
any = true;
continue
}
}
if repeat.is_none() {
if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) {
repeat = Some(value);
any = true;
continue
}
}
if attachment.is_none() {
if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) {
attachment = Some(value);
any = true;
continue
}
}
break
}
if any {
Ok(Longhands {
background_color: color,
background_image: image,
background_position: position,
background_repeat: repeat,
background_attachment: attachment,
background_size: size,
})
} else {
Err(())
}
</%self:shorthand>
${four_sides_shorthand("margin", "margin-%s", "specified::LengthOrPercentageOrAuto::parse")}
${four_sides_shorthand("padding", "padding-%s", "specified::LengthOrPercentage::parse")}
${four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse")}
${four_sides_shorthand("border-style", "border-%s-style",
"specified::BorderStyle::parse")}
<%self:shorthand name="border-width" sub_properties="${
' '.join('border-%s-width' % side
for side in ['top', 'right', 'bottom', 'left'])}">
use values::specified;
use super::parse_four_sides;
let _unused = context;
let (top, right, bottom, left) = try!(parse_four_sides(input, specified::parse_border_width));
Ok(Longhands {
% for side in ["top", "right", "bottom", "left"]:
${to_rust_ident('border-%s-width' % side)}:
Some(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue(${side})),
% endfor
})
</%self:shorthand>
pub fn parse_border(context: &ParserContext, input: &mut Parser)
-> Result<(Option<specified::CSSColor>,
Option<specified::BorderStyle>,
Option<specified::Length>), ()> {
use values::specified;
let _unused = context;
let mut color = None;
let mut style = None;
let mut width = None;
let mut any = false;
loop {
if color.is_none() {
if let Ok(value) = input.try(specified::CSSColor::parse) {
color = Some(value);
any = true;
continue
}
}
if style.is_none() {
if let Ok(value) = input.try(specified::BorderStyle::parse) {
style = Some(value);
any = true;
continue
}
}
if width.is_none() {
if let Ok(value) = input.try(specified::parse_border_width) {
width = Some(value);
any = true;
continue
}
}
break
}
if any { Ok((color, style, width)) } else { Err(()) }
}
% for side in ["top", "right", "bottom", "left"]:
<%self:shorthand name="border-${side}" sub_properties="${' '.join(
'border-%s-%s' % (side, prop)
for prop in ['color', 'style', 'width']
)}">
let (color, style, width) = try!(super::parse_border(context, input));
Ok(Longhands {
border_${side}_color: color,
border_${side}_style: style,
border_${side}_width: width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue),
})
</%self:shorthand>
% endfor
<%self:shorthand name="border" sub_properties="${' '.join(
'border-%s-%s' % (side, prop)
for side in ['top', 'right', 'bottom', 'left']
for prop in ['color', 'style', 'width']
)}">
let (color, style, width) = try!(super::parse_border(context, input));
Ok(Longhands {
% for side in ["top", "right", "bottom", "left"]:
border_${side}_color: color.clone(),
border_${side}_style: style,
border_${side}_width: width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue),
% endfor
})
</%self:shorthand>
<%self:shorthand name="border-radius" sub_properties="${' '.join(
'border-%s-radius' % (corner)
for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
)}">
use util::geometry::Au;
use values::specified::{Length, LengthOrPercentage};
let _ignored = context;
fn parse_one_set_of_border_radii(mut input: &mut Parser)
-> Result<[LengthOrPercentage; 4], ()> {
let mut count = 0;
let mut values = [LengthOrPercentage::Length(Length::Absolute(Au(0))); 4];
while count < 4 {
if let Ok(value) = input.try(LengthOrPercentage::parse) {
values[count] = value;
count += 1;
} else {
break
}
}
match count {
1 => Ok([values[0], values[0], values[0], values[0]]),
2 => Ok([values[0], values[1], values[0], values[1]]),
3 => Ok([values[0], values[1], values[2], values[1]]),
4 => Ok([values[0], values[1], values[2], values[3]]),
_ => Err(()),
}
}
let radii = try!(parse_one_set_of_border_radii(input));
// TODO(pcwalton): Elliptical borders.
Ok(Longhands {
border_top_left_radius: Some(radii[0]),
border_top_right_radius: Some(radii[1]),
border_bottom_right_radius: Some(radii[2]),
border_bottom_left_radius: Some(radii[3]),
})
</%self:shorthand>
<%self:shorthand name="outline" sub_properties="outline-color outline-style outline-width">
use values::specified;
let _unused = context;
let mut color = None;
let mut style = None;
let mut width = None;
let mut any = false;
loop {
if color.is_none() {
if let Ok(value) = input.try(specified::CSSColor::parse) {
color = Some(value);
any = true;
continue
}
}
if style.is_none() {
if let Ok(value) = input.try(specified::BorderStyle::parse) {
style = Some(value);
any = true;
continue
}
}
if width.is_none() {
if let Ok(value) = input.try(specified::parse_border_width) {
width = Some(value);
any = true;
continue
}
}
break
}
if any {
Ok(Longhands {
outline_color: color,
outline_style: style,
outline_width: width,
})
} else {
Err(())
}
</%self:shorthand>
<%self:shorthand name="font" sub_properties="font-style font-variant font-weight
font-size line-height font-family">
use properties::longhands::{font_style, font_variant, font_weight, font_size,
line_height, font_family};
let mut nb_normals = 0;
let mut style = None;
let mut variant = None;
let mut weight = None;
let size;
loop {
// Special-case 'normal' because it is valid in each of
// font-style, font-weight and font-variant.
// Leaves the values to None, 'normal' is the initial value for each of them.
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
nb_normals += 1;
continue;
}
if style.is_none() {
if let Ok(value) = input.try(|input| font_style::parse(context, input)) {
style = Some(value);
continue
}
}
if weight.is_none() {
if let Ok(value) = input.try(|input| font_weight::parse(context, input)) {
weight = Some(value);
continue
}
}
if variant.is_none() {
if let Ok(value) = input.try(|input| font_variant::parse(context, input)) {
variant = Some(value);
continue
}
}
size = Some(try!(font_size::parse(context, input)));
break
}
#[inline]
fn count<T>(opt: &Option<T>) -> u8 {
if opt.is_some() { 1 } else { 0 }
}
if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 {
return Err(())
}
let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
Some(try!(line_height::parse(context, input)))
} else {
None
};
let family = try!(input.parse_comma_separated(font_family::parse_one_family));
Ok(Longhands {
font_style: style,
font_variant: variant,
font_weight: weight,
font_size: size,
line_height: line_height,
font_family: Some(font_family::SpecifiedValue(family))
})
</%self:shorthand>
// Per CSS-TEXT 6.2, "for legacy reasons, UAs must treat `word-wrap` as an alternate name for
// the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`."
<%self:shorthand name="word-wrap" sub_properties="overflow-wrap">
use properties::longhands::overflow_wrap;
Ok(Longhands {
overflow_wrap: Some(try!(overflow_wrap::parse(context, input))),
})
</%self:shorthand>
<%self:shorthand name="list-style"
sub_properties="list-style-image list-style-position list-style-type">
use properties::longhands::{list_style_image, list_style_position, list_style_type};
// `none` is ambiguous until we've finished parsing the shorthands, so we count the number
// of times we see it.
let mut nones = 0u8;
let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false);
loop {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
nones = nones + 1;
if nones > 2 {
return Err(())
}
any = true;
continue
}
if list_style_type.is_none() {
if let Ok(value) = input.try(|input| list_style_type::parse(context, input)) {
list_style_type = Some(value);
any = true;
continue
}
}
if image.is_none() {
if let Ok(value) = input.try(|input| list_style_image::parse(context, input)) {
image = Some(value);
any = true;
continue
}
}
if position.is_none() {
if let Ok(value) = input.try(|input| list_style_position::parse(context, input)) {
position = Some(value);
any = true;
continue
}
}
break
}
// If there are two `none`s, then we can't have a type or image; if there is one `none`,
// then we can't have both a type *and* an image; if there is no `none` then we're fine as
// long as we parsed something.
match (any, nones, list_style_type, image) {
(true, 2, None, None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
}
(true, 1, None, Some(image)) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(image),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
}
(true, 1, Some(list_style_type), None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type),
})
}
(true, 1, None, None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
}
(true, 0, list_style_type, image) => {
Ok(Longhands {
list_style_position: position,
list_style_image: image,
list_style_type: list_style_type,
})
}
_ => Err(()),
}
</%self:shorthand>
<%self:shorthand name="columns" sub_properties="column-count column-width" experimental="True">
use properties::longhands::{column_count, column_width};
let mut column_count = None;
let mut column_width = None;
let mut autos = 0;
loop {
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
// Leave the options to None, 'auto' is the initial value.
autos += 1;
continue
}
if column_count.is_none() {
if let Ok(value) = input.try(|input| column_count::parse(context, input)) {
column_count = Some(value);
continue
}
}
if column_width.is_none() {
if let Ok(value) = input.try(|input| column_width::parse(context, input)) {
column_width = Some(value);
continue
}
}
break
}
let values = autos + column_count.iter().len() + column_width.iter().len();
if values == 0 || values > 2 {
Err(())
} else {
Ok(Longhands {
column_count: column_count,
column_width: column_width,
})
}
</%self:shorthand>
<%self:shorthand name="overflow" sub_properties="overflow-x overflow-y">
use properties::longhands::{overflow_x, overflow_y};
let overflow = try!(overflow_x::parse(context, input));
Ok(Longhands {
overflow_x: Some(overflow),
overflow_y: Some(overflow_y::SpecifiedValue(overflow)),
})
</%self:shorthand>
<%self:shorthand name="transition"
sub_properties="transition-property transition-duration transition-timing-function transition-delay">
use properties::longhands::{transition_delay, transition_duration, transition_property};
use properties::longhands::{transition_timing_function};
struct SingleTransition {
transition_property: transition_property::SingleSpecifiedValue,
transition_duration: transition_duration::SingleSpecifiedValue,
transition_timing_function: transition_timing_function::SingleSpecifiedValue,
transition_delay: transition_delay::SingleSpecifiedValue,
}
fn parse_one_transition(input: &mut Parser) -> Result<SingleTransition,()> {
let (mut property, mut duration) = (None, None);
let (mut timing_function, mut delay) = (None, None);
loop {
if property.is_none() {
if let Ok(value) = input.try(|input| transition_property::parse_one(input)) {
property = Some(value);
continue
}
}
if duration.is_none() {
if let Ok(value) = input.try(|input| transition_duration::parse_one(input)) {
duration = Some(value);
continue
}
}
if timing_function.is_none() {
if let Ok(value) = input.try(|input| {
transition_timing_function::parse_one(input)
}) {
timing_function = Some(value);
continue
}
}
if delay.is_none() {
if let Ok(value) = input.try(|input| transition_delay::parse_one(input)) {
delay = Some(value);
continue;
}
}
break
}
if let Some(property) = property {
Ok(SingleTransition {
transition_property: property,
transition_duration:
duration.unwrap_or(transition_duration::get_initial_single_value()),
transition_timing_function:
timing_function.unwrap_or(
transition_timing_function::get_initial_single_value()),
transition_delay:
delay.unwrap_or(transition_delay::get_initial_single_value()),
})
} else {
Err(())
}
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(Longhands {
transition_property: None,
transition_duration: None,
transition_timing_function: None,
transition_delay: None,
})
}
let results = try!(input.parse_comma_separated(parse_one_transition));
let (mut properties, mut durations) = (Vec::new(), Vec::new());
let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());
for result in results.into_iter() {
properties.push(result.transition_property);
durations.push(result.transition_duration);
timing_functions.push(result.transition_timing_function);
delays.push(result.transition_delay);
}
Ok(Longhands {
transition_property: Some(transition_property::SpecifiedValue(properties)),
transition_duration: Some(transition_duration::SpecifiedValue(durations)),
transition_timing_function:
Some(transition_timing_function::SpecifiedValue(timing_functions)),
transition_delay: Some(transition_delay::SpecifiedValue(delays)),
})
</%self:shorthand>
}
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
// Maybe submit for inclusion in libstd?
mod property_bit_field {
pub struct PropertyBitField {
storage: [u32; (${len(LONGHANDS)} - 1 + 32) / 32]
}
impl PropertyBitField {
#[inline]
pub fn new() -> PropertyBitField {
PropertyBitField { storage: [0; (${len(LONGHANDS)} - 1 + 32) / 32] }
}
#[inline]
fn get(&self, bit: usize) -> bool {
(self.storage[bit / 32] & (1 << (bit % 32))) != 0
}
#[inline]
fn set(&mut self, bit: usize) {
self.storage[bit / 32] |= 1 << (bit % 32)
}
% for i, property in enumerate(LONGHANDS):
% if property.derived_from is None:
#[allow(non_snake_case)]
#[inline]
pub fn get_${property.ident}(&self) -> bool {
self.get(${i})
}
#[allow(non_snake_case)]
#[inline]
pub fn set_${property.ident}(&mut self) {
self.set(${i})
}
% endif
% endfor
}
}
/// Declarations are stored in reverse order.
/// Overridden declarations are skipped.
#[derive(Debug, PartialEq)]
pub struct PropertyDeclarationBlock {
pub important: Arc<Vec<PropertyDeclaration>>,
pub normal: Arc<Vec<PropertyDeclaration>>,
}
pub fn parse_style_attribute(input: &str, base_url: &Url) -> PropertyDeclarationBlock {
let context = ParserContext::new(Origin::Author, base_url);
parse_property_declaration_list(&context, &mut Parser::new(input))
}
struct PropertyDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>,
}
/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> {
type Prelude = ();
type AtRule = (Vec<PropertyDeclaration>, bool);
}
impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
type Declaration = (Vec<PropertyDeclaration>, bool);
fn parse_value(&self, name: &str, input: &mut Parser) -> Result<(Vec<PropertyDeclaration>, bool), ()> {
let mut results = vec![];
match PropertyDeclaration::parse(name, self.context, input, &mut results) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}
_ => return Err(())
}
let important = input.try(parse_important).is_ok();
Ok((results, important))
}
}
pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser)
-> PropertyDeclarationBlock {
let mut important_declarations = Vec::new();
let mut normal_declarations = Vec::new();
let parser = PropertyDeclarationParser {
context: context,
};
let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() {
match declaration {
Ok((results, important)) => {
if important {
important_declarations.push_all(&results);
} else {
normal_declarations.push_all(&results);
}
}
Err(range) => {
let pos = range.start;
let message = format!("Unsupported property declaration: '{}'",
iter.input.slice(range));
log_css_error(iter.input, pos, &*message);
}
}
}
PropertyDeclarationBlock {
important: Arc::new(deduplicate_property_declarations(important_declarations)),
normal: Arc::new(deduplicate_property_declarations(normal_declarations)),
}
}
/// Only keep the last declaration for any given property.
/// The input is in source order, output in reverse source order.
fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>)
-> Vec<PropertyDeclaration> {
let mut deduplicated = vec![];
let mut seen = PropertyBitField::new();
for declaration in declarations.into_iter().rev() {
match declaration {
% for property in LONGHANDS:
PropertyDeclaration::${property.camel_case}(..) => {
% if property.derived_from is None:
if seen.get_${property.ident}() {
continue
}
seen.set_${property.ident}()
% else:
unreachable!();
% endif
},
% endfor
}
deduplicated.push(declaration)
}
deduplicated
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CSSWideKeyword {
InitialKeyword,
InheritKeyword,
UnsetKeyword,
}
impl CSSWideKeyword {
pub fn parse(input: &mut Parser) -> Result<CSSWideKeyword, ()> {
match_ignore_ascii_case! { try!(input.expect_ident()),
"initial" => Ok(CSSWideKeyword::InitialKeyword),
"inherit" => Ok(CSSWideKeyword::InheritKeyword),
"unset" => Ok(CSSWideKeyword::UnsetKeyword)
_ => Err(())
}
}
}
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
pub enum DeclaredValue<T> {
SpecifiedValue(T),
Initial,
Inherit,
// There is no Unset variant here.
// The 'unset' keyword is represented as either Initial or Inherit,
// depending on whether the property is inherited.
}
impl<T: ToCss> DeclaredValue<T> {
pub fn specified_value(&self) -> String {
match self {
&DeclaredValue::SpecifiedValue(ref inner) => inner.to_css_string(),
&DeclaredValue::Initial => "initial".to_owned(),
&DeclaredValue::Inherit => "inherit".to_owned(),
}
}
}
#[derive(Clone, PartialEq)]
pub enum PropertyDeclaration {
% for property in LONGHANDS:
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
% endfor
}
#[derive(Eq, PartialEq, Copy, Clone)]
pub enum PropertyDeclarationParseResult {
UnknownProperty,
ExperimentalProperty,
InvalidValue,
ValidOrIgnoredDeclaration,
}
impl PropertyDeclaration {
pub fn name(&self) -> &'static str {
match self {
% for property in LONGHANDS:
% if property.derived_from is None:
&PropertyDeclaration::${property.camel_case}(..) => "${property.name}",
% endif
% endfor
_ => "",
}
}
pub fn value(&self) -> String {
match self {
% for property in LONGHANDS:
% if property.derived_from is None:
&PropertyDeclaration::${property.camel_case}(ref value) =>
value.specified_value(),
% endif
% endfor
decl => panic!("unsupported property declaration: {:?}", decl.name()),
}
}
pub fn matches(&self, name: &str) -> bool {
match *self {
% for property in LONGHANDS:
% if property.derived_from is None:
PropertyDeclaration::${property.camel_case}(..) => {
name.eq_ignore_ascii_case("${property.name}")
}
% endif
% endfor
_ => false,
}
}
pub fn parse(name: &str, context: &ParserContext, input: &mut Parser,
result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult {
match_ignore_ascii_case! { name,
% for property in LONGHANDS:
% if property.derived_from is None:
"${property.name}" => {
% if property.experimental:
if !::util::opts::experimental_enabled() {
return PropertyDeclarationParseResult::ExperimentalProperty
}
% endif
match longhands::${property.ident}::parse_declared(context, input) {
Ok(value) => {
result_list.push(PropertyDeclaration::${property.camel_case}(value));
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Err(()) => PropertyDeclarationParseResult::InvalidValue,
}
},
% else:
"${property.name}" => PropertyDeclarationParseResult::UnknownProperty,
% endif
% endfor
% for shorthand in SHORTHANDS:
"${shorthand.name}" => {
% if shorthand.experimental:
if !::util::opts::experimental_enabled() {
return PropertyDeclarationParseResult::ExperimentalProperty
}
% endif
match input.try(CSSWideKeyword::parse) {
Ok(CSSWideKeyword::InheritKeyword) => {
% for sub_property in shorthand.sub_properties:
result_list.push(
PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::Inherit));
% endfor
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Ok(CSSWideKeyword::InitialKeyword) => {
% for sub_property in shorthand.sub_properties:
result_list.push(
PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::Initial));
% endfor
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Ok(CSSWideKeyword::UnsetKeyword) => {
% for sub_property in shorthand.sub_properties:
result_list.push(PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::${"Inherit" if sub_property.style_struct.inherited else "Initial"}
));
% endfor
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Err(()) => match shorthands::${shorthand.ident}::parse(context, input) {
Ok(result) => {
% for sub_property in shorthand.sub_properties:
result_list.push(PropertyDeclaration::${sub_property.camel_case}(
match result.${sub_property.ident} {
Some(value) => DeclaredValue::SpecifiedValue(value),
None => DeclaredValue::Initial,
}
));
% endfor
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Err(()) => PropertyDeclarationParseResult::InvalidValue,
}
}
},
% endfor
// Hack to work around quirks of macro_rules parsing in match_ignore_ascii_case!
"_nonexistent" => PropertyDeclarationParseResult::UnknownProperty
_ => PropertyDeclarationParseResult::UnknownProperty
}
}
}
impl Debug for PropertyDeclaration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.name(), self.value())
}
}
pub mod style_structs {
use super::longhands;
% for style_struct in STYLE_STRUCTS:
#[derive(PartialEq, Clone)]
pub struct ${style_struct.name} {
% for longhand in style_struct.longhands:
pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
% endfor
% if style_struct.name == "Font":
pub hash: u64,
% endif
}
% endfor
}
#[derive(Clone)]
pub struct ComputedValues {
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
shareable: bool,
pub writing_mode: WritingMode,
pub root_font_size: Au,
}
impl ComputedValues {
/// Resolves the currentColor keyword.
/// Any color value form computed values (except for the 'color' property itself)
/// should go through this method.
///
/// Usage example:
/// let top_color = style.resolve_color(style.Border.border_top_color);
#[inline]
pub fn resolve_color(&self, color: Color) -> RGBA {
match color {
Color::RGBA(rgba) => rgba,
Color::CurrentColor => self.get_color().color,
}
}
#[inline]
pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto {
let box_style = self.get_box();
if self.writing_mode.is_vertical() {
box_style.height
} else {
box_style.width
}
}
#[inline]
pub fn content_block_size(&self) -> computed::LengthOrPercentageOrAuto {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.width } else { box_style.height }
}
#[inline]
pub fn min_inline_size(&self) -> computed::LengthOrPercentage {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.min_height } else { box_style.min_width }
}
#[inline]
pub fn min_block_size(&self) -> computed::LengthOrPercentage {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.min_width } else { box_style.min_height }
}
#[inline]
pub fn max_inline_size(&self) -> computed::LengthOrPercentageOrNone {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.max_height } else { box_style.max_width }
}
#[inline]
pub fn max_block_size(&self) -> computed::LengthOrPercentageOrNone {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.max_width } else { box_style.max_height }
}
#[inline]
pub fn logical_padding(&self) -> LogicalMargin<computed::LengthOrPercentage> {
let padding_style = self.get_padding();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
padding_style.padding_top,
padding_style.padding_right,
padding_style.padding_bottom,
padding_style.padding_left,
))
}
#[inline]
pub fn logical_border_width(&self) -> LogicalMargin<Au> {
let border_style = self.get_border();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
border_style.border_top_width,
border_style.border_right_width,
border_style.border_bottom_width,
border_style.border_left_width,
))
}
#[inline]
pub fn logical_margin(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto> {
let margin_style = self.get_margin();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
margin_style.margin_top,
margin_style.margin_right,
margin_style.margin_bottom,
margin_style.margin_left,
))
}
#[inline]
pub fn logical_position(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto> {
// FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
let position_style = self.get_positionoffsets();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
position_style.top,
position_style.right,
position_style.bottom,
position_style.left,
))
}
#[inline]
pub fn is_multicol(&self) -> bool {
let style = self.get_column();
style.column_count.is_some() || style.column_width.is_some()
}
#[inline]
pub fn get_font_arc(&self) -> Arc<style_structs::Font> {
self.font.clone()
}
% for style_struct in STYLE_STRUCTS:
#[inline]
pub fn get_${style_struct.name.lower()}
<'a>(&'a self) -> &'a style_structs::${style_struct.name} {
&*self.${style_struct.ident}
}
#[inline]
pub fn mutate_${style_struct.name.lower()}
<'a>(&'a mut self) -> &'a mut style_structs::${style_struct.name} {
&mut *self.${style_struct.ident}.make_unique()
}
% endfor
}
/// Return a WritingMode bitflags from the relevant CSS properties.
pub fn get_writing_mode(inheritedbox_style: &style_structs::InheritedBox) -> WritingMode {
use util::logical_geometry;
let mut flags = WritingMode::empty();
match inheritedbox_style.direction {
computed_values::direction::T::ltr => {},
computed_values::direction::T::rtl => {
flags.insert(logical_geometry::FLAG_RTL);
},
}
match inheritedbox_style.writing_mode {
computed_values::writing_mode::T::horizontal_tb => {},
computed_values::writing_mode::T::vertical_rl => {
flags.insert(logical_geometry::FLAG_VERTICAL);
},
computed_values::writing_mode::T::vertical_lr => {
flags.insert(logical_geometry::FLAG_VERTICAL);
flags.insert(logical_geometry::FLAG_VERTICAL_LR);
},
}
match inheritedbox_style.text_orientation {
computed_values::text_orientation::T::sideways_right => {},
computed_values::text_orientation::T::sideways_left => {
flags.insert(logical_geometry::FLAG_VERTICAL_LR);
},
computed_values::text_orientation::T::sideways => {
if flags.intersects(logical_geometry::FLAG_VERTICAL_LR) {
flags.insert(logical_geometry::FLAG_SIDEWAYS_LEFT);
}
},
}
flags
}
/// The initial values for all style structs as defined by the specification.
lazy_static! {
pub static ref INITIAL_VALUES: ComputedValues = ComputedValues {
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
% for longhand in style_struct.longhands:
${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
% endfor
% if style_struct.name == "Font":
hash: 0,
% endif
}),
% endfor
shareable: true,
writing_mode: WritingMode::empty(),
root_font_size: longhands::font_size::get_initial_value(),
};
}
/// Fast path for the function below. Only computes new inherited styles.
#[allow(unused_mut)]
fn cascade_with_cached_declarations(
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
shareable: bool,
parent_style: &ComputedValues,
cached_style: &ComputedValues,
context: &computed::Context)
-> ComputedValues {
% for style_struct in STYLE_STRUCTS:
% if style_struct.inherited:
let mut style_${style_struct.ident} = parent_style.${style_struct.ident}.clone();
% else:
let mut style_${style_struct.ident} = cached_style.${style_struct.ident}.clone();
% endif
% endfor
let mut seen = PropertyBitField::new();
// Declaration blocks are stored in increasing precedence order,
// we want them in decreasing order here.
for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order.
for declaration in sub_list.declarations.iter() {
match *declaration {
% for style_struct in STYLE_STRUCTS:
% for property in style_struct.longhands:
% if property.derived_from is None:
PropertyDeclaration::${property.camel_case}(ref
${'_' if not style_struct.inherited else ''}declared_value)
=> {
% if style_struct.inherited:
if seen.get_${property.ident}() {
continue
}
seen.set_${property.ident}();
let computed_value = match *declared_value {
DeclaredValue::SpecifiedValue(ref specified_value)
=> specified_value.to_computed_value(context),
DeclaredValue::Initial
=> longhands::${property.ident}::get_initial_value(),
DeclaredValue::Inherit => {
// This is a bit slow, but this is rare so it shouldn't
// matter.
//
// FIXME: is it still?
parent_style.${style_struct.ident}
.${property.ident}
.clone()
}
};
style_${style_struct.ident}.make_unique()
.${property.ident} = computed_value;
% endif
% if property.name in DERIVED_LONGHANDS:
% if not style_struct.inherited:
// Use the cached value.
let computed_value = style_${style_struct.ident}
.${property.ident}.clone();
% endif
% for derived in DERIVED_LONGHANDS[property.name]:
style_${derived.style_struct.ident}
.make_unique()
.${derived.ident} =
longhands::${derived.ident}
::derive_from_${property.ident}(
computed_value,
context);
% endfor
% endif
}
% else:
PropertyDeclaration::${property.camel_case}(_) => {
// Do not allow stylesheets to set derived properties.
}
% endif
% endfor
% endfor
}
}
}
if seen.get_font_style() || seen.get_font_weight() || seen.get_font_stretch() ||
seen.get_font_family() {
compute_font_hash(&mut *style_font.make_unique())
}
ComputedValues {
writing_mode: get_writing_mode(&*style_inheritedbox),
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: style_${style_struct.ident},
% endfor
shareable: shareable,
root_font_size: parent_style.root_font_size,
}
}
/// Performs the CSS cascade, computing new styles for an element from its parent style and
/// optionally a cached related style. The arguments are:
///
/// * `viewport_size`: The size of the initial viewport.
///
/// * `applicable_declarations`: The list of CSS rules that matched.
///
/// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered
/// shareable.
///
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
///
/// * `cached_style`: If present, cascading is short-circuited for everything but inherited
/// values and these values are used instead. Obviously, you must be careful when supplying
/// this that it is safe to only provide inherited declarations. If `parent_style` is `None`,
/// this is ignored.
///
/// Returns the computed values and a boolean indicating whether the result is cacheable.
pub fn cascade(viewport_size: Size2D<Au>,
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
shareable: bool,
parent_style: Option< &ComputedValues >,
cached_style: Option< &ComputedValues >)
-> (ComputedValues, bool) {
let initial_values = &*INITIAL_VALUES;
let (is_root_element, inherited_style) = match parent_style {
Some(parent_style) => (false, parent_style),
None => (true, initial_values),
};
let mut context = {
let inherited_font_style = inherited_style.get_font();
computed::Context {
is_root_element: is_root_element,
viewport_size: viewport_size,
inherited_font_weight: inherited_font_style.font_weight,
inherited_font_size: inherited_font_style.font_size,
inherited_height: inherited_style.get_box().height,
inherited_text_decorations_in_effect:
inherited_style.get_inheritedtext()._servo_text_decorations_in_effect,
// To be overridden by applicable declarations:
font_size: inherited_font_style.font_size,
root_font_size: inherited_style.root_font_size,
display: longhands::display::get_initial_value(),
color: inherited_style.get_color().color,
text_decoration: longhands::text_decoration::get_initial_value(),
overflow_x: longhands::overflow_x::get_initial_value(),
overflow_y: longhands::overflow_y::get_initial_value(),
positioned: false,
floated: false,
border_top_present: false,
border_right_present: false,
border_bottom_present: false,
border_left_present: false,
}
};
// This assumes that the computed and specified values have the same Rust type.
macro_rules! get_specified(
($style_struct_getter: ident, $property: ident, $declared_value: expr) => {
match *$declared_value {
DeclaredValue::SpecifiedValue(specified_value) => specified_value,
DeclaredValue::Initial => longhands::$property::get_initial_value(),
DeclaredValue::Inherit => inherited_style.$style_struct_getter().$property.clone(),
}
};
);
// Initialize `context`
// Declarations blocks are already stored in increasing precedence order.
for sub_list in applicable_declarations.iter() {
// Declarations are stored in reverse source order, we want them in forward order here.
for declaration in sub_list.declarations.iter().rev() {
match *declaration {
PropertyDeclaration::FontSize(ref value) => {
context.font_size = match *value {
DeclaredValue::SpecifiedValue(ref specified_value) => {
match specified_value.0 {
Length::FontRelative(value) => {
value.to_computed_value(context.inherited_font_size,
context.root_font_size)
}
Length::ServoCharacterWidth(value) => {
value.to_computed_value(context.inherited_font_size)
}
_ => specified_value.0.to_computed_value(&context)
}
}
DeclaredValue::Initial => longhands::font_size::get_initial_value(),
DeclaredValue::Inherit => context.inherited_font_size,
}
}
PropertyDeclaration::Color(ref value) => {
context.color = match *value {
DeclaredValue::SpecifiedValue(ref specified_value) => {
specified_value.parsed
}
DeclaredValue::Initial => longhands::color::get_initial_value(),
DeclaredValue::Inherit => inherited_style.get_color().color.clone(),
};
}
PropertyDeclaration::Display(ref value) => {
context.display = get_specified!(get_box, display, value);
}
PropertyDeclaration::Position(ref value) => {
context.positioned = match get_specified!(get_box, position, value) {
longhands::position::SpecifiedValue::absolute |
longhands::position::SpecifiedValue::fixed => true,
_ => false,
}
}
PropertyDeclaration::OverflowX(ref value) => {
context.overflow_x = get_specified!(get_box, overflow_x, value);
}
PropertyDeclaration::OverflowY(ref value) => {
context.overflow_y = get_specified!(get_box, overflow_y, value);
}
PropertyDeclaration::Float(ref value) => {
context.floated = get_specified!(get_box, float, value)
!= longhands::float::SpecifiedValue::none;
}
PropertyDeclaration::TextDecoration(ref value) => {
context.text_decoration = get_specified!(get_text, text_decoration, value);
}
% for side in ["top", "right", "bottom", "left"]:
PropertyDeclaration::Border${side.capitalize()}Style(ref value) => {
context.border_${side}_present =
match get_specified!(get_border, border_${side}_style, value) {
BorderStyle::none | BorderStyle::hidden => false,
_ => true,
};
}
% endfor
_ => {}
}
}
}
match (cached_style, parent_style) {
(Some(cached_style), Some(parent_style)) => {
return (cascade_with_cached_declarations(applicable_declarations,
shareable,
parent_style,
cached_style,
&context), false)
}
(_, _) => {}
}
// Set computed values, overwriting earlier declarations for the same property.
% for style_struct in STYLE_STRUCTS:
let mut style_${style_struct.ident} =
% if style_struct.inherited:
inherited_style
% else:
initial_values
% endif
.${style_struct.ident}.clone();
% endfor
let mut cacheable = true;
let mut seen = PropertyBitField::new();
// Declaration blocks are stored in increasing precedence order,
// we want them in decreasing order here.
for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order.
for declaration in sub_list.declarations.iter() {
match *declaration {
% for style_struct in STYLE_STRUCTS:
% for property in style_struct.longhands:
% if property.derived_from is None:
PropertyDeclaration::${property.camel_case}(ref declared_value) => {
if seen.get_${property.ident}() {
continue
}
seen.set_${property.ident}();
let computed_value = match *declared_value {
DeclaredValue::SpecifiedValue(ref specified_value)
=> specified_value.to_computed_value(&context),
DeclaredValue::Initial
=> longhands::${property.ident}::get_initial_value(),
DeclaredValue::Inherit => {
// This is a bit slow, but this is rare so it shouldn't
// matter.
//
// FIXME: is it still?
cacheable = false;
inherited_style.${style_struct.ident}
.${property.ident}
.clone()
}
};
style_${style_struct.ident}.make_unique()
.${property.ident} = computed_value;
% if property.name in DERIVED_LONGHANDS:
% for derived in DERIVED_LONGHANDS[property.name]:
style_${derived.style_struct.ident}
.make_unique()
.${derived.ident} =
longhands::${derived.ident}
::derive_from_${property.ident}(
computed_value,
&context);
% endfor
% endif
}
% else:
PropertyDeclaration::${property.camel_case}(_) => {
// Do not allow stylesheets to set derived properties.
}
% endif
% endfor
% endfor
}
}
}
// The initial value of border-*-width may be changed at computed value time.
{
let border = style_border.make_unique();
% for side in ["top", "right", "bottom", "left"]:
// Like calling to_computed_value, which wouldn't type check.
if !context.border_${side}_present {
border.border_${side}_width = Au(0);
}
% endfor
}
// The initial value of display may be changed at computed value time.
if !seen.get_display() {
let box_ = style_box_.make_unique();
box_.display = box_.display.to_computed_value(&context);
}
if is_root_element {
context.root_font_size = context.font_size;
}
if seen.get_font_style() || seen.get_font_weight() || seen.get_font_stretch() ||
seen.get_font_family() {
compute_font_hash(&mut *style_font.make_unique())
}
(ComputedValues {
writing_mode: get_writing_mode(&*style_inheritedbox),
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: style_${style_struct.ident},
% endfor
shareable: shareable,
root_font_size: context.root_font_size,
}, cacheable)
}
/// Equivalent to `cascade()` with an empty `applicable_declarations`
/// Performs the CSS cascade for an anonymous box.
///
/// * `parent_style`: Computed style of the element this anonymous box inherits from.
pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues {
let initial_values = &*INITIAL_VALUES;
let mut result = ComputedValues {
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}:
% if style_struct.inherited:
parent_style
% else:
initial_values
% endif
.${style_struct.ident}.clone(),
% endfor
shareable: false,
writing_mode: parent_style.writing_mode,
root_font_size: parent_style.root_font_size,
};
{
let border = result.border.make_unique();
% for side in ["top", "right", "bottom", "left"]:
// Like calling to_computed_value, which wouldn't type check.
border.border_${side}_width = Au(0);
% endfor
}
// None of the teaks on 'display' apply here.
result
}
/// Alters the given style to accommodate replaced content. This is called in flow construction. It
/// handles cases like `<div style="position: absolute">foo bar baz</div>` (in which `foo`, `bar`,
/// and `baz` must not be absolutely-positioned) and cases like `<sup>Foo</sup>` (in which the
/// `vertical-align: top` style of `sup` must not propagate down into `Foo`).
///
/// FIXME(#5625, pcwalton): It would probably be cleaner and faster to do this in the cascade.
#[inline]
pub fn modify_style_for_replaced_content(style: &mut Arc<ComputedValues>) {
// Reset `position` to handle cases like `<div style="position: absolute">foo bar baz</div>`.
if style.box_.display != longhands::display::computed_value::T::inline {
let mut style = style.make_unique();
style.box_.make_unique().display = longhands::display::computed_value::T::inline;
style.box_.make_unique().position = longhands::position::computed_value::T::static_;
}
// Reset `vertical-align` to handle cases like `<sup>foo</sup>`.
if style.box_.vertical_align != longhands::vertical_align::computed_value::T::baseline {
let mut style = style.make_unique();
style.box_.make_unique().vertical_align =
longhands::vertical_align::computed_value::T::baseline
}
// Reset margins.
if style.margin.margin_top != computed::LengthOrPercentageOrAuto::Length(Au(0)) ||
style.margin.margin_left != computed::LengthOrPercentageOrAuto::Length(Au(0)) ||
style.margin.margin_bottom != computed::LengthOrPercentageOrAuto::Length(Au(0)) ||
style.margin.margin_right != computed::LengthOrPercentageOrAuto::Length(Au(0)) {
let mut style = style.make_unique();
let margin = style.margin.make_unique();
margin.margin_top = computed::LengthOrPercentageOrAuto::Length(Au(0));
margin.margin_left = computed::LengthOrPercentageOrAuto::Length(Au(0));
margin.margin_bottom = computed::LengthOrPercentageOrAuto::Length(Au(0));
margin.margin_right = computed::LengthOrPercentageOrAuto::Length(Au(0));
}
}
/// Adjusts borders, padding, and margins as appropriate to account for a fragment's status as the
/// first or last fragment within the range of an element.
///
/// Specifically, this function sets border/padding/margin widths to zero on the sides for which
/// the fragment is not outermost.
#[inline]
pub fn modify_style_for_inline_sides(style: &mut Arc<ComputedValues>,
is_first_fragment_of_element: bool,
is_last_fragment_of_element: bool) {
fn modify_side(style: &mut Arc<ComputedValues>, side: PhysicalSide) {
let mut style = style.make_unique();
let border = style.border.make_unique();
match side {
PhysicalSide::Left => {
border.border_left_width = Au(0);
border.border_left_style = BorderStyle::none;
style.padding.make_unique().padding_left =
computed::LengthOrPercentage::Length(Au(0));
style.margin.make_unique().margin_left =
computed::LengthOrPercentageOrAuto::Length(Au(0))
}
PhysicalSide::Right => {
border.border_right_width = Au(0);
border.border_right_style = BorderStyle::none;
style.padding.make_unique().padding_right =
computed::LengthOrPercentage::Length(Au(0));
style.margin.make_unique().margin_right =
computed::LengthOrPercentageOrAuto::Length(Au(0))
}
PhysicalSide::Bottom => {
border.border_bottom_width = Au(0);
border.border_bottom_style = BorderStyle::none;
style.padding.make_unique().padding_bottom =
computed::LengthOrPercentage::Length(Au(0));
style.margin.make_unique().margin_bottom =
computed::LengthOrPercentageOrAuto::Length(Au(0))
}
PhysicalSide::Top => {
border.border_top_width = Au(0);
border.border_top_style = BorderStyle::none;
style.padding.make_unique().padding_top =
computed::LengthOrPercentage::Length(Au(0));
style.margin.make_unique().margin_top =
computed::LengthOrPercentageOrAuto::Length(Au(0))
}
}
}
if !is_first_fragment_of_element {
let side = style.writing_mode.inline_start_physical_side();
modify_side(style, side)
}
if !is_last_fragment_of_element {
let side = style.writing_mode.inline_end_physical_side();
modify_side(style, side)
}
}
pub fn is_supported_property(property: &str) -> bool {
match property {
% for property in SHORTHANDS + LONGHANDS:
"${property.name}" => true,
% endfor
_ => false,
}
}
#[macro_export]
macro_rules! css_properties_accessors {
($macro_name: ident) => {
$macro_name! {
% for property in SHORTHANDS + LONGHANDS:
% if property.derived_from is None:
% if property != LONGHANDS[-1]:
[${property.camel_case}, Set${property.camel_case}, "${property.name}"],
% else:
[${property.camel_case}, Set${property.camel_case}, "${property.name}"]
% endif
% endif
% endfor
}
}
}
macro_rules! longhand_properties_idents {
($macro_name: ident) => {
$macro_name! {
% for property in LONGHANDS:
${property.ident}
% endfor
}
}
}
pub fn longhands_from_shorthand(shorthand: &str) -> Option<Vec<String>> {
match shorthand {
% for property in SHORTHANDS:
"${property.name}" => Some(vec!(
% for sub in property.sub_properties:
"${sub.name}".to_owned(),
% endfor
)),
% endfor
_ => None,
}
}
/// Corresponds to the fields in `gfx::font_template::FontTemplateDescriptor`.
fn compute_font_hash(font: &mut style_structs::Font) {
let mut hasher: FnvHasher = Default::default();
hasher.write_u16(font.font_weight as u16);
font.font_stretch.hash(&mut hasher);
font.font_family.hash(&mut hasher);
font.hash = hasher.finish()
}