diff options
21 files changed, 4440 insertions, 4361 deletions
diff --git a/components/style/properties/data.py b/components/style/properties/data.py index fd741f9713a..14ec494bfba 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -128,13 +128,6 @@ class PropertiesData(object): def active_style_structs(self): return [s for s in self.style_structs if s.additional_methods or s.longhands] - def switch_to_style_struct(self, name): - for style_struct in self.style_structs: - if style_struct.trait_name == name: - self.current_style_struct = style_struct - return - raise Exception("Failed to find the struct named " + name) - def declare_longhand(self, name, products="gecko servo", **kwargs): products = products.split() if self.product not in products: diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs new file mode 100644 index 00000000000..65a2e857db7 --- /dev/null +++ b/components/style/properties/longhand/background.mako.rs @@ -0,0 +1,327 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("Background", inherited=False, gecko_ffi_name="nsStyleBackground") %> +${helpers.predefined_type( + "background-color", "CSSColor", + "::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")} + +<%helpers:longhand name="background-image"> + use cssparser::ToCss; + use std::fmt; + use values::specified::Image; + use values::LocalToCss; + + pub mod computed_value { + use values::computed; + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<computed::Image>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("none"), + Some(computed::Image::Url(ref url)) => url.to_css(dest), + Some(computed::Image::LinearGradient(ref gradient)) => + gradient.to_css(dest) + } + } + } + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + 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 { + 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue(None) => computed_value::T(None), + SpecifiedValue(Some(ref image)) => + computed_value::T(Some(image.to_computed_value(context))), + } + } + } +</%helpers:longhand> + +<%helpers:longhand name="background-position"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + + pub mod computed_value { + use values::computed::LengthOrPercentage; + + #[derive(PartialEq, Copy, Clone, Debug, HeapSizeOf)] + pub struct T { + pub horizontal: LengthOrPercentage, + pub vertical: LengthOrPercentage, + } + } + + #[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)] + 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 ToCss for computed_value::T { + 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::LengthOrPercentage(_) => + PositionCategory::LengthOrPercentage, + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> 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) + } +</%helpers:longhand> + +${helpers.single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")} + +${helpers.single_keyword("background-attachment", "scroll fixed")} + +${helpers.single_keyword("background-clip", "border-box padding-box content-box")} + +${helpers.single_keyword("background-origin", "padding-box border-box content-box")} + +<%helpers:longhand name="background-size"> + use cssparser::{ToCss, Token}; + use std::ascii::AsciiExt; + use std::fmt; + + pub mod computed_value { + use values::computed::LengthOrPercentageOrAuto; + + #[derive(PartialEq, Clone, Debug, HeapSizeOf)] + pub struct ExplicitSize { + pub width: LengthOrPercentageOrAuto, + pub height: LengthOrPercentageOrAuto, + } + + #[derive(PartialEq, Clone, Debug, HeapSizeOf)] + pub enum T { + Explicit(ExplicitSize), + Cover, + Contain, + } + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + computed_value::T::Explicit(ref size) => size.to_css(dest), + computed_value::T::Cover => dest.write_str("cover"), + computed_value::T::Contain => dest.write_str("contain"), + } + } + } + + #[derive(Clone, PartialEq, Debug, HeapSizeOf)] + 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) + } + } + + impl ToCss for computed_value::ExplicitSize { + 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, HeapSizeOf)] + 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<Cx: TContext>(&self, context: &Cx) -> 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, + })) + } +</%helpers:longhand> diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index 16e8bc7a6eb..e36266b2a3c 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> - <% from data import Method %> <% data.new_style_struct("Border", inherited=False, gecko_ffi_name="nsStyleBorder", @@ -62,3 +61,5 @@ "computed::BorderRadiusSize::zero()", "parse")} % endfor + +${helpers.single_keyword("box-decoration-break", "slice clone", products="gecko")} diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index 34d425ec16e..ec158c0d9fa 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> - <% from data import Method, to_rust_ident %> <% data.new_style_struct("Box", @@ -122,3 +121,712 @@ ${helpers.single_keyword("clear", "none left right both", gecko_ffi_name="mBreak } </%helpers:longhand> + +${helpers.predefined_type("width", + "LengthOrPercentageOrAuto", + "computed::LengthOrPercentageOrAuto::Auto", + "parse_non_negative")} + +${helpers.predefined_type("height", + "LengthOrPercentageOrAuto", + "computed::LengthOrPercentageOrAuto::Auto", + "parse_non_negative")} + +<%helpers:longhand name="vertical-align"> + 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(Debug, Clone, PartialEq, Copy, HeapSizeOf)] + 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: + "${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}), + % endfor + _ => Err(()) + } + }) + } + pub mod computed_value { + use app_units::Au; + use std::fmt; + use values::AuExtensionMethods; + use values::{CSSFloat, computed}; + #[allow(non_camel_case_types)] + #[derive(PartialEq, Copy, Clone, HeapSizeOf, Debug)] + pub enum T { + % for keyword in vertical_align_keywords: + ${to_rust_ident(keyword)}, + % endfor + LengthOrPercentage(computed::LengthOrPercentage), + } + impl ::cssparser::ToCss for T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + % for keyword in vertical_align_keywords: + T::${to_rust_ident(keyword)} => dest.write_str("${keyword}"), + % endfor + T::LengthOrPercentage(value) => value.to_css(dest), + } + } + } + } + #[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<Cx: TContext>(&self, context: &Cx) -> 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) => + computed_value::T::LengthOrPercentage(value.to_computed_value(context)), + } + } + } +</%helpers:longhand> + + +// CSS 2.1, Section 11 - Visual effects + +// Non-standard, see https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box#Specifications +${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box", products="servo", + internal=True)} + +${helpers.single_keyword("overflow-clip-box", "padding-box content-box", products="gecko", + internal=True)} + +// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. +${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=True, + gecko_constant_prefix="NS_STYLE_OVERFLOW")} + +// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. +<%helpers:longhand name="overflow-y" need_clone="True"> + use super::overflow_x; + + 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(Debug, Clone, Copy, PartialEq, HeapSizeOf)] + 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + computed_value::T(self.0.to_computed_value(context)) + } + } + + 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(SpecifiedValue) + } +</%helpers:longhand> + +// TODO(pcwalton): Multiple transitions. +<%helpers: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::{TContext, ToComputedValue}; + + pub use values::computed::Time as SingleComputedValue; + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Vec<SingleComputedValue>); + + impl ToComputedValue for T { + type ComputedValue = T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> 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)))) + } +</%helpers:longhand> + +// TODO(pcwalton): Lots more timing functions. +// TODO(pcwalton): Multiple transitions. +<%helpers:longhand name="transition-timing-function"> + use self::computed_value::{StartEnd, TransitionTimingFunction}; + + use euclid::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 euclid::point::Point2D; + use std::fmt; + + pub use self::TransitionTimingFunction as SingleComputedValue; + + #[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)] + 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, HeapSizeOf)] + 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, HeapSizeOf)] + 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<Cx: TContext>(&self, _: &Cx) -> 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!(specified::parse_number(input)); + try!(input.expect_comma()); + p1y = try!(specified::parse_number(input)); + try!(input.expect_comma()); + p2x = try!(specified::parse_number(input)); + try!(input.expect_comma()); + p2y = try!(specified::parse_number(input)); + Ok(()) + })); + let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(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!(specified::parse_integer(input)); + 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)))) + } +</%helpers:longhand> + +// TODO(pcwalton): Lots more properties. +<%helpers:longhand name="transition-property"> + use self::computed_value::TransitionProperty; + + 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, HeapSizeOf)] + 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, + Transform, + VerticalAlign, + Visibility, + Width, + WordSpacing, + ZIndex, + } + + pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 45] = [ + 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::Transform, + 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::Transform => dest.write_str("transform"), + 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, HeapSizeOf)] + 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), + "transform" => Ok(TransitionProperty::Transform), + "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<Cx: TContext>(&self, _: &Cx) -> computed_value::T { + (*self).clone() + } + } +</%helpers:longhand> + +<%helpers: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}; +</%helpers:longhand> + +// CSSOM View Module +// https://www.w3.org/TR/cssom-view-1/ +${helpers.single_keyword("scroll-behavior", + "auto smooth", + products="gecko")} + +// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x +${helpers.single_keyword("scroll-snap-type-x", + "none mandatory proximity", + products="gecko", + gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE")} + +// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-y +${helpers.single_keyword("scroll-snap-type-y", + "none mandatory proximity", + products="gecko", + gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE")} + +// Compositing and Blending Level 1 +// http://www.w3.org/TR/compositing-1/ +${helpers.single_keyword("isolation", + "auto isolate", + products="gecko")} + +${helpers.single_keyword("page-break-after", + "auto always avoid left right", + products="gecko")} +${helpers.single_keyword("page-break-before", + "auto always avoid left right", + products="gecko")} +${helpers.single_keyword("page-break-inside", + "auto avoid", + products="gecko", + gecko_ffi_name="mBreakInside", + gecko_constant_prefix="NS_STYLE_PAGE_BREAK")} + +// CSS Basic User Interface Module Level 3 +// http://dev.w3.org/csswg/css-ui/ +${helpers.single_keyword("resize", + "none both horizontal vertical", + products="gecko")} diff --git a/components/style/properties/longhand/color.mako.rs b/components/style/properties/longhand/color.mako.rs new file mode 100644 index 00000000000..dc9555e7999 --- /dev/null +++ b/components/style/properties/longhand/color.mako.rs @@ -0,0 +1,43 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("Color", inherited=True, gecko_ffi_name="nsStyleColor") %> + +<%helpers:raw_longhand name="color" need_clone="True"> + use cssparser::Color as CSSParserColor; + use cssparser::RGBA; + use values::specified::{CSSColor, CSSRGBA}; + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> 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 { + CSSParserColor::RGBA(rgba) => rgba, + CSSParserColor::CurrentColor => return Ok(DeclaredValue::Inherit) + }; + Ok(DeclaredValue::Value(CSSRGBA { + parsed: rgba, + authored: value.authored, + })) + } +</%helpers:raw_longhand> diff --git a/components/style/properties/longhand/column.mako.rs b/components/style/properties/longhand/column.mako.rs new file mode 100644 index 00000000000..2e6618e3745 --- /dev/null +++ b/components/style/properties/longhand/column.mako.rs @@ -0,0 +1,196 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("Column", inherited=False, gecko_ffi_name="nsStyleColumn") %> + +<%helpers:longhand name="column-width" experimental="True"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + + #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] + 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 app_units::Au; + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<Au>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("auto"), + Some(l) => l.to_css(dest), + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(None) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::Auto => computed_value::T(None), + SpecifiedValue::Specified(l) => + computed_value::T(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) + } + } +</%helpers:longhand> + +<%helpers:longhand name="column-count" experimental="True"> + use cssparser::ToCss; + use std::fmt; + + #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] + 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 { + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<u32>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("auto"), + Some(count) => write!(dest, "{}", count), + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(None) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::Auto => computed_value::T(None), + SpecifiedValue::Specified(count) => + computed_value::T(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!(specified::parse_integer(input)); + // Zero is invalid + if count <= 0 { + return Err(()) + } + Ok(SpecifiedValue::Specified(count as u32)) + } + } +</%helpers:longhand> + +<%helpers:longhand name="column-gap" experimental="True"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + + #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] + 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 app_units::Au; + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<Au>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("normal"), + Some(l) => l.to_css(dest), + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(None) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::Normal => computed_value::T(None), + SpecifiedValue::Specified(l) => + computed_value::T(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) + } + } +</%helpers:longhand> diff --git a/components/style/properties/longhand/counters.mako.rs b/components/style/properties/longhand/counters.mako.rs new file mode 100644 index 00000000000..9af2ca8aa9c --- /dev/null +++ b/components/style/properties/longhand/counters.mako.rs @@ -0,0 +1,248 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("Counters", inherited=False, gecko_ffi_name="nsStyleContent") %> + +<%helpers: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(Debug, PartialEq, Eq, Clone, HeapSizeOf)] + 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(Debug, PartialEq, Eq, Clone, HeapSizeOf)] + 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(()) + } + } +</%helpers:longhand> + +<%helpers:longhand name="counter-increment"> + use std::fmt; + use super::content; + use values::computed::ComputedValueAsSpecified; + + use cssparser::{ToCss, Token, serialize_identifier}; + use std::borrow::{Cow, ToOwned}; + + pub use self::computed_value::T as SpecifiedValue; + + pub mod computed_value { + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + 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 { + if !first { + try!(dest.write_str(" ")); + } + first = false; + try!(serialize_identifier(&pair.0, dest)); + try!(write!(dest, " {}", pair.1)); + } + Ok(()) + } + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + parse_common(1, input) + } + + pub fn parse_common(default_value: i32, 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| specified::parse_integer(input)).unwrap_or(default_value); + counters.push((counter_name, counter_delta)) + } + + if !counters.is_empty() { + Ok(SpecifiedValue(counters)) + } else { + Err(()) + } + } +</%helpers:longhand> + +<%helpers:longhand name="counter-reset"> + pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value}; + use super::counter_increment::{parse_common}; + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + parse_common(0, input) + } +</%helpers:longhand> diff --git a/components/style/properties/longhand/effects.mako.rs b/components/style/properties/longhand/effects.mako.rs new file mode 100644 index 00000000000..3424debe2b8 --- /dev/null +++ b/components/style/properties/longhand/effects.mako.rs @@ -0,0 +1,1343 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +// Box-shadow, etc. +<% data.new_style_struct("Effects", inherited=False, gecko_ffi_name="nsStyleEffects") %> + +<%helpers:longhand name="opacity"> + use cssparser::ToCss; + use std::fmt; + use values::CSSFloat; + + impl ToCss for SpecifiedValue { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.0.to_css(dest) + } + } + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + 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<Cx: TContext>(&self, _context: &Cx) -> 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, ()> { + specified::parse_number(input).map(SpecifiedValue) + } +</%helpers:longhand> + +<%helpers:longhand name="box-shadow"> + use cssparser::{self, ToCss}; + use std::fmt; + use values::AuExtensionMethods; + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct SpecifiedValue(Vec<SpecifiedBoxShadow>); + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + 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 app_units::Au; + use std::fmt; + use values::computed; + + #[derive(Clone, PartialEq, HeapSizeOf, Debug)] + pub struct T(pub Vec<BoxShadow>); + + #[derive(Clone, PartialEq, Copy, HeapSizeOf, Debug)] + 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 ToCss for computed_value::T { + 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 computed_value::BoxShadow { + 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)); + try!(dest.write_str(" ")); + try!(self.color.to_css(dest)); + Ok(()) + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + computed_value::T(self.0.iter().map(|value| compute_one_box_shadow(value, context)).collect()) + } + } + + pub fn compute_one_box_shadow<Cx: TContext>(value: &SpecifiedBoxShadow, context: &Cx) + -> 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 app_units::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, + }) + } +</%helpers:longhand> + +<%helpers:longhand name="clip"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + + // NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2. + + pub mod computed_value { + use app_units::Au; + + #[derive(Clone, PartialEq, Eq, Copy, Debug, HeapSizeOf)] + pub struct ClipRect { + pub top: Au, + pub right: Option<Au>, + pub bottom: Option<Au>, + pub left: Au, + } + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<ClipRect>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("auto"), + Some(rect) => { + try!(dest.write_str("rect(")); + try!(rect.top.to_css(dest)); + try!(dest.write_str(", ")); + if let Some(right) = rect.right { + try!(right.to_css(dest)); + try!(dest.write_str(", ")); + } else { + try!(dest.write_str("auto, ")); + } + + if let Some(bottom) = rect.bottom { + try!(bottom.to_css(dest)); + try!(dest.write_str(", ")); + } else { + try!(dest.write_str("auto, ")); + } + + try!(rect.left.to_css(dest)); + try!(dest.write_str(")")); + Ok(()) + } + } + } + } + + #[derive(Clone, Debug, PartialEq, Copy, HeapSizeOf)] + 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, HeapSizeOf)] + 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.bottom { + 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 { + computed_value::T(None) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + 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 app_units::Au; + use std::ascii::AsciiExt; + 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(()) + } + } +</%helpers:longhand> + +<%helpers:longhand name="filter"> + //pub use self::computed_value::T as SpecifiedValue; + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + use values::CSSFloat; + use values::specified::{Angle, Length}; + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct SpecifiedValue(Vec<SpecifiedFilter>); + + // TODO(pcwalton): `drop-shadow` + #[derive(Clone, PartialEq, Debug, HeapSizeOf)] + 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 app_units::Au; + use values::CSSFloat; + use values::specified::{Angle}; + + #[derive(Clone, PartialEq, Debug, HeapSizeOf, Deserialize, Serialize)] + 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, HeapSizeOf, Deserialize, Serialize)] + 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 { + if let Filter::Opacity(ref opacity_value) = *filter { + opacity *= *opacity_value + } + } + opacity + } + } + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.filters.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 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 computed_value::Filter { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + computed_value::Filter::Blur(value) => { + try!(dest.write_str("blur(")); + try!(value.to_css(dest)); + try!(dest.write_str(")")); + } + computed_value::Filter::Brightness(value) => try!(write!(dest, "brightness({})", value)), + computed_value::Filter::Contrast(value) => try!(write!(dest, "contrast({})", value)), + computed_value::Filter::Grayscale(value) => try!(write!(dest, "grayscale({})", value)), + computed_value::Filter::HueRotate(value) => { + try!(dest.write_str("hue-rotate(")); + try!(value.to_css(dest)); + try!(dest.write_str(")")); + } + computed_value::Filter::Invert(value) => try!(write!(dest, "invert({})", value)), + computed_value::Filter::Opacity(value) => try!(write!(dest, "opacity({})", value)), + computed_value::Filter::Saturate(value) => try!(write!(dest, "saturate({})", value)), + computed_value::Filter::Sepia(value) => try!(write!(dest, "sepia({})", value)), + } + 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<Cx: TContext>(&self, context: &Cx) -> 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() } + } + } +</%helpers:longhand> + +<%helpers:longhand name="transform"> + use app_units::Au; + use values::CSSFloat; + + use cssparser::ToCss; + use std::fmt; + + pub mod computed_value { + use values::CSSFloat; + use values::computed; + + #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] + pub struct ComputedMatrix { + pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat, + pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat, + pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat, + pub m41: CSSFloat, pub m42: CSSFloat, pub m43: CSSFloat, pub m44: CSSFloat, + } + + impl ComputedMatrix { + pub fn identity() -> ComputedMatrix { + ComputedMatrix { + m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0, + m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0, + m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0, + m41: 0.0, m42: 0.0, m43: 0.0, m44: 1.0 + } + } + } + + #[derive(Clone, Debug, PartialEq, HeapSizeOf)] + pub enum ComputedOperation { + Matrix(ComputedMatrix), + Skew(computed::Angle, computed::Angle), + Translate(computed::LengthOrPercentage, + computed::LengthOrPercentage, + computed::Length), + Scale(CSSFloat, CSSFloat, CSSFloat), + Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle), + Perspective(computed::Length), + } + + #[derive(Clone, Debug, PartialEq, HeapSizeOf)] + pub struct T(pub Option<Vec<ComputedOperation>>); + } + + pub use self::computed_value::ComputedMatrix as SpecifiedMatrix; + + fn parse_two_lengths_or_percentages(input: &mut Parser) + -> Result<(specified::LengthOrPercentage, + specified::LengthOrPercentage),()> { + let first = try!(specified::LengthOrPercentage::parse(input)); + let second = input.try(|input| { + try!(input.expect_comma()); + specified::LengthOrPercentage::parse(input) + }).unwrap_or(specified::LengthOrPercentage::zero()); + Ok((first, second)) + } + + fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> { + let first = try!(specified::parse_number(input)); + let second = input.try(|input| { + try!(input.expect_comma()); + specified::parse_number(input) + }).unwrap_or(first); + Ok((first, second)) + } + + fn parse_two_angles(input: &mut Parser) -> Result<(specified::Angle, specified::Angle),()> { + let first = try!(specified::Angle::parse(input)); + let second = input.try(|input| { + try!(input.expect_comma()); + specified::Angle::parse(input) + }).unwrap_or(specified::Angle(0.0)); + Ok((first, second)) + } + + #[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)] + enum TranslateKind { + Translate, + TranslateX, + TranslateY, + TranslateZ, + Translate3D, + } + + #[derive(Clone, Debug, PartialEq, HeapSizeOf)] + enum SpecifiedOperation { + Matrix(SpecifiedMatrix), + Skew(specified::Angle, specified::Angle), + Translate(TranslateKind, + specified::LengthOrPercentage, + specified::LengthOrPercentage, + specified::Length), + Scale(CSSFloat, CSSFloat, CSSFloat), + Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle), + Perspective(specified::Length), + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { + // TODO(pcwalton) + Ok(()) + } + } + + impl ToCss for SpecifiedOperation { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + // todo(gw): implement serialization for transform + // types other than translate. + SpecifiedOperation::Matrix(_m) => { + Ok(()) + } + SpecifiedOperation::Skew(_sx, _sy) => { + Ok(()) + } + SpecifiedOperation::Translate(kind, tx, ty, tz) => { + match kind { + TranslateKind::Translate => { + try!(dest.write_str("translate(")); + try!(tx.to_css(dest)); + try!(dest.write_str(", ")); + try!(ty.to_css(dest)); + dest.write_str(")") + } + TranslateKind::TranslateX => { + try!(dest.write_str("translateX(")); + try!(tx.to_css(dest)); + dest.write_str(")") + } + TranslateKind::TranslateY => { + try!(dest.write_str("translateY(")); + try!(ty.to_css(dest)); + dest.write_str(")") + } + TranslateKind::TranslateZ => { + try!(dest.write_str("translateZ(")); + try!(tz.to_css(dest)); + dest.write_str(")") + } + TranslateKind::Translate3D => { + try!(dest.write_str("translate3d(")); + try!(tx.to_css(dest)); + try!(dest.write_str(", ")); + try!(ty.to_css(dest)); + try!(dest.write_str(", ")); + try!(tz.to_css(dest)); + dest.write_str(")") + } + } + } + SpecifiedOperation::Scale(_sx, _sy, _sz) => { + Ok(()) + } + SpecifiedOperation::Rotate(_ax, _ay, _az, _angle) => { + Ok(()) + } + SpecifiedOperation::Perspective(_p) => { + Ok(()) + } + } + } + } + + #[derive(Clone, Debug, PartialEq, HeapSizeOf)] + 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 { + if !first { + try!(dest.write_str(" ")); + } + first = false; + try!(operation.to_css(dest)) + } + Ok(()) + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + 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| { + specified::parse_number(input) + })); + if values.len() != 6 { + return Err(()) + } + result.push(SpecifiedOperation::Matrix( + SpecifiedMatrix { + m11: values[0], m12: values[1], m13: 0.0, m14: 0.0, + m21: values[2], m22: values[3], m23: 0.0, m24: 0.0, + m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0, + m41: values[4], m42: values[5], m43: 0.0, m44: 1.0 + })); + Ok(()) + })) + }, + "matrix3d" => { + try!(input.parse_nested_block(|input| { + let values = try!(input.parse_comma_separated(|input| { + specified::parse_number(input) + })); + if values.len() != 16 { + return Err(()) + } + result.push(SpecifiedOperation::Matrix( + SpecifiedMatrix { + m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3], + m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7], + m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11], + m41: values[12], m42: values[13], m43: values[14], m44: values[15] + })); + Ok(()) + })) + }, + "translate" => { + try!(input.parse_nested_block(|input| { + let (tx, ty) = try!(parse_two_lengths_or_percentages(input)); + result.push(SpecifiedOperation::Translate(TranslateKind::Translate, + tx, + ty, + specified::Length::Absolute(Au(0)))); + Ok(()) + })) + }, + "translatex" => { + try!(input.parse_nested_block(|input| { + let tx = try!(specified::LengthOrPercentage::parse(input)); + result.push(SpecifiedOperation::Translate( + TranslateKind::TranslateX, + tx, + specified::LengthOrPercentage::zero(), + specified::Length::Absolute(Au(0)))); + Ok(()) + })) + }, + "translatey" => { + try!(input.parse_nested_block(|input| { + let ty = try!(specified::LengthOrPercentage::parse(input)); + result.push(SpecifiedOperation::Translate( + TranslateKind::TranslateY, + specified::LengthOrPercentage::zero(), + ty, + specified::Length::Absolute(Au(0)))); + Ok(()) + })) + }, + "translatez" => { + try!(input.parse_nested_block(|input| { + let tz = try!(specified::Length::parse(input)); + result.push(SpecifiedOperation::Translate( + TranslateKind::TranslateZ, + specified::LengthOrPercentage::zero(), + specified::LengthOrPercentage::zero(), + tz)); + Ok(()) + })) + }, + "translate3d" => { + try!(input.parse_nested_block(|input| { + let tx = try!(specified::LengthOrPercentage::parse(input)); + try!(input.expect_comma()); + let ty = try!(specified::LengthOrPercentage::parse(input)); + try!(input.expect_comma()); + let tz = try!(specified::Length::parse(input)); + result.push(SpecifiedOperation::Translate( + TranslateKind::Translate3D, + tx, + ty, + tz)); + Ok(()) + })) + + }, + "scale" => { + try!(input.parse_nested_block(|input| { + let (sx, sy) = try!(parse_two_floats(input)); + result.push(SpecifiedOperation::Scale(sx, sy, 1.0)); + Ok(()) + })) + }, + "scalex" => { + try!(input.parse_nested_block(|input| { + let sx = try!(specified::parse_number(input)); + result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0)); + Ok(()) + })) + }, + "scaley" => { + try!(input.parse_nested_block(|input| { + let sy = try!(specified::parse_number(input)); + result.push(SpecifiedOperation::Scale(1.0, sy, 1.0)); + Ok(()) + })) + }, + "scalez" => { + try!(input.parse_nested_block(|input| { + let sz = try!(specified::parse_number(input)); + result.push(SpecifiedOperation::Scale(1.0, 1.0, sz)); + Ok(()) + })) + }, + "scale3d" => { + try!(input.parse_nested_block(|input| { + let sx = try!(specified::parse_number(input)); + try!(input.expect_comma()); + let sy = try!(specified::parse_number(input)); + try!(input.expect_comma()); + let sz = try!(specified::parse_number(input)); + result.push(SpecifiedOperation::Scale(sx, sy, sz)); + Ok(()) + })) + }, + "rotate" => { + try!(input.parse_nested_block(|input| { + let theta = try!(specified::Angle::parse(input)); + result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); + Ok(()) + })) + }, + "rotatex" => { + try!(input.parse_nested_block(|input| { + let theta = try!(specified::Angle::parse(input)); + result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta)); + Ok(()) + })) + }, + "rotatey" => { + try!(input.parse_nested_block(|input| { + let theta = try!(specified::Angle::parse(input)); + result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta)); + Ok(()) + })) + }, + "rotatez" => { + try!(input.parse_nested_block(|input| { + let theta = try!(specified::Angle::parse(input)); + result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); + Ok(()) + })) + }, + "rotate3d" => { + try!(input.parse_nested_block(|input| { + let ax = try!(specified::parse_number(input)); + try!(input.expect_comma()); + let ay = try!(specified::parse_number(input)); + try!(input.expect_comma()); + let az = try!(specified::parse_number(input)); + try!(input.expect_comma()); + let theta = try!(specified::Angle::parse(input)); + // TODO(gw): Check the axis can be normalized!! + result.push(SpecifiedOperation::Rotate(ax, ay, az, theta)); + Ok(()) + })) + }, + "skew" => { + try!(input.parse_nested_block(|input| { + let (theta_x, theta_y) = try!(parse_two_angles(input)); + result.push(SpecifiedOperation::Skew(theta_x, theta_y)); + Ok(()) + })) + }, + "skewx" => { + try!(input.parse_nested_block(|input| { + let theta_x = try!(specified::Angle::parse(input)); + result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0))); + Ok(()) + })) + }, + "skewy" => { + try!(input.parse_nested_block(|input| { + let theta_y = try!(specified::Angle::parse(input)); + result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y)); + Ok(()) + })) + }, + "perspective" => { + try!(input.parse_nested_block(|input| { + let d = try!(specified::Length::parse(input)); + result.push(SpecifiedOperation::Perspective(d)); + 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + if self.0.is_empty() { + return computed_value::T(None) + } + + let mut result = vec!(); + for operation in &self.0 { + match *operation { + SpecifiedOperation::Matrix(ref matrix) => { + result.push(computed_value::ComputedOperation::Matrix(*matrix)); + } + SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => { + result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context), + ty.to_computed_value(context), + tz.to_computed_value(context))); + } + SpecifiedOperation::Scale(sx, sy, sz) => { + result.push(computed_value::ComputedOperation::Scale(sx, sy, sz)); + } + SpecifiedOperation::Rotate(ax, ay, az, theta) => { + result.push(computed_value::ComputedOperation::Rotate(ax, ay, az, theta)); + } + SpecifiedOperation::Skew(theta_x, theta_y) => { + result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y)); + } + SpecifiedOperation::Perspective(d) => { + result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context))); + } + }; + } + + computed_value::T(Some(result)) + } + } +</%helpers:longhand> + +pub struct OriginParseResult { + horizontal: Option<specified::LengthOrPercentage>, + vertical: Option<specified::LengthOrPercentage>, + depth: Option<specified::Length> +} + +pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> { + use values::specified::{LengthOrPercentage, Percentage}; + let (mut horizontal, mut vertical, mut depth) = (None, None, None); + loop { + if let Err(_) = input.try(|input| { + let token = try!(input.expect_ident()); + match_ignore_ascii_case! { + token, + "left" => { + if horizontal.is_none() { + horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.0))) + } else { + return Err(()) + } + }, + "center" => { + if horizontal.is_none() { + horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.5))) + } else if vertical.is_none() { + vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5))) + } else { + return Err(()) + } + }, + "right" => { + if horizontal.is_none() { + horizontal = Some(LengthOrPercentage::Percentage(Percentage(1.0))) + } else { + return Err(()) + } + }, + "top" => { + if vertical.is_none() { + vertical = Some(LengthOrPercentage::Percentage(Percentage(0.0))) + } else { + return Err(()) + } + }, + "bottom" => { + if vertical.is_none() { + vertical = Some(LengthOrPercentage::Percentage(Percentage(1.0))) + } else { + return Err(()) + } + }, + _ => return Err(()) + } + Ok(()) + }) { + match LengthOrPercentage::parse(input) { + Ok(value) => { + if horizontal.is_none() { + horizontal = Some(value); + } else if vertical.is_none() { + vertical = Some(value); + } else if let LengthOrPercentage::Length(length) = value { + depth = Some(length); + } else { + break; + } + } + _ => break, + } + } + } + + if horizontal.is_some() || vertical.is_some() { + Ok(OriginParseResult { + horizontal: horizontal, + vertical: vertical, + depth: depth, + }) + } else { + Err(()) + } +} + +${helpers.single_keyword("backface-visibility", "visible hidden")} + +${helpers.single_keyword("transform-box", "border-box fill-box view-box", products="gecko")} + +${helpers.single_keyword("transform-style", "auto flat preserve-3d")} + +<%helpers:longhand name="transform-origin"> + use app_units::Au; + use values::AuExtensionMethods; + use values::specified::{Length, LengthOrPercentage, Percentage}; + + use cssparser::ToCss; + use std::fmt; + + pub mod computed_value { + use values::computed::{Length, LengthOrPercentage}; + + #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] + pub struct T { + pub horizontal: LengthOrPercentage, + pub vertical: LengthOrPercentage, + pub depth: Length, + } + } + + #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] + pub struct SpecifiedValue { + horizontal: LengthOrPercentage, + vertical: LengthOrPercentage, + depth: Length, + } + + impl ToCss for computed_value::T { + 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)); + try!(dest.write_str(" ")); + self.depth.to_css(dest) + } + } + + 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)); + try!(dest.write_str(" ")); + self.depth.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), + depth: Au(0), + } + } + + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + let result = try!(super::parse_origin(context, input)); + Ok(SpecifiedValue { + horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), + vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), + depth: result.depth.unwrap_or(Length::Absolute(Au(0))), + }) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + computed_value::T { + horizontal: self.horizontal.to_computed_value(context), + vertical: self.vertical.to_computed_value(context), + depth: self.depth.to_computed_value(context), + } + } + } +</%helpers:longhand> + +${helpers.predefined_type("perspective", + "LengthOrNone", + "computed::LengthOrNone::None")} + +<%helpers:longhand name="perspective-origin"> + use values::specified::{LengthOrPercentage, Percentage}; + + use cssparser::ToCss; + use std::fmt; + + pub mod computed_value { + use values::computed::LengthOrPercentage; + + #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] + pub struct T { + pub horizontal: LengthOrPercentage, + pub vertical: LengthOrPercentage, + } + } + + impl ToCss for computed_value::T { + 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) + } + } + + #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] + 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(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + let result = try!(super::parse_origin(context, input)); + match result.depth { + Some(_) => Err(()), + None => Ok(SpecifiedValue { + horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), + vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), + }) + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + computed_value::T { + horizontal: self.horizontal.to_computed_value(context), + vertical: self.vertical.to_computed_value(context), + } + } + } +</%helpers:longhand> + +${helpers.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""", gecko_constant_prefix="NS_STYLE_BLEND")} diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs new file mode 100644 index 00000000000..b12acc6ccc7 --- /dev/null +++ b/components/style/properties/longhand/font.mako.rs @@ -0,0 +1,312 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> +<% from data import Method %> + +<% data.new_style_struct("Font", + inherited=True, + gecko_ffi_name="nsStyleFont", + additional_methods=[Method("compute_font_hash", is_mut=True)]) %> +<%helpers:longhand name="font-family"> + use self::computed_value::FontFamily; + use values::computed::ComputedValueAsSpecified; + pub use self::computed_value::T as SpecifiedValue; + + const SERIF: &'static str = "serif"; + const SANS_SERIF: &'static str = "sans-serif"; + const CURSIVE: &'static str = "cursive"; + const FANTASY: &'static str = "fantasy"; + const MONOSPACE: &'static str = "monospace"; + + impl ComputedValueAsSpecified for SpecifiedValue {} + pub mod computed_value { + use cssparser::ToCss; + use std::fmt; + use string_cache::Atom; + + #[derive(Debug, PartialEq, Eq, Clone, Hash, HeapSizeOf, Deserialize, Serialize)] + 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, + FontFamily::Serif => super::SERIF, + FontFamily::SansSerif => super::SANS_SERIF, + FontFamily::Cursive => super::CURSIVE, + FontFamily::Fantasy => super::FANTASY, + FontFamily::Monospace => super::MONOSPACE + } + } + + pub fn from_atom(input: Atom) -> FontFamily { + let option = match_ignore_ascii_case! { &input, + super::SERIF => Some(FontFamily::Serif), + super::SANS_SERIF => Some(FontFamily::SansSerif), + super::CURSIVE => Some(FontFamily::Cursive), + super::FANTASY => Some(FontFamily::Fantasy), + super::MONOSPACE => Some(FontFamily::Monospace), + _ => None + }; + + match option { + Some(family) => family, + None => FontFamily::FamilyName(input) + } + } + } + impl ToCss for FontFamily { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(self.name()) + } + } + 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(Debug, Clone, PartialEq, Eq, Hash, HeapSizeOf)] + pub struct T(pub Vec<FontFamily>); + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(vec![FontFamily::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(&*value))) + } + let first_ident = try!(input.expect_ident()); + + match_ignore_ascii_case! { first_ident, + SERIF => return Ok(FontFamily::Serif), + SANS_SERIF => return Ok(FontFamily::SansSerif), + CURSIVE => return Ok(FontFamily::Cursive), + FANTASY => return Ok(FontFamily::Fantasy), + MONOSPACE => return Ok(FontFamily::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(value))) + } +</%helpers:longhand> + + +${helpers.single_keyword("font-style", "normal italic oblique")} +${helpers.single_keyword("font-variant", "normal small-caps")} + +<%helpers:longhand name="font-weight" need_clone="True"> + use cssparser::ToCss; + use std::fmt; + + #[derive(Debug, Clone, PartialEq, Eq, Copy, HeapSizeOf)] + 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, Deserialize, Serialize, HeapSizeOf, Debug)] + pub enum T { + % for weight in range(100, 901, 100): + Weight${weight} = ${weight}, + % endfor + } + impl T { + #[inline] + pub fn is_bold(self) -> bool { + match self { + T::Weight900 | T::Weight800 | + T::Weight700 | T::Weight600 => true, + _ => false + } + } + } + } + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + % for weight in range(100, 901, 100): + computed_value::T::Weight${weight} => dest.write_str("${weight}"), + % endfor + } + } + } + #[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<Cx: TContext>(&self, context: &Cx) -> 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_style().get_font().clone_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_style().get_font().clone_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, + }, + } + } + } +</%helpers:longhand> + +<%helpers:longhand name="font-size" need_clone="True"> + use app_units::Au; + use cssparser::ToCss; + use std::fmt; + use values::FONT_MEDIUM_PX; + use values::specified::{LengthOrPercentage, Length, Percentage}; + + impl ToCss for SpecifiedValue { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.0.to_css(dest) + } + } + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct SpecifiedValue(pub specified::LengthOrPercentage); + pub mod computed_value { + use app_units::Au; + pub type T = Au; + } + #[inline] pub fn get_initial_value() -> computed_value::T { + Au::from_px(FONT_MEDIUM_PX) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match self.0 { + LengthOrPercentage::Length(Length::FontRelative(value)) => { + value.to_computed_value(context.inherited_style().get_font().clone_font_size(), + context.style().root_font_size()) + } + LengthOrPercentage::Length(Length::ServoCharacterWidth(value)) => { + value.to_computed_value(context.inherited_style().get_font().clone_font_size()) + } + LengthOrPercentage::Length(l) => { + l.to_computed_value(context) + } + LengthOrPercentage::Percentage(Percentage(value)) => { + context.inherited_style().get_font().clone_font_size().scale_by(value) + } + LengthOrPercentage::Calc(calc) => { + let calc = calc.to_computed_value(context); + calc.length() + context.inherited_style().get_font().clone_font_size() + .scale_by(calc.percentage()) + } + } + } + } + /// <length> | <percentage> | <absolute-size> | <relative-size> + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use values::specified::{Length, LengthOrPercentage}; + + input.try(specified::LengthOrPercentage::parse_non_negative) + .or_else(|()| { + let ident = try!(input.expect_ident()); + specified::Length::from_str(&ident as &str) + .ok_or(()) + .map(specified::LengthOrPercentage::Length) + }) + .map(SpecifiedValue) + } +</%helpers:longhand> + +${helpers.single_keyword("font-stretch", + "normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded \ + expanded extra-expanded ultra-expanded")} + +${helpers.single_keyword("font-kerning", "auto none normal", products="gecko")} diff --git a/components/style/properties/longhand/inherited_box.mako.rs b/components/style/properties/longhand/inherited_box.mako.rs new file mode 100644 index 00000000000..4c8dd202596 --- /dev/null +++ b/components/style/properties/longhand/inherited_box.mako.rs @@ -0,0 +1,90 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("InheritedBox", inherited=True, gecko_ffi_name="nsStyleVisibility") %> + +${helpers.single_keyword("direction", "ltr rtl", need_clone=True)} + +// TODO: collapse. Well, do tables first. +${helpers.single_keyword("visibility", + "visible hidden", + extra_gecko_values="collapse", + gecko_ffi_name="mVisible")} + +// CSS Writing Modes Level 3 +// http://dev.w3.org/csswg/css-writing-modes/ +${helpers.single_keyword("writing-mode", + "horizontal-tb vertical-rl vertical-lr", + experimental=True, + need_clone=True)} + +// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support) +// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented +// FIXME(bholley): sideways-right is needed as an alias to sideways in gecko. +${helpers.single_keyword("text-orientation", + "sideways", + experimental=True, + need_clone=True, + extra_gecko_values="mixed upright", + extra_servo_values="sideways-right sideways-left")} + +// CSS Color Module Level 4 +// https://drafts.csswg.org/css-color/ +${helpers.single_keyword("color-adjust", "economy exact", products="gecko")} + +<%helpers:longhand name="image-rendering"> + pub mod computed_value { + use cssparser::ToCss; + use std::fmt; + + #[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)] + 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<Cx: TContext>(&self, _: &Cx) -> computed_value::T { + *self + } + } +</%helpers:longhand> diff --git a/components/style/properties/longhand/inherited_table.mako.rs b/components/style/properties/longhand/inherited_table.mako.rs new file mode 100644 index 00000000000..5ec6a2e1982 --- /dev/null +++ b/components/style/properties/longhand/inherited_table.mako.rs @@ -0,0 +1,100 @@ +/* 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/. */ + + <%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("InheritedTable", inherited=True, gecko_ffi_name="nsStyleTableBorder") %> + +${helpers.single_keyword("border-collapse", "separate collapse", gecko_constant_prefix="NS_STYLE_BORDER")} +${helpers.single_keyword("empty-cells", "show hide", gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS")} +${helpers.single_keyword("caption-side", "top bottom", extra_gecko_values="right left top-outside bottom-outside")} + +<%helpers:longhand name="border-spacing"> + use app_units::Au; + use values::AuExtensionMethods; + + use cssparser::ToCss; + use std::fmt; + + pub mod computed_value { + use app_units::Au; + + #[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, HeapSizeOf)] + pub struct T { + pub horizontal: Au, + pub vertical: Au, + } + } + + #[derive(Clone, Debug, PartialEq, HeapSizeOf)] + 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 ToCss for computed_value::T { + 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<Cx: TContext>(&self, context: &Cx) -> 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"), + } + } +</%helpers:longhand> diff --git a/components/style/properties/longhand/inherited_text.mako.rs b/components/style/properties/longhand/inherited_text.mako.rs new file mode 100644 index 00000000000..32fef25c68a --- /dev/null +++ b/components/style/properties/longhand/inherited_text.mako.rs @@ -0,0 +1,613 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("InheritedText", inherited=True, gecko_ffi_name="nsStyleText") %> + +<%helpers:longhand name="line-height"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + use values::CSSFloat; + + #[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)] + pub enum SpecifiedValue { + Normal, + Number(CSSFloat), + LengthOrPercentage(specified::LengthOrPercentage), + } + + 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::LengthOrPercentage(value) => value.to_css(dest), + SpecifiedValue::Number(number) => write!(dest, "{}", number), + } + } + } + /// normal | <number> | <length> | <percentage> + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use cssparser::Token; + use std::ascii::AsciiExt; + input.try(specified::LengthOrPercentage::parse_non_negative) + .map(SpecifiedValue::LengthOrPercentage) + .or_else(|()| { + match try!(input.next()) { + Token::Number(ref value) if value.value >= 0. => { + Ok(SpecifiedValue::Number(value.value)) + } + Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => { + Ok(SpecifiedValue::Normal) + } + _ => Err(()), + } + }) + } + pub mod computed_value { + use app_units::Au; + use std::fmt; + use values::CSSFloat; + #[derive(PartialEq, Copy, Clone, HeapSizeOf, Debug)] + pub enum T { + Normal, + Length(Au), + Number(CSSFloat), + } + } + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + computed_value::T::Normal => dest.write_str("normal"), + computed_value::T::Length(length) => length.to_css(dest), + computed_value::T::Number(number) => write!(dest, "{}", 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::Normal => computed_value::T::Normal, + SpecifiedValue::Number(value) => computed_value::T::Number(value), + SpecifiedValue::LengthOrPercentage(value) => { + match value { + specified::LengthOrPercentage::Length(value) => + computed_value::T::Length(value.to_computed_value(context)), + specified::LengthOrPercentage::Percentage(specified::Percentage(value)) => { + let fr = specified::Length::FontRelative(specified::FontRelativeLength::Em(value)); + computed_value::T::Length(fr.to_computed_value(context)) + }, + specified::LengthOrPercentage::Calc(calc) => { + let calc = calc.to_computed_value(context); + let fr = specified::FontRelativeLength::Em(calc.percentage()); + let fr = specified::Length::FontRelative(fr); + computed_value::T::Length(calc.length() + fr.to_computed_value(context)) + } + } + } + } + } + } +</%helpers:longhand> + +<%helpers: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, + servo_left("-servo-left") => 7, + servo_right("-servo-right") => 8, + } + } + #[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) + } +</%helpers:longhand> + +<%helpers:longhand name="letter-spacing"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + + #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] + 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 app_units::Au; + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<Au>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("normal"), + Some(l) => l.to_css(dest), + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(None) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::Normal => computed_value::T(None), + SpecifiedValue::Specified(l) => + computed_value::T(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) + } + } +</%helpers:longhand> + +<%helpers:longhand name="word-spacing"> + use cssparser::ToCss; + use std::fmt; + use values::AuExtensionMethods; + + #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] + 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 app_units::Au; + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<Au>); + } + + impl ToCss for computed_value::T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("normal"), + Some(l) => l.to_css(dest), + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(None) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::Normal => computed_value::T(None), + SpecifiedValue::Specified(l) => + computed_value::T(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) + } + } +</%helpers:longhand> + +${helpers.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. +${helpers.single_keyword("overflow-wrap", + "normal break-word", + gecko_ffi_name="mWordWrap", + gecko_constant_prefix="NS_STYLE_WORDWRAP")} + +// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support. +${helpers.single_keyword("word-break", + "normal break-all", + extra_gecko_values="keep-all", + gecko_constant_prefix="NS_STYLE_WORDBREAK")} + +// TODO(pcwalton): Support `text-justify: distribute`. +${helpers.single_keyword("text-justify", + "auto none inter-word", + products="servo")} + +<%helpers:longhand name="-servo-text-decorations-in-effect" + derived_from="display text-decoration" need_clone="True" products="servo"> + use cssparser::{RGBA, ToCss}; + use std::fmt; + + use values::computed::ComputedValueAsSpecified; + use properties::style_struct_traits::{Box, Color, Text}; + + impl ComputedValueAsSpecified for SpecifiedValue {} + + #[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)] + 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; + } + + impl ToCss for SpecifiedValue { + fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { + // Web compat doesn't matter here. + Ok(()) + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + SpecifiedValue { + underline: None, + overline: None, + line_through: None, + } + } + + fn maybe<Cx: TContext>(flag: bool, context: &Cx) -> Option<RGBA> { + if flag { + Some(context.style().get_color().clone_color()) + } else { + None + } + } + + fn derive<Cx: TContext>(context: &Cx) -> computed_value::T { + // Start with no declarations if this is an atomic inline-level box; otherwise, start with the + // declarations in effect and add in the text decorations that this block specifies. + let mut result = match context.style().get_box().clone_display() { + super::display::computed_value::T::inline_block | + super::display::computed_value::T::inline_table => SpecifiedValue { + underline: None, + overline: None, + line_through: None, + }, + _ => context.inherited_style().get_inheritedtext().clone__servo_text_decorations_in_effect() + }; + + result.underline = maybe(context.style().get_text().has_underline() + || result.underline.is_some(), context); + result.overline = maybe(context.style().get_text().has_overline() + || result.overline.is_some(), context); + result.line_through = maybe(context.style().get_text().has_line_through() + || result.line_through.is_some(), context); + + result + } + + #[inline] + pub fn derive_from_text_decoration<Cx: TContext>(context: &mut Cx) { + let derived = derive(context); + context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived); + } + + #[inline] + pub fn derive_from_display<Cx: TContext>(context: &mut Cx) { + let derived = derive(context); + context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived); + } +</%helpers:longhand> + +<%helpers:single_keyword_computed name="white-space" values="normal pre nowrap pre-wrap pre-line", + gecko_constant_prefix="NS_STYLE_WHITESPACE"> + use values::computed::ComputedValueAsSpecified; + impl ComputedValueAsSpecified for SpecifiedValue {} + + impl SpecifiedValue { + pub fn allow_wrap(&self) -> bool { + match *self { + SpecifiedValue::nowrap | + SpecifiedValue::pre => false, + SpecifiedValue::normal | + SpecifiedValue::pre_wrap | + SpecifiedValue::pre_line => true, + } + } + + pub fn preserve_newlines(&self) -> bool { + match *self { + SpecifiedValue::normal | + SpecifiedValue::nowrap => false, + SpecifiedValue::pre | + SpecifiedValue::pre_wrap | + SpecifiedValue::pre_line => true, + } + } + + pub fn preserve_spaces(&self) -> bool { + match *self { + SpecifiedValue::normal | + SpecifiedValue::nowrap | + SpecifiedValue::pre_line => false, + SpecifiedValue::pre | + SpecifiedValue::pre_wrap => true, + } + } + } +</%helpers:single_keyword_computed> + +<%helpers:longhand name="text-shadow"> + use cssparser::{self, ToCss}; + use std::fmt; + use values::AuExtensionMethods; + + #[derive(Clone, PartialEq, Debug, HeapSizeOf)] + pub struct SpecifiedValue(Vec<SpecifiedTextShadow>); + + #[derive(Clone, PartialEq, Debug, HeapSizeOf)] + pub struct SpecifiedTextShadow { + pub offset_x: specified::Length, + pub offset_y: specified::Length, + pub blur_radius: specified::Length, + pub color: Option<specified::CSSColor>, + } + + pub mod computed_value { + use app_units::Au; + use cssparser::Color; + + #[derive(Clone, PartialEq, Debug, HeapSizeOf)] + pub struct T(pub Vec<TextShadow>); + + #[derive(Clone, PartialEq, Debug, HeapSizeOf)] + pub struct TextShadow { + pub offset_x: Au, + pub offset_y: Au, + pub blur_radius: Au, + pub color: Color, + } + } + + impl ToCss for computed_value::T { + 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 computed_value::TextShadow { + 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)); + try!(dest.write_str(" ")); + try!(self.color.to_css(dest)); + Ok(()) + } + } + + 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(SpecifiedValue) + } + } + + fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> { + use app_units::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<Cx: TContext>(&self, context: &Cx) -> 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()) + } + } +</%helpers:longhand> + + + +// TODO(pcwalton): `full-width` +${helpers.single_keyword("text-transform", + "none capitalize uppercase lowercase", + extra_gecko_values="full-width")} + +${helpers.single_keyword("text-rendering", "auto optimizespeed optimizelegibility geometricprecision")} + +// CSS Text Module Level 3 +// https://www.w3.org/TR/css-text-3/ +${helpers.single_keyword("hyphens", "none manual auto", products="gecko")} + +// CSS Ruby Layout Module Level 1 +// https://www.w3.org/TR/css-ruby-1/ +${helpers.single_keyword("ruby-align", "start center space-between space-around", products="gecko")} + +${helpers.single_keyword("ruby-position", "over under", products="gecko")} diff --git a/components/style/properties/longhand/list.mako.rs b/components/style/properties/longhand/list.mako.rs new file mode 100644 index 00000000000..764a50617a6 --- /dev/null +++ b/components/style/properties/longhand/list.mako.rs @@ -0,0 +1,153 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("List", inherited=True, gecko_ffi_name="nsStyleList") %> + +${helpers.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/ +${helpers.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 +""")} + +<%helpers:longhand name="list-style-image"> + use cssparser::{ToCss, Token}; + use std::fmt; + use url::Url; + use values::LocalToCss; + + #[derive(Debug, Clone, PartialEq, Eq, HeapSizeOf)] + 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) => url.to_css(dest), + } + } + } + + pub mod computed_value { + use cssparser::{ToCss, Token}; + use std::fmt; + use url::Url; + use values::LocalToCss; + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Option<Url>); + + impl ToCss for T { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => dest.write_str("none"), + Some(ref url) => url.to_css(dest), + } + } + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T { + match *self { + SpecifiedValue::None => computed_value::T(None), + SpecifiedValue::Url(ref url) => computed_value::T(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 { + computed_value::T(None) + } +</%helpers:longhand> + +<%helpers:longhand name="quotes"> + use std::borrow::Cow; + use std::fmt; + use values::computed::ComputedValueAsSpecified; + + use cssparser::{ToCss, Token}; + + pub use self::computed_value::T as SpecifiedValue; + + pub mod computed_value { + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + 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 { + 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_owned(), "\u{201d}".to_owned()), + ("\u{2018}".to_owned(), "\u{2019}".to_owned()), + ]) + } + + 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(()) + } + } +</%helpers:longhand> diff --git a/components/style/properties/longhand/outline.mako.rs b/components/style/properties/longhand/outline.mako.rs index 0b36e664a23..3df026263b4 100644 --- a/components/style/properties/longhand/outline.mako.rs +++ b/components/style/properties/longhand/outline.mako.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> - <% from data import Method %> <% data.new_style_struct("Outline", diff --git a/components/style/properties/longhand/pointing.mako.rs b/components/style/properties/longhand/pointing.mako.rs new file mode 100644 index 00000000000..1d7f4a4040b --- /dev/null +++ b/components/style/properties/longhand/pointing.mako.rs @@ -0,0 +1,56 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("Pointing", inherited=True, gecko_ffi_name="nsStyleUserInterface") %> + +<%helpers:longhand name="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 style_traits::cursor::Cursor; + + #[derive(Clone, PartialEq, Eq, Copy, Debug, HeapSizeOf)] + 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; + use style_traits::cursor::Cursor; + let ident = try!(input.expect_ident()); + if ident.eq_ignore_ascii_case("auto") { + Ok(SpecifiedValue::AutoCursor) + } else { + Cursor::from_css_keyword(&ident) + .map(SpecifiedValue::SpecifiedCursor) + } + } +</%helpers: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. +${helpers.single_keyword("pointer-events", "auto none")} diff --git a/components/style/properties/longhand/position.mako.rs b/components/style/properties/longhand/position.mako.rs index 8a945fa0ec2..b3d35516b79 100644 --- a/components/style/properties/longhand/position.mako.rs +++ b/components/style/properties/longhand/position.mako.rs @@ -56,3 +56,62 @@ } } </%helpers:longhand> + +// CSS Flexible Box Layout Module Level 1 +// http://www.w3.org/TR/css3-flexbox/ + +// Flex container properties +${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse", experimental=True)} + +// https://drafts.csswg.org/css-flexbox/#propdef-order +<%helpers:longhand name="order"> + use values::computed::ComputedValueAsSpecified; + + impl ComputedValueAsSpecified for SpecifiedValue {} + + pub type SpecifiedValue = computed_value::T; + + pub mod computed_value { + pub type T = i32; + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + 0 + } + + fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + specified::parse_integer(input) + } +</%helpers:longhand> + +${helpers.predefined_type("flex-basis", + "LengthOrPercentageOrAutoOrContent", + "computed::LengthOrPercentageOrAutoOrContent::Auto")} + +${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse", products="gecko")} + +${helpers.predefined_type("min-width", + "LengthOrPercentage", + "computed::LengthOrPercentage::Length(Au(0))", + "parse_non_negative")} +${helpers.predefined_type("max-width", + "LengthOrPercentageOrNone", + "computed::LengthOrPercentageOrNone::None", + "parse_non_negative")} + +${helpers.predefined_type("min-height", + "LengthOrPercentage", + "computed::LengthOrPercentage::Length(Au(0))", + "parse_non_negative")} +${helpers.predefined_type("max-height", + "LengthOrPercentageOrNone", + "computed::LengthOrPercentageOrNone::None", + "parse_non_negative")} + +${helpers.single_keyword("box-sizing", + "content-box border-box")} + +// CSS Image Values and Replaced Content Module Level 3 +// https://drafts.csswg.org/css-images-3/ +${helpers.single_keyword("object-fit", "fill contain cover none scale-down", products="gecko")} diff --git a/components/style/properties/longhand/svg.mako.rs b/components/style/properties/longhand/svg.mako.rs new file mode 100644 index 00000000000..759ac3fe2f6 --- /dev/null +++ b/components/style/properties/longhand/svg.mako.rs @@ -0,0 +1,18 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("SVG", inherited=False, gecko_ffi_name="nsStyleSVGReset") %> + +${helpers.single_keyword("dominant-baseline", + """auto use-script no-change reset-size ideographic alphabetic hanging + mathematical central middle text-after-edge text-before-edge""", + products="gecko")} + +${helpers.single_keyword("vector-effect", "none non-scaling-stroke", products="gecko")} + +// CSS Masking Module Level 1 +// https://www.w3.org/TR/css-masking-1/ +${helpers.single_keyword("mask-type", "luminance alpha", products="gecko")} diff --git a/components/style/properties/longhand/svg_inherited.mako.rs b/components/style/properties/longhand/svg_inherited.mako.rs new file mode 100644 index 00000000000..2a0c6884974 --- /dev/null +++ b/components/style/properties/longhand/svg_inherited.mako.rs @@ -0,0 +1,38 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +// SVG 1.1 (Second Edition) +// https://www.w3.org/TR/SVG/ +<% data.new_style_struct("SVGInherited", + inherited=True, + gecko_ffi_name="nsStyleSVG") %> + +// Section 10 - Text + +${helpers.single_keyword("text-anchor", "start middle end", products="gecko")} + +// Section 11 - Painting: Filling, Stroking and Marker Symbols +${helpers.single_keyword("color-interpolation", "auto sRGB linearRGB", products="gecko")} + +${helpers.single_keyword("color-interpolation-filters", + "auto sRGB linearRGB", + products="gecko", + gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION")} + +${helpers.single_keyword("fill-rule", "nonzero evenodd", products="gecko")} + +${helpers.single_keyword("shape-rendering", + "auto optimizeSpeed crispEdges geometricPrecision", + products="gecko")} + +${helpers.single_keyword("stroke-linecap", "butt round square", products="gecko")} + +${helpers.single_keyword("stroke-linejoin", "miter round bevel", products="gecko")} + +// Section 14 - Clipping, Masking and Compositing +${helpers.single_keyword("clip-rule", "nonzero evenodd", + products="gecko", + gecko_constant_prefix="NS_STYLE_FILL_RULE")} diff --git a/components/style/properties/longhand/table.mako.rs b/components/style/properties/longhand/table.mako.rs new file mode 100644 index 00000000000..0abe7b2ce1a --- /dev/null +++ b/components/style/properties/longhand/table.mako.rs @@ -0,0 +1,9 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<% data.new_style_struct("Table", inherited=False, gecko_ffi_name="nsStyleTable") %> + +${helpers.single_keyword("table-layout", "auto fixed", gecko_ffi_name="mLayoutStrategy")} diff --git a/components/style/properties/longhand/text.mako.rs b/components/style/properties/longhand/text.mako.rs new file mode 100644 index 00000000000..b6d30dd0131 --- /dev/null +++ b/components/style/properties/longhand/text.mako.rs @@ -0,0 +1,109 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> +<% from data import Method %> + +<% data.new_style_struct("Text", + inherited=False, + gecko_ffi_name="nsStyleTextReset", + additional_methods=[Method("has_underline", "bool"), + Method("has_overline", "bool"), + Method("has_line_through", "bool")]) %> + +${helpers.single_keyword("text-overflow", "clip ellipsis")} + +${helpers.single_keyword("unicode-bidi", "normal embed isolate bidi-override isolate-override plaintext")} + +<%helpers:longhand name="text-decoration" custom_cascade="${product == 'servo'}"> + use cssparser::ToCss; + use std::fmt; + use values::computed::ComputedValueAsSpecified; + + impl ComputedValueAsSpecified for SpecifiedValue {} + + #[derive(PartialEq, Eq, Copy, Clone, Debug, HeapSizeOf)] + 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(()) } + } + + % if product == "servo": + fn cascade_property_custom<C: ComputedValues>( + _declaration: &PropertyDeclaration, + _inherited_style: &C, + context: &mut computed::Context<C>, + _seen: &mut PropertyBitField, + _cacheable: &mut bool, + _error_reporter: &mut StdBox<ParseErrorReporter + Send>) { + longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context); + } + % endif +</%helpers:longhand> + +${helpers.single_keyword("text-decoration-style", + "-moz-none solid double dotted dashed wavy", + products="gecko")} diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 5b000c20642..ab71f3047aa 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -47,4363 +47,27 @@ pub mod longhands { use parser::ParserContext; use values::specified; + <%include file="/longhand/background.mako.rs" /> <%include file="/longhand/border.mako.rs" /> <%include file="/longhand/box.mako.rs" /> + <%include file="/longhand/color.mako.rs" /> + <%include file="/longhand/column.mako.rs" /> + <%include file="/longhand/counters.mako.rs" /> + <%include file="/longhand/effects.mako.rs" /> + <%include file="/longhand/font.mako.rs" /> + <%include file="/longhand/inherited_box.mako.rs" /> + <%include file="/longhand/inherited_table.mako.rs" /> + <%include file="/longhand/inherited_text.mako.rs" /> + <%include file="/longhand/list.mako.rs" /> <%include file="/longhand/margin.mako.rs" /> <%include file="/longhand/outline.mako.rs" /> <%include file="/longhand/padding.mako.rs" /> + <%include file="/longhand/pointing.mako.rs" /> <%include file="/longhand/position.mako.rs" /> - - <% data.new_style_struct("InheritedBox", inherited=True, gecko_ffi_name="nsStyleVisibility") %> - - ${helpers.single_keyword("direction", "ltr rtl", need_clone=True)} - - // CSS 2.1, Section 10 - Visual formatting model details - - <% data.switch_to_style_struct("Box") %> - - ${helpers.predefined_type("width", "LengthOrPercentageOrAuto", - "computed::LengthOrPercentageOrAuto::Auto", - "parse_non_negative")} - - ${helpers.predefined_type("height", "LengthOrPercentageOrAuto", - "computed::LengthOrPercentageOrAuto::Auto", - "parse_non_negative")} - - <% data.switch_to_style_struct("Position") %> - - ${helpers.predefined_type("min-width", "LengthOrPercentage", - "computed::LengthOrPercentage::Length(Au(0))", - "parse_non_negative")} - ${helpers.predefined_type("max-width", "LengthOrPercentageOrNone", - "computed::LengthOrPercentageOrNone::None", - "parse_non_negative")} - - ${helpers.predefined_type("min-height", "LengthOrPercentage", - "computed::LengthOrPercentage::Length(Au(0))", - "parse_non_negative")} - ${helpers.predefined_type("max-height", "LengthOrPercentageOrNone", - "computed::LengthOrPercentageOrNone::None", - "parse_non_negative")} - - <% data.new_style_struct("InheritedText", inherited=True, gecko_ffi_name="nsStyleText") %> - - <%helpers:longhand name="line-height"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - use values::CSSFloat; - - #[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)] - pub enum SpecifiedValue { - Normal, - Number(CSSFloat), - LengthOrPercentage(specified::LengthOrPercentage), - } - - 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::LengthOrPercentage(value) => value.to_css(dest), - SpecifiedValue::Number(number) => write!(dest, "{}", number), - } - } - } - /// normal | <number> | <length> | <percentage> - pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - use cssparser::Token; - use std::ascii::AsciiExt; - input.try(specified::LengthOrPercentage::parse_non_negative) - .map(SpecifiedValue::LengthOrPercentage) - .or_else(|()| { - match try!(input.next()) { - Token::Number(ref value) if value.value >= 0. => { - Ok(SpecifiedValue::Number(value.value)) - } - Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => { - Ok(SpecifiedValue::Normal) - } - _ => Err(()), - } - }) - } - pub mod computed_value { - use app_units::Au; - use std::fmt; - use values::CSSFloat; - #[derive(PartialEq, Copy, Clone, HeapSizeOf, Debug)] - pub enum T { - Normal, - Length(Au), - Number(CSSFloat), - } - } - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - computed_value::T::Normal => dest.write_str("normal"), - computed_value::T::Length(length) => length.to_css(dest), - computed_value::T::Number(number) => write!(dest, "{}", 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::Normal => computed_value::T::Normal, - SpecifiedValue::Number(value) => computed_value::T::Number(value), - SpecifiedValue::LengthOrPercentage(value) => { - match value { - specified::LengthOrPercentage::Length(value) => - computed_value::T::Length(value.to_computed_value(context)), - specified::LengthOrPercentage::Percentage(specified::Percentage(value)) => { - let fr = specified::Length::FontRelative(specified::FontRelativeLength::Em(value)); - computed_value::T::Length(fr.to_computed_value(context)) - }, - specified::LengthOrPercentage::Calc(calc) => { - let calc = calc.to_computed_value(context); - let fr = specified::FontRelativeLength::Em(calc.percentage()); - let fr = specified::Length::FontRelative(fr); - computed_value::T::Length(calc.length() + fr.to_computed_value(context)) - } - } - } - } - } - } - </%helpers:longhand> - - <% data.switch_to_style_struct("Box") %> - - <%helpers:longhand name="vertical-align"> - 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(Debug, Clone, PartialEq, Copy, HeapSizeOf)] - 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: - "${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}), - % endfor - _ => Err(()) - } - }) - } - pub mod computed_value { - use app_units::Au; - use std::fmt; - use values::AuExtensionMethods; - use values::{CSSFloat, computed}; - #[allow(non_camel_case_types)] - #[derive(PartialEq, Copy, Clone, HeapSizeOf, Debug)] - pub enum T { - % for keyword in vertical_align_keywords: - ${to_rust_ident(keyword)}, - % endfor - LengthOrPercentage(computed::LengthOrPercentage), - } - impl ::cssparser::ToCss for T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - % for keyword in vertical_align_keywords: - T::${to_rust_ident(keyword)} => dest.write_str("${keyword}"), - % endfor - T::LengthOrPercentage(value) => value.to_css(dest), - } - } - } - } - #[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<Cx: TContext>(&self, context: &Cx) -> 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) => - computed_value::T::LengthOrPercentage(value.to_computed_value(context)), - } - } - } - </%helpers:longhand> - - - // CSS 2.1, Section 11 - Visual effects - - // Non-standard, see https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box#Specifications - ${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box", products="servo", - internal=True)} - - ${helpers.single_keyword("overflow-clip-box", "padding-box content-box", products="gecko", - internal=True)} - - // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. - ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=True, - gecko_constant_prefix="NS_STYLE_OVERFLOW")} - - // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. - <%helpers:longhand name="overflow-y" need_clone="True"> - use super::overflow_x; - - 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(Debug, Clone, Copy, PartialEq, HeapSizeOf)] - 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - computed_value::T(self.0.to_computed_value(context)) - } - } - - 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(SpecifiedValue) - } - </%helpers:longhand> - - // CSSOM View Module - // https://www.w3.org/TR/cssom-view-1/ - ${helpers.single_keyword("scroll-behavior", "auto smooth", products="gecko")} - - // Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x - ${helpers.single_keyword("scroll-snap-type-x", "none mandatory proximity", - products="gecko", gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE")} - - // Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-y - ${helpers.single_keyword("scroll-snap-type-y", "none mandatory proximity", - products="gecko", gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE")} - - // Compositing and Blending Level 1 - // http://www.w3.org/TR/compositing-1/ - ${helpers.single_keyword("isolation", "auto isolate", products="gecko")} - - <% data.switch_to_style_struct("InheritedBox") %> - - // TODO: collapse. Well, do tables first. - ${helpers.single_keyword("visibility", "visible hidden", extra_gecko_values="collapse", - gecko_ffi_name="mVisible")} - - // CSS 2.1, Section 12 - Generated content, automatic numbering, and lists - - <% data.new_style_struct("Counters", inherited=False, gecko_ffi_name="nsStyleContent") %> - - <%helpers: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(Debug, PartialEq, Eq, Clone, HeapSizeOf)] - 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(Debug, PartialEq, Eq, Clone, HeapSizeOf)] - 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(()) - } - } - </%helpers:longhand> - - <% data.new_style_struct("List", inherited=True, gecko_ffi_name="nsStyleList") %> - - ${helpers.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/ - ${helpers.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 - """)} - - <%helpers:longhand name="list-style-image"> - use cssparser::{ToCss, Token}; - use std::fmt; - use url::Url; - use values::LocalToCss; - - #[derive(Debug, Clone, PartialEq, Eq, HeapSizeOf)] - 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) => url.to_css(dest), - } - } - } - - pub mod computed_value { - use cssparser::{ToCss, Token}; - use std::fmt; - use url::Url; - use values::LocalToCss; - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<Url>); - - impl ToCss for T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("none"), - Some(ref url) => url.to_css(dest), - } - } - } - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::None => computed_value::T(None), - SpecifiedValue::Url(ref url) => computed_value::T(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 { - computed_value::T(None) - } - </%helpers:longhand> - - <%helpers:longhand name="quotes"> - use std::borrow::Cow; - use std::fmt; - use values::computed::ComputedValueAsSpecified; - - use cssparser::{ToCss, Token}; - - pub use self::computed_value::T as SpecifiedValue; - - pub mod computed_value { - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - 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 { - 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_owned(), "\u{201d}".to_owned()), - ("\u{2018}".to_owned(), "\u{2019}".to_owned()), - ]) - } - - 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(()) - } - } - </%helpers:longhand> - - <% data.switch_to_style_struct("Counters") %> - - <%helpers:longhand name="counter-increment"> - use std::fmt; - use super::content; - use values::computed::ComputedValueAsSpecified; - - use cssparser::{ToCss, Token, serialize_identifier}; - use std::borrow::{Cow, ToOwned}; - - pub use self::computed_value::T as SpecifiedValue; - - pub mod computed_value { - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - 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 { - if !first { - try!(dest.write_str(" ")); - } - first = false; - try!(serialize_identifier(&pair.0, dest)); - try!(write!(dest, " {}", pair.1)); - } - Ok(()) - } - } - - pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { - parse_common(1, input) - } - - pub fn parse_common(default_value: i32, 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| specified::parse_integer(input)).unwrap_or(default_value); - counters.push((counter_name, counter_delta)) - } - - if !counters.is_empty() { - Ok(SpecifiedValue(counters)) - } else { - Err(()) - } - } - </%helpers:longhand> - - <%helpers:longhand name="counter-reset"> - pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value}; - use super::counter_increment::{parse_common}; - - pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { - parse_common(0, input) - } - </%helpers:longhand> - - // CSS 2.1, Section 13 - Paged media - - <% data.switch_to_style_struct("Box") %> - - ${helpers.single_keyword("page-break-after", "auto always avoid left right", products="gecko")} - ${helpers.single_keyword("page-break-before", "auto always avoid left right", products="gecko")} - ${helpers.single_keyword("page-break-inside", "auto avoid", - products="gecko", gecko_ffi_name="mBreakInside", gecko_constant_prefix="NS_STYLE_PAGE_BREAK")} - - // CSS 2.1, Section 14 - Colors and Backgrounds - - <% data.new_style_struct("Background", inherited=False, gecko_ffi_name="nsStyleBackground") %> - ${helpers.predefined_type( - "background-color", "CSSColor", - "::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")} - - <%helpers:longhand name="background-image"> - use cssparser::ToCss; - use std::fmt; - use values::specified::Image; - use values::LocalToCss; - - pub mod computed_value { - use values::computed; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<computed::Image>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("none"), - Some(computed::Image::Url(ref url)) => url.to_css(dest), - Some(computed::Image::LinearGradient(ref gradient)) => - gradient.to_css(dest) - } - } - } - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - 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 { - 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue(None) => computed_value::T(None), - SpecifiedValue(Some(ref image)) => - computed_value::T(Some(image.to_computed_value(context))), - } - } - } - </%helpers:longhand> - - <%helpers:longhand name="background-position"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - - pub mod computed_value { - use values::computed::LengthOrPercentage; - - #[derive(PartialEq, Copy, Clone, Debug, HeapSizeOf)] - pub struct T { - pub horizontal: LengthOrPercentage, - pub vertical: LengthOrPercentage, - } - } - - #[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)] - 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 ToCss for computed_value::T { - 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::LengthOrPercentage(_) => - PositionCategory::LengthOrPercentage, - } - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> 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) - } - </%helpers:longhand> - - ${helpers.single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")} - - ${helpers.single_keyword("background-attachment", "scroll fixed")} - - ${helpers.single_keyword("background-clip", "border-box padding-box content-box")} - - ${helpers.single_keyword("background-origin", "padding-box border-box content-box")} - - <%helpers:longhand name="background-size"> - use cssparser::{ToCss, Token}; - use std::ascii::AsciiExt; - use std::fmt; - - pub mod computed_value { - use values::computed::LengthOrPercentageOrAuto; - - #[derive(PartialEq, Clone, Debug, HeapSizeOf)] - pub struct ExplicitSize { - pub width: LengthOrPercentageOrAuto, - pub height: LengthOrPercentageOrAuto, - } - - #[derive(PartialEq, Clone, Debug, HeapSizeOf)] - pub enum T { - Explicit(ExplicitSize), - Cover, - Contain, - } - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - computed_value::T::Explicit(ref size) => size.to_css(dest), - computed_value::T::Cover => dest.write_str("cover"), - computed_value::T::Contain => dest.write_str("contain"), - } - } - } - - #[derive(Clone, PartialEq, Debug, HeapSizeOf)] - 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) - } - } - - impl ToCss for computed_value::ExplicitSize { - 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, HeapSizeOf)] - 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<Cx: TContext>(&self, context: &Cx) -> 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, - })) - } - </%helpers:longhand> - - <% data.new_style_struct("Color", inherited=True, gecko_ffi_name="nsStyleColor") %> - - <%helpers:raw_longhand name="color" need_clone="True"> - use cssparser::Color as CSSParserColor; - use cssparser::RGBA; - use values::specified::{CSSColor, CSSRGBA}; - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> 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 { - CSSParserColor::RGBA(rgba) => rgba, - CSSParserColor::CurrentColor => return Ok(DeclaredValue::Inherit) - }; - Ok(DeclaredValue::Value(CSSRGBA { - parsed: rgba, - authored: value.authored, - })) - } - </%helpers:raw_longhand> - - // CSS 2.1, Section 15 - Fonts - - <% data.new_style_struct("Font", inherited=True, gecko_ffi_name="nsStyleFont", - additional_methods=[Method("compute_font_hash", is_mut=True)]) %> - <%helpers:longhand name="font-family"> - use self::computed_value::FontFamily; - use values::computed::ComputedValueAsSpecified; - pub use self::computed_value::T as SpecifiedValue; - - const SERIF: &'static str = "serif"; - const SANS_SERIF: &'static str = "sans-serif"; - const CURSIVE: &'static str = "cursive"; - const FANTASY: &'static str = "fantasy"; - const MONOSPACE: &'static str = "monospace"; - - impl ComputedValueAsSpecified for SpecifiedValue {} - pub mod computed_value { - use cssparser::ToCss; - use std::fmt; - use string_cache::Atom; - - #[derive(Debug, PartialEq, Eq, Clone, Hash, HeapSizeOf, Deserialize, Serialize)] - 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, - FontFamily::Serif => super::SERIF, - FontFamily::SansSerif => super::SANS_SERIF, - FontFamily::Cursive => super::CURSIVE, - FontFamily::Fantasy => super::FANTASY, - FontFamily::Monospace => super::MONOSPACE - } - } - - pub fn from_atom(input: Atom) -> FontFamily { - let option = match_ignore_ascii_case! { &input, - super::SERIF => Some(FontFamily::Serif), - super::SANS_SERIF => Some(FontFamily::SansSerif), - super::CURSIVE => Some(FontFamily::Cursive), - super::FANTASY => Some(FontFamily::Fantasy), - super::MONOSPACE => Some(FontFamily::Monospace), - _ => None - }; - - match option { - Some(family) => family, - None => FontFamily::FamilyName(input) - } - } - } - impl ToCss for FontFamily { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - dest.write_str(self.name()) - } - } - 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(Debug, Clone, PartialEq, Eq, Hash, HeapSizeOf)] - pub struct T(pub Vec<FontFamily>); - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T(vec![FontFamily::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(&*value))) - } - let first_ident = try!(input.expect_ident()); - - match_ignore_ascii_case! { first_ident, - SERIF => return Ok(FontFamily::Serif), - SANS_SERIF => return Ok(FontFamily::SansSerif), - CURSIVE => return Ok(FontFamily::Cursive), - FANTASY => return Ok(FontFamily::Fantasy), - MONOSPACE => return Ok(FontFamily::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(value))) - } - </%helpers:longhand> - - - ${helpers.single_keyword("font-style", "normal italic oblique")} - ${helpers.single_keyword("font-variant", "normal small-caps")} - - <%helpers:longhand name="font-weight" need_clone="True"> - use cssparser::ToCss; - use std::fmt; - - #[derive(Debug, Clone, PartialEq, Eq, Copy, HeapSizeOf)] - 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, Deserialize, Serialize, HeapSizeOf, Debug)] - pub enum T { - % for weight in range(100, 901, 100): - Weight${weight} = ${weight}, - % endfor - } - impl T { - #[inline] - pub fn is_bold(self) -> bool { - match self { - T::Weight900 | T::Weight800 | - T::Weight700 | T::Weight600 => true, - _ => false - } - } - } - } - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - % for weight in range(100, 901, 100): - computed_value::T::Weight${weight} => dest.write_str("${weight}"), - % endfor - } - } - } - #[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<Cx: TContext>(&self, context: &Cx) -> 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_style().get_font().clone_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_style().get_font().clone_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, - }, - } - } - } - </%helpers:longhand> - - <%helpers:longhand name="font-size" need_clone="True"> - use app_units::Au; - use cssparser::ToCss; - use std::fmt; - use values::FONT_MEDIUM_PX; - use values::specified::{LengthOrPercentage, Length, Percentage}; - - impl ToCss for SpecifiedValue { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.0.to_css(dest) - } - } - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct SpecifiedValue(pub specified::LengthOrPercentage); - pub mod computed_value { - use app_units::Au; - pub type T = Au; - } - #[inline] pub fn get_initial_value() -> computed_value::T { - Au::from_px(FONT_MEDIUM_PX) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match self.0 { - LengthOrPercentage::Length(Length::FontRelative(value)) => { - value.to_computed_value(context.inherited_style().get_font().clone_font_size(), - context.style().root_font_size()) - } - LengthOrPercentage::Length(Length::ServoCharacterWidth(value)) => { - value.to_computed_value(context.inherited_style().get_font().clone_font_size()) - } - LengthOrPercentage::Length(l) => { - l.to_computed_value(context) - } - LengthOrPercentage::Percentage(Percentage(value)) => { - context.inherited_style().get_font().clone_font_size().scale_by(value) - } - LengthOrPercentage::Calc(calc) => { - let calc = calc.to_computed_value(context); - calc.length() + context.inherited_style().get_font().clone_font_size() - .scale_by(calc.percentage()) - } - } - } - } - /// <length> | <percentage> | <absolute-size> | <relative-size> - pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - use values::specified::{Length, LengthOrPercentage}; - - input.try(specified::LengthOrPercentage::parse_non_negative) - .or_else(|()| { - let ident = try!(input.expect_ident()); - specified::Length::from_str(&ident as &str) - .ok_or(()) - .map(specified::LengthOrPercentage::Length) - }) - .map(SpecifiedValue) - } - </%helpers:longhand> - - ${helpers.single_keyword("font-stretch", - "normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded \ - expanded extra-expanded ultra-expanded")} - - ${helpers.single_keyword("font-kerning", "auto none normal", products="gecko")} - - // CSS 2.1, Section 16 - Text - - <% data.switch_to_style_struct("InheritedText") %> - - <%helpers: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, - servo_left("-servo-left") => 7, - servo_right("-servo-right") => 8, - } - } - #[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) - } - </%helpers:longhand> - - <%helpers:longhand name="letter-spacing"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - - #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] - 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 app_units::Au; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<Au>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("normal"), - Some(l) => l.to_css(dest), - } - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T(None) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::Normal => computed_value::T(None), - SpecifiedValue::Specified(l) => - computed_value::T(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) - } - } - </%helpers:longhand> - - <%helpers:longhand name="word-spacing"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - - #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] - 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 app_units::Au; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<Au>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("normal"), - Some(l) => l.to_css(dest), - } - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T(None) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::Normal => computed_value::T(None), - SpecifiedValue::Specified(l) => - computed_value::T(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) - } - } - </%helpers:longhand> - - ${helpers.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. - ${helpers.single_keyword("overflow-wrap", "normal break-word", gecko_ffi_name="mWordWrap", - gecko_constant_prefix="NS_STYLE_WORDWRAP")} - - // TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support. - ${helpers.single_keyword("word-break", "normal break-all", extra_gecko_values="keep-all", - gecko_constant_prefix="NS_STYLE_WORDBREAK")} - - // TODO(pcwalton): Support `text-justify: distribute`. - ${helpers.single_keyword("text-justify", "auto none inter-word", products="servo")} - - <% data.new_style_struct("Text", inherited=False, gecko_ffi_name="nsStyleTextReset", - additional_methods=[Method("has_underline", "bool"), - Method("has_overline", "bool"), - Method("has_line_through", "bool")]) %> - - ${helpers.single_keyword("text-overflow", "clip ellipsis")} - - ${helpers.single_keyword("unicode-bidi", "normal embed isolate bidi-override isolate-override plaintext")} - - <%helpers:longhand name="text-decoration" custom_cascade="${product == 'servo'}"> - use cssparser::ToCss; - use std::fmt; - use values::computed::ComputedValueAsSpecified; - - impl ComputedValueAsSpecified for SpecifiedValue {} - - #[derive(PartialEq, Eq, Copy, Clone, Debug, HeapSizeOf)] - 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(()) } - } - - % if product == "servo": - fn cascade_property_custom<C: ComputedValues>( - _declaration: &PropertyDeclaration, - _inherited_style: &C, - context: &mut computed::Context<C>, - _seen: &mut PropertyBitField, - _cacheable: &mut bool, - _error_reporter: &mut StdBox<ParseErrorReporter + Send>) { - longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context); - } - % endif - </%helpers:longhand> - - ${helpers.single_keyword("text-decoration-style", "-moz-none solid double dotted dashed wavy", - products="gecko")} - - <% data.switch_to_style_struct("InheritedText") %> - - <%helpers:longhand name="-servo-text-decorations-in-effect" - derived_from="display text-decoration" need_clone="True" products="servo"> - use cssparser::{RGBA, ToCss}; - use std::fmt; - - use values::computed::ComputedValueAsSpecified; - use properties::style_struct_traits::{Box, Color, Text}; - - impl ComputedValueAsSpecified for SpecifiedValue {} - - #[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)] - 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; - } - - impl ToCss for SpecifiedValue { - fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { - // Web compat doesn't matter here. - Ok(()) - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - SpecifiedValue { - underline: None, - overline: None, - line_through: None, - } - } - - fn maybe<Cx: TContext>(flag: bool, context: &Cx) -> Option<RGBA> { - if flag { - Some(context.style().get_color().clone_color()) - } else { - None - } - } - - fn derive<Cx: TContext>(context: &Cx) -> computed_value::T { - // Start with no declarations if this is an atomic inline-level box; otherwise, start with the - // declarations in effect and add in the text decorations that this block specifies. - let mut result = match context.style().get_box().clone_display() { - super::display::computed_value::T::inline_block | - super::display::computed_value::T::inline_table => SpecifiedValue { - underline: None, - overline: None, - line_through: None, - }, - _ => context.inherited_style().get_inheritedtext().clone__servo_text_decorations_in_effect() - }; - - result.underline = maybe(context.style().get_text().has_underline() - || result.underline.is_some(), context); - result.overline = maybe(context.style().get_text().has_overline() - || result.overline.is_some(), context); - result.line_through = maybe(context.style().get_text().has_line_through() - || result.line_through.is_some(), context); - - result - } - - #[inline] - pub fn derive_from_text_decoration<Cx: TContext>(context: &mut Cx) { - let derived = derive(context); - context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived); - } - - #[inline] - pub fn derive_from_display<Cx: TContext>(context: &mut Cx) { - let derived = derive(context); - context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived); - } - </%helpers:longhand> - - <%helpers:single_keyword_computed name="white-space" values="normal pre nowrap pre-wrap pre-line", - gecko_constant_prefix="NS_STYLE_WHITESPACE"> - use values::computed::ComputedValueAsSpecified; - impl ComputedValueAsSpecified for SpecifiedValue {} - - impl SpecifiedValue { - pub fn allow_wrap(&self) -> bool { - match *self { - SpecifiedValue::nowrap | - SpecifiedValue::pre => false, - SpecifiedValue::normal | - SpecifiedValue::pre_wrap | - SpecifiedValue::pre_line => true, - } - } - - pub fn preserve_newlines(&self) -> bool { - match *self { - SpecifiedValue::normal | - SpecifiedValue::nowrap => false, - SpecifiedValue::pre | - SpecifiedValue::pre_wrap | - SpecifiedValue::pre_line => true, - } - } - - pub fn preserve_spaces(&self) -> bool { - match *self { - SpecifiedValue::normal | - SpecifiedValue::nowrap | - SpecifiedValue::pre_line => false, - SpecifiedValue::pre | - SpecifiedValue::pre_wrap => true, - } - } - } - </%helpers:single_keyword_computed> - - // TODO(pcwalton): `full-width` - ${helpers.single_keyword("text-transform", "none capitalize uppercase lowercase", - extra_gecko_values="full-width")} - - ${helpers.single_keyword("text-rendering", "auto optimizespeed optimizelegibility geometricprecision")} - - // CSS Text Module Level 3 - // https://www.w3.org/TR/css-text-3/ - ${helpers.single_keyword("hyphens", "none manual auto", products="gecko")} - - // CSS Ruby Layout Module Level 1 - // https://www.w3.org/TR/css-ruby-1/ - ${helpers.single_keyword("ruby-align", "start center space-between space-around", products="gecko")} - - ${helpers.single_keyword("ruby-position", "over under", products="gecko")} - - // CSS 2.1, Section 17 - Tables - <% data.new_style_struct("Table", inherited=False, gecko_ffi_name="nsStyleTable") %> - - ${helpers.single_keyword("table-layout", "auto fixed", gecko_ffi_name="mLayoutStrategy")} - - <% data.new_style_struct("InheritedTable", inherited=True, gecko_ffi_name="nsStyleTableBorder") %> - - ${helpers.single_keyword("border-collapse", "separate collapse", gecko_constant_prefix="NS_STYLE_BORDER")} - - ${helpers.single_keyword("empty-cells", "show hide", gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS")} - - ${helpers.single_keyword("caption-side", "top bottom", extra_gecko_values="right left top-outside bottom-outside")} - - <%helpers:longhand name="border-spacing"> - use app_units::Au; - use values::AuExtensionMethods; - - use cssparser::ToCss; - use std::fmt; - - pub mod computed_value { - use app_units::Au; - - #[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, HeapSizeOf)] - pub struct T { - pub horizontal: Au, - pub vertical: Au, - } - } - - #[derive(Clone, Debug, PartialEq, HeapSizeOf)] - 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 ToCss for computed_value::T { - 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<Cx: TContext>(&self, context: &Cx) -> 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"), - } - } - </%helpers:longhand> - - // CSS Fragmentation Module Level 3 - // https://www.w3.org/TR/css-break-3/ - <% data.switch_to_style_struct("Border") %> - - ${helpers.single_keyword("box-decoration-break", "slice clone", products="gecko")} - - // CSS Writing Modes Level 3 - // http://dev.w3.org/csswg/css-writing-modes/ - <% data.switch_to_style_struct("InheritedBox") %> - - ${helpers.single_keyword("writing-mode", "horizontal-tb vertical-rl vertical-lr", - experimental=True, need_clone=True)} - - // FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support) - // FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented - // FIXME(bholley): sideways-right is needed as an alias to sideways in gecko. - ${helpers.single_keyword("text-orientation", "sideways", - experimental=True, need_clone=True, - extra_gecko_values="mixed upright", - extra_servo_values="sideways-right sideways-left")} - - // CSS Color Module Level 4 - // https://drafts.csswg.org/css-color/ - ${helpers.single_keyword("color-adjust", "economy exact", products="gecko")} - - // CSS Basic User Interface Module Level 3 - // http://dev.w3.org/csswg/css-ui/ - <% data.switch_to_style_struct("Box") %> - - ${helpers.single_keyword("resize", "none both horizontal vertical", products="gecko")} - - <% data.switch_to_style_struct("Position") %> - - ${helpers.single_keyword("box-sizing", "content-box border-box")} - - <% data.new_style_struct("Pointing", inherited=True, gecko_ffi_name="nsStyleUserInterface") %> - - <%helpers:longhand name="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 style_traits::cursor::Cursor; - - #[derive(Clone, PartialEq, Eq, Copy, Debug, HeapSizeOf)] - 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; - use style_traits::cursor::Cursor; - let ident = try!(input.expect_ident()); - if ident.eq_ignore_ascii_case("auto") { - Ok(SpecifiedValue::AutoCursor) - } else { - Cursor::from_css_keyword(&ident) - .map(SpecifiedValue::SpecifiedCursor) - } - } - </%helpers: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. - ${helpers.single_keyword("pointer-events", "auto none")} - - - <% data.new_style_struct("Column", inherited=False, gecko_ffi_name="nsStyleColumn") %> - - <%helpers:longhand name="column-width" experimental="True"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - - #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] - 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 app_units::Au; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<Au>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("auto"), - Some(l) => l.to_css(dest), - } - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T(None) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::Auto => computed_value::T(None), - SpecifiedValue::Specified(l) => - computed_value::T(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) - } - } - </%helpers:longhand> - - <%helpers:longhand name="column-count" experimental="True"> - use cssparser::ToCss; - use std::fmt; - - #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] - 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 { - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<u32>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("auto"), - Some(count) => write!(dest, "{}", count), - } - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T(None) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::Auto => computed_value::T(None), - SpecifiedValue::Specified(count) => - computed_value::T(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!(specified::parse_integer(input)); - // Zero is invalid - if count <= 0 { - return Err(()) - } - Ok(SpecifiedValue::Specified(count as u32)) - } - } - </%helpers:longhand> - - <%helpers:longhand name="column-gap" experimental="True"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - - #[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)] - 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 app_units::Au; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<Au>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("normal"), - Some(l) => l.to_css(dest), - } - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T(None) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - match *self { - SpecifiedValue::Normal => computed_value::T(None), - SpecifiedValue::Specified(l) => - computed_value::T(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) - } - } - </%helpers:longhand> - - // Box-shadow, etc. - <% data.new_style_struct("Effects", inherited=False, gecko_ffi_name="nsStyleEffects") %> - - <%helpers:longhand name="opacity"> - use cssparser::ToCss; - use std::fmt; - use values::CSSFloat; - - impl ToCss for SpecifiedValue { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.0.to_css(dest) - } - } - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - 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<Cx: TContext>(&self, _context: &Cx) -> 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, ()> { - specified::parse_number(input).map(SpecifiedValue) - } - </%helpers:longhand> - - <%helpers:longhand name="box-shadow"> - use cssparser::{self, ToCss}; - use std::fmt; - use values::AuExtensionMethods; - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct SpecifiedValue(Vec<SpecifiedBoxShadow>); - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - 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 app_units::Au; - use std::fmt; - use values::computed; - - #[derive(Clone, PartialEq, HeapSizeOf, Debug)] - pub struct T(pub Vec<BoxShadow>); - - #[derive(Clone, PartialEq, Copy, HeapSizeOf, Debug)] - 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 ToCss for computed_value::T { - 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 computed_value::BoxShadow { - 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)); - try!(dest.write_str(" ")); - try!(self.color.to_css(dest)); - Ok(()) - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - computed_value::T(self.0.iter().map(|value| compute_one_box_shadow(value, context)).collect()) - } - } - - pub fn compute_one_box_shadow<Cx: TContext>(value: &SpecifiedBoxShadow, context: &Cx) - -> 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 app_units::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, - }) - } - </%helpers:longhand> - - <%helpers:longhand name="clip"> - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - - // NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2. - - pub mod computed_value { - use app_units::Au; - - #[derive(Clone, PartialEq, Eq, Copy, Debug, HeapSizeOf)] - pub struct ClipRect { - pub top: Au, - pub right: Option<Au>, - pub bottom: Option<Au>, - pub left: Au, - } - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Option<ClipRect>); - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.0 { - None => dest.write_str("auto"), - Some(rect) => { - try!(dest.write_str("rect(")); - try!(rect.top.to_css(dest)); - try!(dest.write_str(", ")); - if let Some(right) = rect.right { - try!(right.to_css(dest)); - try!(dest.write_str(", ")); - } else { - try!(dest.write_str("auto, ")); - } - - if let Some(bottom) = rect.bottom { - try!(bottom.to_css(dest)); - try!(dest.write_str(", ")); - } else { - try!(dest.write_str("auto, ")); - } - - try!(rect.left.to_css(dest)); - try!(dest.write_str(")")); - Ok(()) - } - } - } - } - - #[derive(Clone, Debug, PartialEq, Copy, HeapSizeOf)] - 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, HeapSizeOf)] - 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.bottom { - 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 { - computed_value::T(None) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - 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 app_units::Au; - use std::ascii::AsciiExt; - 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(()) - } - } - </%helpers:longhand> - - <% data.switch_to_style_struct("InheritedText") %> - - <%helpers:longhand name="text-shadow"> - use cssparser::{self, ToCss}; - use std::fmt; - use values::AuExtensionMethods; - - #[derive(Clone, PartialEq, Debug, HeapSizeOf)] - pub struct SpecifiedValue(Vec<SpecifiedTextShadow>); - - #[derive(Clone, PartialEq, Debug, HeapSizeOf)] - pub struct SpecifiedTextShadow { - pub offset_x: specified::Length, - pub offset_y: specified::Length, - pub blur_radius: specified::Length, - pub color: Option<specified::CSSColor>, - } - - pub mod computed_value { - use app_units::Au; - use cssparser::Color; - - #[derive(Clone, PartialEq, Debug, HeapSizeOf)] - pub struct T(pub Vec<TextShadow>); - - #[derive(Clone, PartialEq, Debug, HeapSizeOf)] - pub struct TextShadow { - pub offset_x: Au, - pub offset_y: Au, - pub blur_radius: Au, - pub color: Color, - } - } - - impl ToCss for computed_value::T { - 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 computed_value::TextShadow { - 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)); - try!(dest.write_str(" ")); - try!(self.color.to_css(dest)); - Ok(()) - } - } - - 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(SpecifiedValue) - } - } - - fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> { - use app_units::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<Cx: TContext>(&self, context: &Cx) -> 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()) - } - } - </%helpers:longhand> - - <% data.switch_to_style_struct("Effects") %> - - <%helpers:longhand name="filter"> - //pub use self::computed_value::T as SpecifiedValue; - use cssparser::ToCss; - use std::fmt; - use values::AuExtensionMethods; - use values::CSSFloat; - use values::specified::{Angle, Length}; - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct SpecifiedValue(Vec<SpecifiedFilter>); - - // TODO(pcwalton): `drop-shadow` - #[derive(Clone, PartialEq, Debug, HeapSizeOf)] - 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 app_units::Au; - use values::CSSFloat; - use values::specified::{Angle}; - - #[derive(Clone, PartialEq, Debug, HeapSizeOf, Deserialize, Serialize)] - 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, HeapSizeOf, Deserialize, Serialize)] - 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 { - if let Filter::Opacity(ref opacity_value) = *filter { - opacity *= *opacity_value - } - } - opacity - } - } - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut iter = self.filters.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 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 computed_value::Filter { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - computed_value::Filter::Blur(value) => { - try!(dest.write_str("blur(")); - try!(value.to_css(dest)); - try!(dest.write_str(")")); - } - computed_value::Filter::Brightness(value) => try!(write!(dest, "brightness({})", value)), - computed_value::Filter::Contrast(value) => try!(write!(dest, "contrast({})", value)), - computed_value::Filter::Grayscale(value) => try!(write!(dest, "grayscale({})", value)), - computed_value::Filter::HueRotate(value) => { - try!(dest.write_str("hue-rotate(")); - try!(value.to_css(dest)); - try!(dest.write_str(")")); - } - computed_value::Filter::Invert(value) => try!(write!(dest, "invert({})", value)), - computed_value::Filter::Opacity(value) => try!(write!(dest, "opacity({})", value)), - computed_value::Filter::Saturate(value) => try!(write!(dest, "saturate({})", value)), - computed_value::Filter::Sepia(value) => try!(write!(dest, "sepia({})", value)), - } - 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<Cx: TContext>(&self, context: &Cx) -> 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() } - } - } - </%helpers:longhand> - - <%helpers:longhand name="transform"> - use app_units::Au; - use values::CSSFloat; - - use cssparser::ToCss; - use std::fmt; - - pub mod computed_value { - use values::CSSFloat; - use values::computed; - - #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] - pub struct ComputedMatrix { - pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat, - pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat, - pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat, - pub m41: CSSFloat, pub m42: CSSFloat, pub m43: CSSFloat, pub m44: CSSFloat, - } - - impl ComputedMatrix { - pub fn identity() -> ComputedMatrix { - ComputedMatrix { - m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0, - m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0, - m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0, - m41: 0.0, m42: 0.0, m43: 0.0, m44: 1.0 - } - } - } - - #[derive(Clone, Debug, PartialEq, HeapSizeOf)] - pub enum ComputedOperation { - Matrix(ComputedMatrix), - Skew(computed::Angle, computed::Angle), - Translate(computed::LengthOrPercentage, - computed::LengthOrPercentage, - computed::Length), - Scale(CSSFloat, CSSFloat, CSSFloat), - Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle), - Perspective(computed::Length), - } - - #[derive(Clone, Debug, PartialEq, HeapSizeOf)] - pub struct T(pub Option<Vec<ComputedOperation>>); - } - - pub use self::computed_value::ComputedMatrix as SpecifiedMatrix; - - fn parse_two_lengths_or_percentages(input: &mut Parser) - -> Result<(specified::LengthOrPercentage, - specified::LengthOrPercentage),()> { - let first = try!(specified::LengthOrPercentage::parse(input)); - let second = input.try(|input| { - try!(input.expect_comma()); - specified::LengthOrPercentage::parse(input) - }).unwrap_or(specified::LengthOrPercentage::zero()); - Ok((first, second)) - } - - fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> { - let first = try!(specified::parse_number(input)); - let second = input.try(|input| { - try!(input.expect_comma()); - specified::parse_number(input) - }).unwrap_or(first); - Ok((first, second)) - } - - fn parse_two_angles(input: &mut Parser) -> Result<(specified::Angle, specified::Angle),()> { - let first = try!(specified::Angle::parse(input)); - let second = input.try(|input| { - try!(input.expect_comma()); - specified::Angle::parse(input) - }).unwrap_or(specified::Angle(0.0)); - Ok((first, second)) - } - - #[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)] - enum TranslateKind { - Translate, - TranslateX, - TranslateY, - TranslateZ, - Translate3D, - } - - #[derive(Clone, Debug, PartialEq, HeapSizeOf)] - enum SpecifiedOperation { - Matrix(SpecifiedMatrix), - Skew(specified::Angle, specified::Angle), - Translate(TranslateKind, - specified::LengthOrPercentage, - specified::LengthOrPercentage, - specified::Length), - Scale(CSSFloat, CSSFloat, CSSFloat), - Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle), - Perspective(specified::Length), - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { - // TODO(pcwalton) - Ok(()) - } - } - - impl ToCss for SpecifiedOperation { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - // todo(gw): implement serialization for transform - // types other than translate. - SpecifiedOperation::Matrix(_m) => { - Ok(()) - } - SpecifiedOperation::Skew(_sx, _sy) => { - Ok(()) - } - SpecifiedOperation::Translate(kind, tx, ty, tz) => { - match kind { - TranslateKind::Translate => { - try!(dest.write_str("translate(")); - try!(tx.to_css(dest)); - try!(dest.write_str(", ")); - try!(ty.to_css(dest)); - dest.write_str(")") - } - TranslateKind::TranslateX => { - try!(dest.write_str("translateX(")); - try!(tx.to_css(dest)); - dest.write_str(")") - } - TranslateKind::TranslateY => { - try!(dest.write_str("translateY(")); - try!(ty.to_css(dest)); - dest.write_str(")") - } - TranslateKind::TranslateZ => { - try!(dest.write_str("translateZ(")); - try!(tz.to_css(dest)); - dest.write_str(")") - } - TranslateKind::Translate3D => { - try!(dest.write_str("translate3d(")); - try!(tx.to_css(dest)); - try!(dest.write_str(", ")); - try!(ty.to_css(dest)); - try!(dest.write_str(", ")); - try!(tz.to_css(dest)); - dest.write_str(")") - } - } - } - SpecifiedOperation::Scale(_sx, _sy, _sz) => { - Ok(()) - } - SpecifiedOperation::Rotate(_ax, _ay, _az, _angle) => { - Ok(()) - } - SpecifiedOperation::Perspective(_p) => { - Ok(()) - } - } - } - } - - #[derive(Clone, Debug, PartialEq, HeapSizeOf)] - 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 { - if !first { - try!(dest.write_str(" ")); - } - first = false; - try!(operation.to_css(dest)) - } - Ok(()) - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - 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| { - specified::parse_number(input) - })); - if values.len() != 6 { - return Err(()) - } - result.push(SpecifiedOperation::Matrix( - SpecifiedMatrix { - m11: values[0], m12: values[1], m13: 0.0, m14: 0.0, - m21: values[2], m22: values[3], m23: 0.0, m24: 0.0, - m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0, - m41: values[4], m42: values[5], m43: 0.0, m44: 1.0 - })); - Ok(()) - })) - }, - "matrix3d" => { - try!(input.parse_nested_block(|input| { - let values = try!(input.parse_comma_separated(|input| { - specified::parse_number(input) - })); - if values.len() != 16 { - return Err(()) - } - result.push(SpecifiedOperation::Matrix( - SpecifiedMatrix { - m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3], - m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7], - m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11], - m41: values[12], m42: values[13], m43: values[14], m44: values[15] - })); - Ok(()) - })) - }, - "translate" => { - try!(input.parse_nested_block(|input| { - let (tx, ty) = try!(parse_two_lengths_or_percentages(input)); - result.push(SpecifiedOperation::Translate(TranslateKind::Translate, - tx, - ty, - specified::Length::Absolute(Au(0)))); - Ok(()) - })) - }, - "translatex" => { - try!(input.parse_nested_block(|input| { - let tx = try!(specified::LengthOrPercentage::parse(input)); - result.push(SpecifiedOperation::Translate( - TranslateKind::TranslateX, - tx, - specified::LengthOrPercentage::zero(), - specified::Length::Absolute(Au(0)))); - Ok(()) - })) - }, - "translatey" => { - try!(input.parse_nested_block(|input| { - let ty = try!(specified::LengthOrPercentage::parse(input)); - result.push(SpecifiedOperation::Translate( - TranslateKind::TranslateY, - specified::LengthOrPercentage::zero(), - ty, - specified::Length::Absolute(Au(0)))); - Ok(()) - })) - }, - "translatez" => { - try!(input.parse_nested_block(|input| { - let tz = try!(specified::Length::parse(input)); - result.push(SpecifiedOperation::Translate( - TranslateKind::TranslateZ, - specified::LengthOrPercentage::zero(), - specified::LengthOrPercentage::zero(), - tz)); - Ok(()) - })) - }, - "translate3d" => { - try!(input.parse_nested_block(|input| { - let tx = try!(specified::LengthOrPercentage::parse(input)); - try!(input.expect_comma()); - let ty = try!(specified::LengthOrPercentage::parse(input)); - try!(input.expect_comma()); - let tz = try!(specified::Length::parse(input)); - result.push(SpecifiedOperation::Translate( - TranslateKind::Translate3D, - tx, - ty, - tz)); - Ok(()) - })) - - }, - "scale" => { - try!(input.parse_nested_block(|input| { - let (sx, sy) = try!(parse_two_floats(input)); - result.push(SpecifiedOperation::Scale(sx, sy, 1.0)); - Ok(()) - })) - }, - "scalex" => { - try!(input.parse_nested_block(|input| { - let sx = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0)); - Ok(()) - })) - }, - "scaley" => { - try!(input.parse_nested_block(|input| { - let sy = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(1.0, sy, 1.0)); - Ok(()) - })) - }, - "scalez" => { - try!(input.parse_nested_block(|input| { - let sz = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(1.0, 1.0, sz)); - Ok(()) - })) - }, - "scale3d" => { - try!(input.parse_nested_block(|input| { - let sx = try!(specified::parse_number(input)); - try!(input.expect_comma()); - let sy = try!(specified::parse_number(input)); - try!(input.expect_comma()); - let sz = try!(specified::parse_number(input)); - result.push(SpecifiedOperation::Scale(sx, sy, sz)); - Ok(()) - })) - }, - "rotate" => { - try!(input.parse_nested_block(|input| { - let theta = try!(specified::Angle::parse(input)); - result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); - Ok(()) - })) - }, - "rotatex" => { - try!(input.parse_nested_block(|input| { - let theta = try!(specified::Angle::parse(input)); - result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta)); - Ok(()) - })) - }, - "rotatey" => { - try!(input.parse_nested_block(|input| { - let theta = try!(specified::Angle::parse(input)); - result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta)); - Ok(()) - })) - }, - "rotatez" => { - try!(input.parse_nested_block(|input| { - let theta = try!(specified::Angle::parse(input)); - result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta)); - Ok(()) - })) - }, - "rotate3d" => { - try!(input.parse_nested_block(|input| { - let ax = try!(specified::parse_number(input)); - try!(input.expect_comma()); - let ay = try!(specified::parse_number(input)); - try!(input.expect_comma()); - let az = try!(specified::parse_number(input)); - try!(input.expect_comma()); - let theta = try!(specified::Angle::parse(input)); - // TODO(gw): Check the axis can be normalized!! - result.push(SpecifiedOperation::Rotate(ax, ay, az, theta)); - Ok(()) - })) - }, - "skew" => { - try!(input.parse_nested_block(|input| { - let (theta_x, theta_y) = try!(parse_two_angles(input)); - result.push(SpecifiedOperation::Skew(theta_x, theta_y)); - Ok(()) - })) - }, - "skewx" => { - try!(input.parse_nested_block(|input| { - let theta_x = try!(specified::Angle::parse(input)); - result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0))); - Ok(()) - })) - }, - "skewy" => { - try!(input.parse_nested_block(|input| { - let theta_y = try!(specified::Angle::parse(input)); - result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y)); - Ok(()) - })) - }, - "perspective" => { - try!(input.parse_nested_block(|input| { - let d = try!(specified::Length::parse(input)); - result.push(SpecifiedOperation::Perspective(d)); - 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<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - if self.0.is_empty() { - return computed_value::T(None) - } - - let mut result = vec!(); - for operation in &self.0 { - match *operation { - SpecifiedOperation::Matrix(ref matrix) => { - result.push(computed_value::ComputedOperation::Matrix(*matrix)); - } - SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => { - result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context), - ty.to_computed_value(context), - tz.to_computed_value(context))); - } - SpecifiedOperation::Scale(sx, sy, sz) => { - result.push(computed_value::ComputedOperation::Scale(sx, sy, sz)); - } - SpecifiedOperation::Rotate(ax, ay, az, theta) => { - result.push(computed_value::ComputedOperation::Rotate(ax, ay, az, theta)); - } - SpecifiedOperation::Skew(theta_x, theta_y) => { - result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y)); - } - SpecifiedOperation::Perspective(d) => { - result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context))); - } - }; - } - - computed_value::T(Some(result)) - } - } - </%helpers:longhand> - - pub struct OriginParseResult { - horizontal: Option<specified::LengthOrPercentage>, - vertical: Option<specified::LengthOrPercentage>, - depth: Option<specified::Length> - } - - pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> { - use values::specified::{LengthOrPercentage, Percentage}; - let (mut horizontal, mut vertical, mut depth) = (None, None, None); - loop { - if let Err(_) = input.try(|input| { - let token = try!(input.expect_ident()); - match_ignore_ascii_case! { - token, - "left" => { - if horizontal.is_none() { - horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.0))) - } else { - return Err(()) - } - }, - "center" => { - if horizontal.is_none() { - horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.5))) - } else if vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5))) - } else { - return Err(()) - } - }, - "right" => { - if horizontal.is_none() { - horizontal = Some(LengthOrPercentage::Percentage(Percentage(1.0))) - } else { - return Err(()) - } - }, - "top" => { - if vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(0.0))) - } else { - return Err(()) - } - }, - "bottom" => { - if vertical.is_none() { - vertical = Some(LengthOrPercentage::Percentage(Percentage(1.0))) - } else { - return Err(()) - } - }, - _ => return Err(()) - } - Ok(()) - }) { - match LengthOrPercentage::parse(input) { - Ok(value) => { - if horizontal.is_none() { - horizontal = Some(value); - } else if vertical.is_none() { - vertical = Some(value); - } else if let LengthOrPercentage::Length(length) = value { - depth = Some(length); - } else { - break; - } - } - _ => break, - } - } - } - - if horizontal.is_some() || vertical.is_some() { - Ok(OriginParseResult { - horizontal: horizontal, - vertical: vertical, - depth: depth, - }) - } else { - Err(()) - } - } - - ${helpers.single_keyword("backface-visibility", "visible hidden")} - - ${helpers.single_keyword("transform-box", "border-box fill-box view-box", products="gecko")} - - ${helpers.single_keyword("transform-style", "auto flat preserve-3d")} - - <%helpers:longhand name="transform-origin"> - use app_units::Au; - use values::AuExtensionMethods; - use values::specified::{Length, LengthOrPercentage, Percentage}; - - use cssparser::ToCss; - use std::fmt; - - pub mod computed_value { - use values::computed::{Length, LengthOrPercentage}; - - #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] - pub struct T { - pub horizontal: LengthOrPercentage, - pub vertical: LengthOrPercentage, - pub depth: Length, - } - } - - #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] - pub struct SpecifiedValue { - horizontal: LengthOrPercentage, - vertical: LengthOrPercentage, - depth: Length, - } - - impl ToCss for computed_value::T { - 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)); - try!(dest.write_str(" ")); - self.depth.to_css(dest) - } - } - - 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)); - try!(dest.write_str(" ")); - self.depth.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), - depth: Au(0), - } - } - - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { - let result = try!(super::parse_origin(context, input)); - Ok(SpecifiedValue { - horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - depth: result.depth.unwrap_or(Length::Absolute(Au(0))), - }) - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - computed_value::T { - horizontal: self.horizontal.to_computed_value(context), - vertical: self.vertical.to_computed_value(context), - depth: self.depth.to_computed_value(context), - } - } - } - </%helpers:longhand> - - ${helpers.predefined_type("perspective", - "LengthOrNone", - "computed::LengthOrNone::None")} - - <%helpers:longhand name="perspective-origin"> - use values::specified::{LengthOrPercentage, Percentage}; - - use cssparser::ToCss; - use std::fmt; - - pub mod computed_value { - use values::computed::LengthOrPercentage; - - #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] - pub struct T { - pub horizontal: LengthOrPercentage, - pub vertical: LengthOrPercentage, - } - } - - impl ToCss for computed_value::T { - 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) - } - } - - #[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)] - 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(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { - let result = try!(super::parse_origin(context, input)); - match result.depth { - Some(_) => Err(()), - None => Ok(SpecifiedValue { - horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))), - }) - } - } - - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T { - computed_value::T { - horizontal: self.horizontal.to_computed_value(context), - vertical: self.vertical.to_computed_value(context), - } - } - } - </%helpers:longhand> - - ${helpers.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""", gecko_constant_prefix="NS_STYLE_BLEND")} - - // CSS Image Values and Replaced Content Module Level 3 - // https://drafts.csswg.org/css-images-3/ - - <% data.switch_to_style_struct("Position") %> - - ${helpers.single_keyword("object-fit", "fill contain cover none scale-down", products="gecko")} - - <% data.switch_to_style_struct("InheritedBox") %> - - <%helpers:longhand name="image-rendering"> - - pub mod computed_value { - use cssparser::ToCss; - use std::fmt; - - #[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)] - 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<Cx: TContext>(&self, _: &Cx) -> computed_value::T { - *self - } - } - </%helpers:longhand> - - <% data.switch_to_style_struct("Box") %> - - // TODO(pcwalton): Multiple transitions. - <%helpers: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::{TContext, ToComputedValue}; - - pub use values::computed::Time as SingleComputedValue; - - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] - pub struct T(pub Vec<SingleComputedValue>); - - impl ToComputedValue for T { - type ComputedValue = T; - - #[inline] - fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> 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)))) - } - </%helpers:longhand> - - // TODO(pcwalton): Lots more timing functions. - // TODO(pcwalton): Multiple transitions. - <%helpers:longhand name="transition-timing-function"> - use self::computed_value::{StartEnd, TransitionTimingFunction}; - - use euclid::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 euclid::point::Point2D; - use std::fmt; - - pub use self::TransitionTimingFunction as SingleComputedValue; - - #[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)] - 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, HeapSizeOf)] - 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, HeapSizeOf)] - 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<Cx: TContext>(&self, _: &Cx) -> 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!(specified::parse_number(input)); - try!(input.expect_comma()); - p1y = try!(specified::parse_number(input)); - try!(input.expect_comma()); - p2x = try!(specified::parse_number(input)); - try!(input.expect_comma()); - p2y = try!(specified::parse_number(input)); - Ok(()) - })); - let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(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!(specified::parse_integer(input)); - 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)))) - } - </%helpers:longhand> - - // TODO(pcwalton): Lots more properties. - <%helpers:longhand name="transition-property"> - use self::computed_value::TransitionProperty; - - 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, HeapSizeOf)] - 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, - Transform, - VerticalAlign, - Visibility, - Width, - WordSpacing, - ZIndex, - } - - pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 45] = [ - 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::Transform, - 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::Transform => dest.write_str("transform"), - 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, HeapSizeOf)] - 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), - "transform" => Ok(TransitionProperty::Transform), - "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<Cx: TContext>(&self, _: &Cx) -> computed_value::T { - (*self).clone() - } - } - </%helpers:longhand> - - <%helpers: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}; - </%helpers:longhand> - - // CSS Flexible Box Layout Module Level 1 - // http://www.w3.org/TR/css3-flexbox/ - - <% data.switch_to_style_struct("Position") %> - - // Flex container properties - ${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse", experimental=True)} - - // https://drafts.csswg.org/css-flexbox/#propdef-order - <%helpers:longhand name="order"> - use values::computed::ComputedValueAsSpecified; - - impl ComputedValueAsSpecified for SpecifiedValue {} - - pub type SpecifiedValue = computed_value::T; - - pub mod computed_value { - pub type T = i32; - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - 0 - } - - fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - specified::parse_integer(input) - } - </%helpers:longhand> - - ${helpers.predefined_type("flex-basis", "LengthOrPercentageOrAutoOrContent", - "computed::LengthOrPercentageOrAutoOrContent::Auto")} - - ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse", products="gecko")} - - // SVG 1.1 (Second Edition) - // https://www.w3.org/TR/SVG/ - <% data.new_style_struct("SVGInherited", inherited=True, gecko_ffi_name="nsStyleSVG") %> - - // Section 10 - Text - - ${helpers.single_keyword("text-anchor", "start middle end", products="gecko")} - - // Section 11 - Painting: Filling, Stroking and Marker Symbols - ${helpers.single_keyword("color-interpolation", "auto sRGB linearRGB", products="gecko")} - - ${helpers.single_keyword("color-interpolation-filters", "auto sRGB linearRGB", - products="gecko", gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION")} - - ${helpers.single_keyword("fill-rule", "nonzero evenodd", products="gecko")} - - ${helpers.single_keyword("shape-rendering", "auto optimizeSpeed crispEdges geometricPrecision", - products="gecko")} - - ${helpers.single_keyword("stroke-linecap", "butt round square", products="gecko")} - - ${helpers.single_keyword("stroke-linejoin", "miter round bevel", products="gecko")} - - // Section 14 - Clipping, Masking and Compositing - ${helpers.single_keyword("clip-rule", "nonzero evenodd", - products="gecko", gecko_constant_prefix="NS_STYLE_FILL_RULE")} - - <% data.new_style_struct("SVG", inherited=False, gecko_ffi_name="nsStyleSVGReset") %> - - ${helpers.single_keyword("dominant-baseline", - """auto use-script no-change reset-size ideographic alphabetic hanging - mathematical central middle text-after-edge text-before-edge""", - products="gecko")} - - ${helpers.single_keyword("vector-effect", "none non-scaling-stroke", products="gecko")} - - // CSS Masking Module Level 1 - // https://www.w3.org/TR/css-masking-1/ - ${helpers.single_keyword("mask-type", "luminance alpha", products="gecko")} + <%include file="/longhand/table.mako.rs" /> + <%include file="/longhand/text.mako.rs" /> + <%include file="/longhand/svg_inherited.mako.rs" /> + <%include file="/longhand/svg.mako.rs" /> } |