diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index c62518a9977..ced34de3ca2 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -53,7 +53,7 @@ use style::properties::{self, ServoComputedValues}; use style::properties::style_structs; use style::values::RGBA; use style::values::computed; -use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto, LinearGradient}; +use style::values::computed::{Gradient, GradientKind, LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; use style_traits::cursor::Cursor; use table_cell::CollapsedBordersForCell; @@ -167,13 +167,13 @@ pub trait FragmentDisplayListBuilding { /// Adds the display items necessary to paint the background linear gradient of this fragment /// to the appropriate section of the display list. - fn build_display_list_for_background_linear_gradient(&self, - state: &mut DisplayListBuildState, - display_list_section: DisplayListSection, - absolute_bounds: &Rect, - clip: &ClippingRegion, - gradient: &LinearGradient, - style: &ServoComputedValues); + fn build_display_list_for_background_gradient(&self, + state: &mut DisplayListBuildState, + display_list_section: DisplayListSection, + absolute_bounds: &Rect, + clip: &ClippingRegion, + gradient: &Gradient, + style: &ServoComputedValues); /// Adds the display items necessary to paint the borders of this fragment to a display list if /// necessary. @@ -402,13 +402,16 @@ impl FragmentDisplayListBuilding for Fragment { for (i, background_image) in background.background_image.0.iter().enumerate().rev() { match background_image.0 { None => {} - Some(computed::Image::LinearGradient(ref gradient)) => { - self.build_display_list_for_background_linear_gradient(state, - display_list_section, - &bounds, - &clip, - gradient, - style); + Some(computed::Image::Gradient(ref gradient)) => { + // FIXME: Radial gradients aren't implemented yet. + if let GradientKind::Linear(_) = gradient.gradient_kind { + self.build_display_list_for_background_gradient(state, + display_list_section, + &bounds, + &clip, + gradient, + style); + } } Some(computed::Image::Url(ref image_url, ref _extra_data)) => { self.build_display_list_for_background_image(state, @@ -636,36 +639,45 @@ impl FragmentDisplayListBuilding for Fragment { } } - fn build_display_list_for_background_linear_gradient(&self, - state: &mut DisplayListBuildState, - display_list_section: DisplayListSection, - absolute_bounds: &Rect, - clip: &ClippingRegion, - gradient: &LinearGradient, - style: &ServoComputedValues) { + fn build_display_list_for_background_gradient(&self, + state: &mut DisplayListBuildState, + display_list_section: DisplayListSection, + absolute_bounds: &Rect, + clip: &ClippingRegion, + gradient: &Gradient, + style: &ServoComputedValues) { let mut clip = clip.clone(); clip.intersect_rect(absolute_bounds); - let angle = match gradient.angle_or_corner { - AngleOrCorner::Angle(angle) => angle.radians(), - AngleOrCorner::Corner(horizontal, vertical) => { - // This the angle for one of the diagonals of the box. Our angle - // will either be this one, this one + PI, or one of the other - // two perpendicular angles. - let atan = (absolute_bounds.size.height.to_f32_px() / - absolute_bounds.size.width.to_f32_px()).atan(); - match (horizontal, vertical) { - (HorizontalDirection::Right, VerticalDirection::Bottom) - => f32::consts::PI - atan, - (HorizontalDirection::Left, VerticalDirection::Bottom) - => f32::consts::PI + atan, - (HorizontalDirection::Right, VerticalDirection::Top) - => atan, - (HorizontalDirection::Left, VerticalDirection::Top) - => -atan, + // FIXME: Repeating gradients aren't implemented yet. + if gradient.repeating { + return; + } + let angle = if let GradientKind::Linear(angle_or_corner) = gradient.gradient_kind { + match angle_or_corner { + AngleOrCorner::Angle(angle) => angle.radians(), + AngleOrCorner::Corner(horizontal, vertical) => { + // This the angle for one of the diagonals of the box. Our angle + // will either be this one, this one + PI, or one of the other + // two perpendicular angles. + let atan = (absolute_bounds.size.height.to_f32_px() / + absolute_bounds.size.width.to_f32_px()).atan(); + match (horizontal, vertical) { + (HorizontalDirection::Right, VerticalDirection::Bottom) + => f32::consts::PI - atan, + (HorizontalDirection::Left, VerticalDirection::Bottom) + => f32::consts::PI + atan, + (HorizontalDirection::Right, VerticalDirection::Top) + => atan, + (HorizontalDirection::Left, VerticalDirection::Top) + => -atan, + } } } + } else { + // FIXME: Radial gradients aren't implemented yet. + return; }; // Get correct gradient line length, based on: diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 4a49673dbc0..4dea502a5d5 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -173,8 +173,10 @@ use parser::{ParserContext, ParserContextExtraData}; use properties::{CSSWideKeyword, DeclaredValue, Shorthand}; % endif + #[allow(unused_imports)] use cascade_info::CascadeInfo; use error_reporting::ParseErrorReporter; + use parser::Parse; use properties::longhands; use properties::property_bit_field::PropertyBitField; use properties::{ComputedValues, PropertyDeclaration}; @@ -565,6 +567,8 @@ <%self:shorthand name="${name}" sub_properties="${ ' '.join(sub_property_pattern % side for side in ['top', 'right', 'bottom', 'left'])}"> + #[allow(unused_imports)] + use parser::Parse; use super::parse_four_sides; use values::specified; diff --git a/components/style/properties/longhand/effects.mako.rs b/components/style/properties/longhand/effects.mako.rs index 723875fbc99..5e000b9105f 100644 --- a/components/style/properties/longhand/effects.mako.rs +++ b/components/style/properties/longhand/effects.mako.rs @@ -15,6 +15,7 @@ ${helpers.predefined_type("opacity", <%helpers:vector_longhand name="box-shadow" allow_empty="True" animatable="True"> use cssparser::{self, ToCss}; use std::fmt; + use parser::Parse; use values::LocalToCss; use values::HasViewportPercentage; @@ -355,6 +356,7 @@ ${helpers.predefined_type("opacity", pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { use app_units::Au; + use parser::Parse; use std::ascii::AsciiExt; use values::specified::Length; @@ -1199,6 +1201,7 @@ pub struct OriginParseResult { } pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result { + use parser::Parse; use values::specified::{LengthOrPercentage, Percentage}; let (mut horizontal, mut vertical, mut depth) = (None, None, None); loop { diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs new file mode 100644 index 00000000000..d92ed0689dc --- /dev/null +++ b/components/style/values/computed/image.rs @@ -0,0 +1,460 @@ +/* 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/. */ + +//! CSS handling for the computed value of +//! [`image`][image]s +//! +//! [image]: https://drafts.csswg.org/css-images/#image-values + +use cssparser::Color as CSSColor; +use std::fmt; +use url::Url; +use values::LocalToCss; +use values::computed::{Context, Length, LengthOrPercentage, ToComputedValue}; +use values::computed::position::Position; +use values::specified; +use values::specified::{AngleOrCorner, SizeKeyword, UrlExtraData}; + + +impl ToComputedValue for specified::Image { + type ComputedValue = Image; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Image { + match *self { + specified::Image::Url(ref url, ref extra_data) => { + Image::Url(url.clone(), extra_data.clone()) + }, + specified::Image::Gradient(ref gradient) => { + Image::Gradient(gradient.to_computed_value(context)) + } + } + } + + #[inline] + fn from_computed_value(computed: &Image) -> Self { + match *computed { + Image::Url(ref url, ref extra_data) => { + specified::Image::Url(url.clone(), extra_data.clone()) + }, + Image::Gradient(ref linear_gradient) => { + specified::Image::Gradient( + ToComputedValue::from_computed_value(linear_gradient) + ) + } + } + } +} + +/// Computed values for an image according to CSS-IMAGES. +/// https://drafts.csswg.org/css-images/#image-values +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Image { + Url(Url, UrlExtraData), + Gradient(Gradient), +} + +impl fmt::Debug for Image { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Image::Url(ref url, ref _extra_data) => write!(f, "url(\"{}\")", url), + Image::Gradient(ref grad) => { + if grad.repeating { + let _ = write!(f, "repeating-"); + } + match grad.gradient_kind { + GradientKind::Linear(_) => write!(f, "linear-gradient({:?})", grad), + GradientKind::Radial(_, _) => write!(f, "radial-gradient({:?})", grad), + } + }, + } + } +} + +impl ::cssparser::ToCss for Image { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + use values::LocalToCss; + match *self { + Image::Url(ref url, _) => { + url.to_css(dest) + } + Image::Gradient(ref gradient) => gradient.to_css(dest) + } + } +} + +/// Computed values for a CSS gradient. +/// https://drafts.csswg.org/css-images/#gradients +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Gradient { + /// The color stops. + pub stops: Vec, + /// True if this is a repeating gradient. + pub repeating: bool, + /// Gradient kind can be linear or radial. + pub gradient_kind: GradientKind, +} + +impl ::cssparser::ToCss for Gradient { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.repeating { + try!(dest.write_str("repeating-")); + } + match self.gradient_kind { + GradientKind::Linear(angle_or_corner) => { + try!(dest.write_str("linear-gradient(")); + try!(angle_or_corner.to_css(dest)); + }, + GradientKind::Radial(ref shape, position) => { + try!(dest.write_str("radial-gradient(")); + try!(shape.to_css(dest)); + try!(dest.write_str(" at ")); + try!(position.to_css(dest)); + }, + } + for stop in &self.stops { + try!(dest.write_str(", ")); + try!(stop.to_css(dest)); + } + try!(dest.write_str(")")); + Ok(()) + } +} + +impl fmt::Debug for Gradient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.gradient_kind { + GradientKind::Linear(angle_or_corner) => { + let _ = write!(f, "{:?}", angle_or_corner); + }, + GradientKind::Radial(ref shape, position) => { + let _ = write!(f, "{:?} at {:?}", shape, position); + }, + } + + for stop in &self.stops { + let _ = write!(f, ", {:?}", stop); + } + Ok(()) + } +} + +impl ToComputedValue for specified::Gradient { + type ComputedValue = Gradient; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Gradient { + let specified::Gradient { + ref stops, + repeating, + ref gradient_kind + } = *self; + Gradient { + stops: stops.iter().map(|s| s.to_computed_value(context)).collect(), + repeating: repeating, + gradient_kind: gradient_kind.to_computed_value(context), + } + } + #[inline] + fn from_computed_value(computed: &Gradient) -> Self { + let Gradient { + ref stops, + repeating, + ref gradient_kind + } = *computed; + specified::Gradient { + stops: stops.iter().map(ToComputedValue::from_computed_value).collect(), + repeating: repeating, + gradient_kind: ToComputedValue::from_computed_value(gradient_kind), + } + } +} + +/// Computed values for CSS linear or radial gradients. +/// https://drafts.csswg.org/css-images/#gradients +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GradientKind { + Linear(AngleOrCorner), + Radial(EndingShape, Position), +} + +impl ToComputedValue for specified::GradientKind { + type ComputedValue = GradientKind; + + #[inline] + fn to_computed_value(&self, context: &Context) -> GradientKind { + match *self { + specified::GradientKind::Linear(angle_or_corner) => { + GradientKind::Linear(angle_or_corner) + }, + specified::GradientKind::Radial(ref shape, position) => { + GradientKind::Radial(shape.to_computed_value(context), + position.to_computed_value(context)) + }, + } + } + #[inline] + fn from_computed_value(computed: &GradientKind) -> Self { + match *computed { + GradientKind::Linear(angle_or_corner) => { + specified::GradientKind::Linear(ToComputedValue::from_computed_value(&angle_or_corner)) + }, + GradientKind::Radial(ref shape, position) => { + specified::GradientKind::Radial(ToComputedValue::from_computed_value(shape), + ToComputedValue::from_computed_value(&position)) + }, + } + } +} + +/// Computed values for one color stop in a linear gradient. +/// https://drafts.csswg.org/css-images/#typedef-color-stop-list +#[derive(Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct ColorStop { + /// The color of this stop. + pub color: CSSColor, + + /// The position of this stop. If not specified, this stop is placed halfway between the + /// point that precedes it and the point that follows it per CSS-IMAGES § 3.4. + pub position: Option, +} + +impl ::cssparser::ToCss for ColorStop { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + try!(self.color.to_css(dest)); + if let Some(position) = self.position { + try!(dest.write_str(" ")); + try!(position.to_css(dest)); + } + Ok(()) + } +} + +impl fmt::Debug for ColorStop { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ = write!(f, "{:?}", self.color); + self.position.map(|pos| { + let _ = write!(f, " {:?}", pos); + }); + Ok(()) + } +} + +impl ToComputedValue for specified::ColorStop { + type ComputedValue = ColorStop; + + #[inline] + fn to_computed_value(&self, context: &Context) -> ColorStop { + ColorStop { + color: self.color.parsed, + position: match self.position { + None => None, + Some(value) => Some(value.to_computed_value(context)), + }, + } + } + #[inline] + fn from_computed_value(computed: &ColorStop) -> Self { + specified::ColorStop { + color: ToComputedValue::from_computed_value(&computed.color), + position: match computed.position { + None => None, + Some(value) => Some(ToComputedValue::from_computed_value(&value)), + }, + } + } +} + +/// Computed values for EndingShape +/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-ending-shape +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum EndingShape { + Circle(LengthOrKeyword), + Ellipse(LengthOrPercentageOrKeyword), +} + +impl ::cssparser::ToCss for EndingShape { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + EndingShape::Circle(ref length) => { + try!(dest.write_str("circle ")); + try!(length.to_css(dest)); + }, + EndingShape::Ellipse(ref length) => { + try!(dest.write_str("ellipse ")); + try!(length.to_css(dest)); + }, + } + Ok(()) + } +} + +impl fmt::Debug for EndingShape { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + EndingShape::Circle(ref length) => { + let _ = write!(f, "circle {:?}", length); + }, + EndingShape::Ellipse(ref length) => { + let _ = write!(f, "ellipse {:?}", length); + } + } + Ok(()) + } +} + +impl ToComputedValue for specified::GradientEndingShape { + type ComputedValue = EndingShape; + + #[inline] + fn to_computed_value(&self, context: &Context) -> EndingShape { + match *self { + specified::GradientEndingShape::Circle(ref length) => { + EndingShape::Circle(length.to_computed_value(context)) + }, + specified::GradientEndingShape::Ellipse(ref length) => { + EndingShape::Ellipse(length.to_computed_value(context)) + }, + } + } + #[inline] + fn from_computed_value(computed: &EndingShape) -> Self { + match *computed { + EndingShape::Circle(ref length) => { + specified::GradientEndingShape::Circle(ToComputedValue::from_computed_value(length)) + }, + EndingShape::Ellipse(ref length) => { + specified::GradientEndingShape::Ellipse(ToComputedValue::from_computed_value(length)) + }, + } + } +} + +/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrKeyword { + Length(Length), + Keyword(SizeKeyword), +} + +impl ::cssparser::ToCss for LengthOrKeyword { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrKeyword::Length(ref length) => length.to_css(dest), + LengthOrKeyword::Keyword(keyword) => keyword.to_css(dest), + } + } +} + +impl fmt::Debug for LengthOrKeyword { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrKeyword::Length(ref length) => { + let _ = write!(f, "{:?}", length); + }, + LengthOrKeyword::Keyword(keyword) => { + let _ = write!(f, "{:?}", keyword); + }, + } + Ok(()) + } +} + +impl ToComputedValue for specified::LengthOrKeyword { + type ComputedValue = LengthOrKeyword; + + #[inline] + fn to_computed_value(&self, context: &Context) -> LengthOrKeyword { + match *self { + specified::LengthOrKeyword::Length(length) => { + LengthOrKeyword::Length(length.to_computed_value(context)) + }, + specified::LengthOrKeyword::Keyword(keyword) => { + LengthOrKeyword::Keyword(keyword) + }, + } + } + #[inline] + fn from_computed_value(computed: &LengthOrKeyword) -> Self { + match *computed { + LengthOrKeyword::Length(length) => { + specified::LengthOrKeyword::Length(ToComputedValue::from_computed_value(&length)) + }, + LengthOrKeyword::Keyword(keyword) => { + specified::LengthOrKeyword::Keyword(keyword) + }, + } + } +} + +/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrKeyword { + LengthOrPercentage(LengthOrPercentage, LengthOrPercentage), + Keyword(SizeKeyword), +} + +impl ::cssparser::ToCss for LengthOrPercentageOrKeyword { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => { + try!(first_len.to_css(dest)); + try!(dest.write_str(" ")); + second_len.to_css(dest) + }, + LengthOrPercentageOrKeyword::Keyword(keyword) => keyword.to_css(dest), + } + } +} + +impl fmt::Debug for LengthOrPercentageOrKeyword { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => { + let _ = write!(f, "{:?} {:?}", first_len, second_len); + }, + LengthOrPercentageOrKeyword::Keyword(keyword) => { + let _ = write!(f, "{:?}", keyword); + }, + } + Ok(()) + } +} + +impl ToComputedValue for specified::LengthOrPercentageOrKeyword { + type ComputedValue = LengthOrPercentageOrKeyword; + + #[inline] + fn to_computed_value(&self, context: &Context) -> LengthOrPercentageOrKeyword { + match *self { + specified::LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) => { + LengthOrPercentageOrKeyword::LengthOrPercentage(first_len.to_computed_value(context), + second_len.to_computed_value(context)) + }, + specified::LengthOrPercentageOrKeyword::Keyword(keyword) => { + LengthOrPercentageOrKeyword::Keyword(keyword) + }, + } + } + #[inline] + fn from_computed_value(computed: &LengthOrPercentageOrKeyword) -> Self { + match *computed { + LengthOrPercentageOrKeyword::LengthOrPercentage(first_len, second_len) => { + specified::LengthOrPercentageOrKeyword::LengthOrPercentage( + ToComputedValue::from_computed_value(&first_len), + ToComputedValue::from_computed_value(&second_len)) + }, + LengthOrPercentageOrKeyword::Keyword(keyword) => { + specified::LengthOrPercentageOrKeyword::Keyword(keyword) + }, + } + } +} diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 1f51fa6212b..90a1462d834 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -9,13 +9,14 @@ use properties::ComputedValues; use std::fmt; use super::{CSSFloat, specified}; use super::LocalToCss; -use super::specified::AngleOrCorner; -use url::Url; pub use cssparser::Color as CSSColor; +pub use self::image::{EndingShape as GradientShape, Gradient, GradientKind, Image}; +pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword}; pub use super::specified::{Angle, BorderStyle, Time, UrlExtraData}; pub mod basic_shape; +pub mod image; pub mod position; pub struct Context<'a> { @@ -640,175 +641,6 @@ impl ::cssparser::ToCss for LengthOrNone { } } -impl ToComputedValue for specified::Image { - type ComputedValue = Image; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Image { - match *self { - specified::Image::Url(ref url, ref extra_data) => { - Image::Url(url.clone(), extra_data.clone()) - }, - specified::Image::LinearGradient(ref linear_gradient) => { - Image::LinearGradient(linear_gradient.to_computed_value(context)) - } - } - } - - #[inline] - fn from_computed_value(computed: &Image) -> Self { - match *computed { - Image::Url(ref url, ref extra_data) => { - specified::Image::Url(url.clone(), extra_data.clone()) - }, - Image::LinearGradient(ref linear_gradient) => { - specified::Image::LinearGradient( - ToComputedValue::from_computed_value(linear_gradient) - ) - } - } - } -} - - -/// Computed values for an image according to CSS-IMAGES. -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Image { - Url(Url, UrlExtraData), - LinearGradient(LinearGradient), -} - -impl fmt::Debug for Image { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Image::Url(ref url, ref _extra_data) => write!(f, "url(\"{}\")", url), - Image::LinearGradient(ref grad) => write!(f, "linear-gradient({:?})", grad), - } - } -} - -impl ::cssparser::ToCss for Image { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - use values::LocalToCss; - match *self { - Image::Url(ref url, _) => { - url.to_css(dest) - } - Image::LinearGradient(ref gradient) => gradient.to_css(dest) - } - } -} - -/// Computed values for a CSS linear gradient. -#[derive(Clone, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct LinearGradient { - /// The angle or corner of the gradient. - pub angle_or_corner: AngleOrCorner, - - /// The color stops. - pub stops: Vec, -} - -impl ::cssparser::ToCss for LinearGradient { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(dest.write_str("linear-gradient(")); - try!(self.angle_or_corner.to_css(dest)); - for stop in &self.stops { - try!(dest.write_str(", ")); - try!(stop.to_css(dest)); - } - try!(dest.write_str(")")); - Ok(()) - } -} - -impl fmt::Debug for LinearGradient { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let _ = write!(f, "{:?}", self.angle_or_corner); - for stop in &self.stops { - let _ = write!(f, ", {:?}", stop); - } - Ok(()) - } -} - -/// Computed values for one color stop in a linear gradient. -#[derive(Clone, PartialEq, Copy)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ColorStop { - /// The color of this stop. - pub color: CSSColor, - - /// The position of this stop. If not specified, this stop is placed halfway between the - /// point that precedes it and the point that follows it per CSS-IMAGES § 3.4. - pub position: Option, -} - -impl ::cssparser::ToCss for ColorStop { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.color.to_css(dest)); - if let Some(position) = self.position { - try!(dest.write_str(" ")); - try!(position.to_css(dest)); - } - Ok(()) - } -} - -impl fmt::Debug for ColorStop { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let _ = write!(f, "{:?}", self.color); - self.position.map(|pos| { - let _ = write!(f, " {:?}", pos); - }); - Ok(()) - } -} - -impl ToComputedValue for specified::LinearGradient { - type ComputedValue = LinearGradient; - - #[inline] - fn to_computed_value(&self, context: &Context) -> LinearGradient { - let specified::LinearGradient { - angle_or_corner, - ref stops - } = *self; - LinearGradient { - angle_or_corner: angle_or_corner, - stops: stops.iter().map(|stop| { - ColorStop { - color: stop.color.parsed, - position: match stop.position { - None => None, - Some(value) => Some(value.to_computed_value(context)), - }, - } - }).collect() - } - } - #[inline] - fn from_computed_value(computed: &LinearGradient) -> Self { - let LinearGradient { - angle_or_corner, - ref stops - } = *computed; - specified::LinearGradient { - angle_or_corner: angle_or_corner, - stops: stops.iter().map(|stop| { - specified::ColorStop { - color: ToComputedValue::from_computed_value(&stop.color), - position: match stop.position { - None => None, - Some(value) => Some(ToComputedValue::from_computed_value(&value)), - }, - } - }).collect() - } - } -} pub type Length = Au; pub type Number = CSSFloat; pub type Opacity = CSSFloat; diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs new file mode 100644 index 00000000000..2960cae6833 --- /dev/null +++ b/components/style/values/specified/image.rs @@ -0,0 +1,420 @@ +/* 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/. */ + +//! CSS handling for the specified value of +//! [`image`][image]s +//! +//! [image]: https://drafts.csswg.org/css-images/#image-values + +use cssparser::{Parser, ToCss}; +use parser::{Parse, ParserContext}; +use std::f32::consts::PI; +use std::fmt; +use url::Url; +use values::computed::ComputedValueAsSpecified; +use values::specified::{Angle, CSSColor, Length, LengthOrPercentage, UrlExtraData}; +use values::specified::position::{Keyword, Position}; + +/// Specified values for an image according to CSS-IMAGES. +/// https://drafts.csswg.org/css-images/#image-values +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum Image { + Url(Url, UrlExtraData), + Gradient(Gradient), +} + +impl ToCss for Image { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + use values::LocalToCss; + match *self { + Image::Url(ref url, ref _extra_data) => { + url.to_css(dest) + } + Image::Gradient(ref gradient) => gradient.to_css(dest) + } + } +} + +impl Image { + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(url) = input.try(|input| input.expect_url()) { + match UrlExtraData::make_from(context) { + Some(extra_data) => { + Ok(Image::Url(context.parse_url(&url), extra_data)) + }, + None => { + // FIXME(heycam) should ensure we always have a principal, etc., when + // parsing style attributes and re-parsing due to CSS Variables. + println!("stylo: skipping declaration without ParserContextExtraData"); + Err(()) + }, + } + } else { + Ok(Image::Gradient(try!(Gradient::parse_function(input)))) + } + } +} + +/// Specified values for a CSS gradient. +/// https://drafts.csswg.org/css-images/#gradients +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Gradient { + /// The color stops. + pub stops: Vec, + /// True if this is a repeating gradient. + pub repeating: bool, + /// Gradients can be linear or radial. + pub gradient_kind: GradientKind, +} + +impl ToCss for Gradient { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.repeating { + try!(dest.write_str("repeating-")); + } + match self.gradient_kind { + GradientKind::Linear(angle_or_corner) => { + try!(dest.write_str("linear-gradient(")); + try!(angle_or_corner.to_css(dest)); + }, + GradientKind::Radial(ref shape, position) => { + try!(dest.write_str("radial-gradient(")); + try!(shape.to_css(dest)); + try!(dest.write_str(" at ")); + try!(position.to_css(dest)); + }, + } + for stop in &self.stops { + try!(dest.write_str(", ")); + try!(stop.to_css(dest)); + } + dest.write_str(")") + } +} + +impl Gradient { + /// Parses a gradient from the given arguments. + pub fn parse_function(input: &mut Parser) -> Result { + let mut repeating = false; + let (gradient_kind, stops) = match_ignore_ascii_case! { try!(input.expect_function()), + "linear-gradient" => { + try!(input.parse_nested_block(|input| { + let kind = try!(GradientKind::parse_linear(input)); + let stops = try!(input.parse_comma_separated(ColorStop::parse)); + Ok((kind, stops)) + }) + ) + }, + "repeating-linear-gradient" => { + repeating = true; + try!(input.parse_nested_block(|input| { + let kind = try!(GradientKind::parse_linear(input)); + let stops = try!(input.parse_comma_separated(ColorStop::parse)); + Ok((kind, stops)) + }) + ) + }, + "radial-gradient" => { + try!(input.parse_nested_block(|input| { + let kind = try!(GradientKind::parse_radial(input)); + let stops = try!(input.parse_comma_separated(ColorStop::parse)); + Ok((kind, stops)) + }) + ) + }, + "repeating-radial-gradient" => { + repeating = true; + try!(input.parse_nested_block(|input| { + let kind = try!(GradientKind::parse_radial(input)); + let stops = try!(input.parse_comma_separated(ColorStop::parse)); + Ok((kind, stops)) + }) + ) + }, + _ => { return Err(()); } + }; + + Ok(Gradient { + stops: stops, + repeating: repeating, + gradient_kind: gradient_kind, + }) + } +} + +/// Specified values for CSS linear or radial gradients. +/// https://drafts.csswg.org/css-images/#gradients +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum GradientKind { + Linear(AngleOrCorner), + Radial(EndingShape, Position), +} + +impl GradientKind { + /// Parses a linear gradient kind from the given arguments. + pub fn parse_linear(input: &mut Parser) -> Result { + let angle_or_corner = try!(AngleOrCorner::parse(input)); + Ok(GradientKind::Linear(angle_or_corner)) + } + + /// Parses a radial gradient from the given arguments. + pub fn parse_radial(input: &mut Parser) -> Result { + let mut needs_comma = false; + let shape = if let Ok(shape) = EndingShape::parse(input) { + needs_comma = true; + shape + } else { + EndingShape::Circle(LengthOrKeyword::Keyword(SizeKeyword::FarthestSide)) + }; + + let position = if input.try(|input| input.expect_ident_matching("at")).is_ok() { + needs_comma = true; + try!(Position::parse(input)) + } else { + Position { + horiz_keyword: Some(Keyword::Center), + horiz_position: None, + vert_keyword: Some(Keyword::Center), + vert_position: None, + } + }; + + if needs_comma { + try!(input.expect_comma()); + } + + Ok(GradientKind::Radial(shape, position)) + } +} + +/// Specified values for an angle or a corner in a linear gradient. +#[derive(Clone, PartialEq, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum AngleOrCorner { + Angle(Angle), + Corner(HorizontalDirection, VerticalDirection), +} + +impl ToCss for AngleOrCorner { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + AngleOrCorner::Angle(angle) => angle.to_css(dest), + AngleOrCorner::Corner(horizontal, vertical) => { + try!(dest.write_str("to ")); + try!(horizontal.to_css(dest)); + try!(dest.write_str(" ")); + try!(vertical.to_css(dest)); + Ok(()) + } + } + } +} + +impl Parse for AngleOrCorner { + fn parse(input: &mut Parser) -> Result { + if input.try(|input| input.expect_ident_matching("to")).is_ok() { + let (horizontal, vertical) = + if let Ok(value) = input.try(HorizontalDirection::parse) { + (Some(value), input.try(VerticalDirection::parse).ok()) + } else { + let value = try!(VerticalDirection::parse(input)); + (input.try(HorizontalDirection::parse).ok(), Some(value)) + }; + try!(input.expect_comma()); + match (horizontal, vertical) { + (None, Some(VerticalDirection::Top)) => { + Ok(AngleOrCorner::Angle(Angle(0.0))) + }, + (Some(HorizontalDirection::Right), None) => { + Ok(AngleOrCorner::Angle(Angle(PI * 0.5))) + }, + (None, Some(VerticalDirection::Bottom)) => { + Ok(AngleOrCorner::Angle(Angle(PI))) + }, + (Some(HorizontalDirection::Left), None) => { + Ok(AngleOrCorner::Angle(Angle(PI * 1.5))) + }, + (Some(horizontal), Some(vertical)) => { + Ok(AngleOrCorner::Corner(horizontal, vertical)) + } + (None, None) => unreachable!(), + } + } else if let Ok(angle) = input.try(Angle::parse) { + try!(input.expect_comma()); + Ok(AngleOrCorner::Angle(angle)) + } else { + Ok(AngleOrCorner::Angle(Angle(PI))) + } + } +} + +impl ComputedValueAsSpecified for AngleOrCorner {} + +/// Specified values for one color stop in a linear gradient. +/// https://drafts.csswg.org/css-images/#typedef-color-stop-list +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct ColorStop { + /// The color of this stop. + pub color: CSSColor, + + /// The position of this stop. If not specified, this stop is placed halfway between the + /// point that precedes it and the point that follows it. + pub position: Option, +} + +impl ToCss for ColorStop { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + try!(self.color.to_css(dest)); + if let Some(position) = self.position { + try!(dest.write_str(" ")); + try!(position.to_css(dest)); + } + Ok(()) + } +} + +define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right); +define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom); + +impl Parse for ColorStop { + fn parse(input: &mut Parser) -> Result { + Ok(ColorStop { + color: try!(CSSColor::parse(input)), + position: input.try(LengthOrPercentage::parse).ok(), + }) + } +} + +/// Determines whether the gradient's ending shape is a circle or an ellipse. +/// If is omitted, the ending shape defaults to a circle +/// if the is a single , and to an ellipse otherwise. +/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-ending-shape +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum EndingShape { + Circle(LengthOrKeyword), + Ellipse(LengthOrPercentageOrKeyword), +} + +impl Parse for EndingShape { + fn parse(input: &mut Parser) -> Result { + // FIXME(#13664): Normally size can come before shape keywords but currently + // parsing fails if size comes before shape keyword. + match_ignore_ascii_case! { try!(input.expect_ident()), + "circle" => { + let position = input.try(LengthOrKeyword::parse).unwrap_or( + LengthOrKeyword::Keyword(SizeKeyword::FarthestSide)); + Ok(EndingShape::Circle(position)) + }, + "ellipse" => { + let length = input.try(LengthOrPercentageOrKeyword::parse) + .unwrap_or(LengthOrPercentageOrKeyword::Keyword(SizeKeyword::FarthestSide)); + Ok(EndingShape::Ellipse(length)) + }, + _ => { + // If two is present, it defaults to ellipse, otherwise defaults to circle. + if let Ok(length) = LengthOrPercentageOrKeyword::parse(input) { + if let LengthOrPercentageOrKeyword::Keyword(keyword) = length { + // A single keyword is valid for both ellipse and circle, but we default to circle. + // The grammar for ending shapes for circle and ellipse have overlap so we cannot simply + // try to parse as circle first + Ok(EndingShape::Circle(LengthOrKeyword::Keyword(keyword))) + } else { + Ok(EndingShape::Ellipse(length)) + } + } else { + // If both shape and size are omitted, we do not parse as an EndingShape + // Instead, GradientKind::parse_radial will go ahead and parse the stops + // This is necessary because GradientKind::parse_radial needs to know + // whether or not to expect a comma + Ok(EndingShape::Circle(try!(input.try(LengthOrKeyword::parse)))) + } + } + } + } +} + +impl ToCss for EndingShape { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + EndingShape::Circle(ref length) => { + try!(dest.write_str("circle ")); + try!(length.to_css(dest)); + }, + EndingShape::Ellipse(ref length) => { + try!(dest.write_str("ellipse ")); + try!(length.to_css(dest)); + }, + } + Ok(()) + } +} + +/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrKeyword { + Length(Length), + Keyword(SizeKeyword), +} + +impl Parse for LengthOrKeyword { + fn parse(input: &mut Parser) -> Result { + if let Ok(keyword) = input.try(SizeKeyword::parse) { + Ok(LengthOrKeyword::Keyword(keyword)) + } else { + Ok(LengthOrKeyword::Length(try!(Length::parse(input)))) + } + } +} + +impl ToCss for LengthOrKeyword { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrKeyword::Length(ref length) => length.to_css(dest), + LengthOrKeyword::Keyword(keyword) => keyword.to_css(dest), + } + } +} + +/// https://drafts.csswg.org/css-images/#valdef-radial-gradient-size +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum LengthOrPercentageOrKeyword { + LengthOrPercentage(LengthOrPercentage, LengthOrPercentage), + Keyword(SizeKeyword), +} + + +impl Parse for LengthOrPercentageOrKeyword { + fn parse(input: &mut Parser) -> Result { + if let Ok(keyword) = input.try(SizeKeyword::parse) { + Ok(LengthOrPercentageOrKeyword::Keyword(keyword)) + } else { + Ok(LengthOrPercentageOrKeyword::LengthOrPercentage(try!(LengthOrPercentage::parse(input)), + try!(LengthOrPercentage::parse(input)))) + } + } +} + +impl ToCss for LengthOrPercentageOrKeyword { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + LengthOrPercentageOrKeyword::LengthOrPercentage(ref first_len, second_len) => { + try!(first_len.to_css(dest)); + try!(dest.write_str(" ")); + second_len.to_css(dest) + }, + LengthOrPercentageOrKeyword::Keyword(keyword) => keyword.to_css(dest), + } + } +} + +/// https://drafts.csswg.org/css-images/#typedef-extent-keyword +define_css_keyword_enum!(SizeKeyword: "closest-side" => ClosestSide, "farthest-side" => FarthestSide, + "closest-corner" => ClosestCorner, "farthest-corner" => FarthestCorner); diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 64c66ccc729..f1694aa340d 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -7,7 +7,7 @@ use cssparser::{self, Parser, ToCss, Token}; use euclid::size::Size2D; #[cfg(feature = "gecko")] use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI}; -use parser::ParserContext; +use parser::{Parse, ParserContext}; #[cfg(feature = "gecko")] use parser::ParserContextExtraData; use std::ascii::AsciiExt; @@ -18,9 +18,13 @@ use std::ops::Mul; use style_traits::values::specified::AllowedNumericType; use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewportPercentage}; use super::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue}; -use url::Url; + +pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; +pub use self::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword}; +pub use self::image::{SizeKeyword, VerticalDirection}; pub mod basic_shape; +pub mod image; pub mod position; impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order @@ -299,9 +303,6 @@ impl Length { _ => Err(()) } } - pub fn parse(input: &mut Parser) -> Result { - Length::parse_internal(input, AllowedNumericType::All) - } pub fn parse_non_negative(input: &mut Parser) -> Result { Length::parse_internal(input, AllowedNumericType::NonNegative) } @@ -333,6 +334,12 @@ impl Length { } } +impl Parse for Length { + fn parse(input: &mut Parser) -> Result { + Length::parse_internal(input, AllowedNumericType::All) + } +} + #[derive(Clone, Debug)] struct CalcSumNode { products: Vec, @@ -908,16 +915,20 @@ impl LengthOrPercentage { _ => Err(()) } } - #[inline] - pub fn parse(input: &mut Parser) -> Result { - LengthOrPercentage::parse_internal(input, AllowedNumericType::All) - } + #[inline] pub fn parse_non_negative(input: &mut Parser) -> Result { LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative) } } +impl Parse for LengthOrPercentage { + #[inline] + fn parse(input: &mut Parser) -> Result { + LengthOrPercentage::parse_internal(input, AllowedNumericType::All) + } +} + #[derive(Clone, PartialEq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum LengthOrPercentageOrAuto { @@ -1267,180 +1278,6 @@ impl UrlExtraData { } } -/// Specified values for an image according to CSS-IMAGES. -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Image { - Url(Url, UrlExtraData), - LinearGradient(LinearGradient), -} - -impl ToCss for Image { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - use values::LocalToCss; - match *self { - Image::Url(ref url, ref _extra_data) => { - url.to_css(dest) - } - Image::LinearGradient(ref gradient) => gradient.to_css(dest) - } - } -} - -impl Image { - pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { - if let Ok(url) = input.try(|input| input.expect_url()) { - match UrlExtraData::make_from(context) { - Some(extra_data) => { - Ok(Image::Url(context.parse_url(&url), extra_data)) - }, - None => { - // FIXME(heycam) should ensure we always have a principal, etc., when - // parsing style attributes and re-parsing due to CSS Variables. - println!("stylo: skipping declaration without ParserContextExtraData"); - Err(()) - }, - } - } else { - match_ignore_ascii_case! { try!(input.expect_function()), - "linear-gradient" => { - Ok(Image::LinearGradient(try!( - input.parse_nested_block(LinearGradient::parse_function)))) - }, - _ => Err(()) - } - } - } -} - -/// Specified values for a CSS linear gradient. -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct LinearGradient { - /// The angle or corner of the gradient. - pub angle_or_corner: AngleOrCorner, - - /// The color stops. - pub stops: Vec, -} - -impl ToCss for LinearGradient { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(dest.write_str("linear-gradient(")); - try!(self.angle_or_corner.to_css(dest)); - for stop in &self.stops { - try!(dest.write_str(", ")); - try!(stop.to_css(dest)); - } - try!(dest.write_str(")")); - Ok(()) - } -} - -/// Specified values for an angle or a corner in a linear gradient. -#[derive(Clone, PartialEq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum AngleOrCorner { - Angle(Angle), - Corner(HorizontalDirection, VerticalDirection), -} - -impl ToCss for AngleOrCorner { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - AngleOrCorner::Angle(angle) => angle.to_css(dest), - AngleOrCorner::Corner(horizontal, vertical) => { - try!(dest.write_str("to ")); - try!(horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(vertical.to_css(dest)); - Ok(()) - } - } - } -} - -/// Specified values for one color stop in a linear gradient. -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ColorStop { - /// The color of this stop. - pub color: CSSColor, - - /// The position of this stop. If not specified, this stop is placed halfway between the - /// point that precedes it and the point that follows it. - pub position: Option, -} - -impl ToCss for ColorStop { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.color.to_css(dest)); - if let Some(position) = self.position { - try!(dest.write_str(" ")); - try!(position.to_css(dest)); - } - Ok(()) - } -} - -define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right); -define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom); - -fn parse_one_color_stop(input: &mut Parser) -> Result { - Ok(ColorStop { - color: try!(CSSColor::parse(input)), - position: input.try(LengthOrPercentage::parse).ok(), - }) -} - -impl LinearGradient { - /// Parses a linear gradient from the given arguments. - pub fn parse_function(input: &mut Parser) -> Result { - let angle_or_corner = if input.try(|input| input.expect_ident_matching("to")).is_ok() { - let (horizontal, vertical) = - if let Ok(value) = input.try(HorizontalDirection::parse) { - (Some(value), input.try(VerticalDirection::parse).ok()) - } else { - let value = try!(VerticalDirection::parse(input)); - (input.try(HorizontalDirection::parse).ok(), Some(value)) - }; - try!(input.expect_comma()); - match (horizontal, vertical) { - (None, Some(VerticalDirection::Top)) => { - AngleOrCorner::Angle(Angle(0.0)) - }, - (Some(HorizontalDirection::Right), None) => { - AngleOrCorner::Angle(Angle(PI * 0.5)) - }, - (None, Some(VerticalDirection::Bottom)) => { - AngleOrCorner::Angle(Angle(PI)) - }, - (Some(HorizontalDirection::Left), None) => { - AngleOrCorner::Angle(Angle(PI * 1.5)) - }, - (Some(horizontal), Some(vertical)) => { - AngleOrCorner::Corner(horizontal, vertical) - } - (None, None) => unreachable!(), - } - } else if let Ok(angle) = input.try(Angle::parse) { - try!(input.expect_comma()); - AngleOrCorner::Angle(angle) - } else { - AngleOrCorner::Angle(Angle(PI)) - }; - // Parse the color stops. - let stops = try!(input.parse_comma_separated(parse_one_color_stop)); - if stops.len() < 2 { - return Err(()) - } - Ok(LinearGradient { - angle_or_corner: angle_or_corner, - stops: stops, - }) - } -} - pub fn parse_border_radius(input: &mut Parser) -> Result { input.try(BorderRadiusSize::parse).or_else(|()| { match_ignore_ascii_case! { try!(input.expect_ident()), diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index 1de237a6b5e..caebf12108d 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -8,6 +8,7 @@ //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position use cssparser::{Parser, ToCss, Token}; +use parser::Parse; use std::fmt; use values::HasViewportPercentage; use values::computed::{CalcLengthOrPercentage, Context};