mirror of
https://github.com/servo/servo.git
synced 2025-08-01 11:40:30 +01:00
Implement parsing of all gradients
This commit is contained in:
parent
522734de22
commit
26c98edd68
8 changed files with 962 additions and 393 deletions
|
@ -53,7 +53,7 @@ use style::properties::{self, ServoComputedValues};
|
||||||
use style::properties::style_structs;
|
use style::properties::style_structs;
|
||||||
use style::values::RGBA;
|
use style::values::RGBA;
|
||||||
use style::values::computed;
|
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::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
|
||||||
use style_traits::cursor::Cursor;
|
use style_traits::cursor::Cursor;
|
||||||
use table_cell::CollapsedBordersForCell;
|
use table_cell::CollapsedBordersForCell;
|
||||||
|
@ -167,12 +167,12 @@ pub trait FragmentDisplayListBuilding {
|
||||||
|
|
||||||
/// Adds the display items necessary to paint the background linear gradient of this fragment
|
/// Adds the display items necessary to paint the background linear gradient of this fragment
|
||||||
/// to the appropriate section of the display list.
|
/// to the appropriate section of the display list.
|
||||||
fn build_display_list_for_background_linear_gradient(&self,
|
fn build_display_list_for_background_gradient(&self,
|
||||||
state: &mut DisplayListBuildState,
|
state: &mut DisplayListBuildState,
|
||||||
display_list_section: DisplayListSection,
|
display_list_section: DisplayListSection,
|
||||||
absolute_bounds: &Rect<Au>,
|
absolute_bounds: &Rect<Au>,
|
||||||
clip: &ClippingRegion,
|
clip: &ClippingRegion,
|
||||||
gradient: &LinearGradient,
|
gradient: &Gradient,
|
||||||
style: &ServoComputedValues);
|
style: &ServoComputedValues);
|
||||||
|
|
||||||
/// Adds the display items necessary to paint the borders of this fragment to a display list if
|
/// Adds the display items necessary to paint the borders of this fragment to a display list if
|
||||||
|
@ -402,14 +402,17 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
for (i, background_image) in background.background_image.0.iter().enumerate().rev() {
|
for (i, background_image) in background.background_image.0.iter().enumerate().rev() {
|
||||||
match background_image.0 {
|
match background_image.0 {
|
||||||
None => {}
|
None => {}
|
||||||
Some(computed::Image::LinearGradient(ref gradient)) => {
|
Some(computed::Image::Gradient(ref gradient)) => {
|
||||||
self.build_display_list_for_background_linear_gradient(state,
|
// 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,
|
display_list_section,
|
||||||
&bounds,
|
&bounds,
|
||||||
&clip,
|
&clip,
|
||||||
gradient,
|
gradient,
|
||||||
style);
|
style);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Some(computed::Image::Url(ref image_url, ref _extra_data)) => {
|
Some(computed::Image::Url(ref image_url, ref _extra_data)) => {
|
||||||
self.build_display_list_for_background_image(state,
|
self.build_display_list_for_background_image(state,
|
||||||
style,
|
style,
|
||||||
|
@ -636,18 +639,23 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_display_list_for_background_linear_gradient(&self,
|
fn build_display_list_for_background_gradient(&self,
|
||||||
state: &mut DisplayListBuildState,
|
state: &mut DisplayListBuildState,
|
||||||
display_list_section: DisplayListSection,
|
display_list_section: DisplayListSection,
|
||||||
absolute_bounds: &Rect<Au>,
|
absolute_bounds: &Rect<Au>,
|
||||||
clip: &ClippingRegion,
|
clip: &ClippingRegion,
|
||||||
gradient: &LinearGradient,
|
gradient: &Gradient,
|
||||||
style: &ServoComputedValues) {
|
style: &ServoComputedValues) {
|
||||||
let mut clip = clip.clone();
|
let mut clip = clip.clone();
|
||||||
clip.intersect_rect(absolute_bounds);
|
clip.intersect_rect(absolute_bounds);
|
||||||
|
|
||||||
|
|
||||||
let angle = match gradient.angle_or_corner {
|
// 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::Angle(angle) => angle.radians(),
|
||||||
AngleOrCorner::Corner(horizontal, vertical) => {
|
AngleOrCorner::Corner(horizontal, vertical) => {
|
||||||
// This the angle for one of the diagonals of the box. Our angle
|
// This the angle for one of the diagonals of the box. Our angle
|
||||||
|
@ -666,6 +674,10 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
=> -atan,
|
=> -atan,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// FIXME: Radial gradients aren't implemented yet.
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get correct gradient line length, based on:
|
// Get correct gradient line length, based on:
|
||||||
|
|
|
@ -173,8 +173,10 @@
|
||||||
use parser::{ParserContext, ParserContextExtraData};
|
use parser::{ParserContext, ParserContextExtraData};
|
||||||
use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
|
use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
|
||||||
% endif
|
% endif
|
||||||
|
#[allow(unused_imports)]
|
||||||
use cascade_info::CascadeInfo;
|
use cascade_info::CascadeInfo;
|
||||||
use error_reporting::ParseErrorReporter;
|
use error_reporting::ParseErrorReporter;
|
||||||
|
use parser::Parse;
|
||||||
use properties::longhands;
|
use properties::longhands;
|
||||||
use properties::property_bit_field::PropertyBitField;
|
use properties::property_bit_field::PropertyBitField;
|
||||||
use properties::{ComputedValues, PropertyDeclaration};
|
use properties::{ComputedValues, PropertyDeclaration};
|
||||||
|
@ -565,6 +567,8 @@
|
||||||
<%self:shorthand name="${name}" sub_properties="${
|
<%self:shorthand name="${name}" sub_properties="${
|
||||||
' '.join(sub_property_pattern % side
|
' '.join(sub_property_pattern % side
|
||||||
for side in ['top', 'right', 'bottom', 'left'])}">
|
for side in ['top', 'right', 'bottom', 'left'])}">
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use parser::Parse;
|
||||||
use super::parse_four_sides;
|
use super::parse_four_sides;
|
||||||
use values::specified;
|
use values::specified;
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ ${helpers.predefined_type("opacity",
|
||||||
<%helpers:vector_longhand name="box-shadow" allow_empty="True" animatable="True">
|
<%helpers:vector_longhand name="box-shadow" allow_empty="True" animatable="True">
|
||||||
use cssparser::{self, ToCss};
|
use cssparser::{self, ToCss};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use parser::Parse;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
use values::HasViewportPercentage;
|
use values::HasViewportPercentage;
|
||||||
|
|
||||||
|
@ -355,6 +356,7 @@ ${helpers.predefined_type("opacity",
|
||||||
|
|
||||||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use parser::Parse;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use values::specified::Length;
|
use values::specified::Length;
|
||||||
|
|
||||||
|
@ -1199,6 +1201,7 @@ pub struct OriginParseResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> {
|
pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> {
|
||||||
|
use parser::Parse;
|
||||||
use values::specified::{LengthOrPercentage, Percentage};
|
use values::specified::{LengthOrPercentage, Percentage};
|
||||||
let (mut horizontal, mut vertical, mut depth) = (None, None, None);
|
let (mut horizontal, mut vertical, mut depth) = (None, None, None);
|
||||||
loop {
|
loop {
|
||||||
|
|
460
components/style/values/computed/image.rs
Normal file
460
components/style/values/computed/image.rs
Normal file
|
@ -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<W>(&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<ColorStop>,
|
||||||
|
/// 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<W>(&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<LengthOrPercentage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::cssparser::ToCss for ColorStop {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
try!(self.color.to_css(dest));
|
||||||
|
if let Some(position) = self.position {
|
||||||
|
try!(dest.write_str(" "));
|
||||||
|
try!(position.to_css(dest));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ColorStop {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let _ = write!(f, "{:?}", self.color);
|
||||||
|
self.position.map(|pos| {
|
||||||
|
let _ = write!(f, " {:?}", pos);
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for specified::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<W>(&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<W>(&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<W>(&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)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,13 +9,14 @@ use properties::ComputedValues;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::{CSSFloat, specified};
|
use super::{CSSFloat, specified};
|
||||||
use super::LocalToCss;
|
use super::LocalToCss;
|
||||||
use super::specified::AngleOrCorner;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub use cssparser::Color as CSSColor;
|
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 use super::specified::{Angle, BorderStyle, Time, UrlExtraData};
|
||||||
|
|
||||||
pub mod basic_shape;
|
pub mod basic_shape;
|
||||||
|
pub mod image;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
|
|
||||||
pub struct Context<'a> {
|
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<W>(&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<ColorStop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::cssparser::ToCss for LinearGradient {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
try!(dest.write_str("linear-gradient("));
|
|
||||||
try!(self.angle_or_corner.to_css(dest));
|
|
||||||
for stop in &self.stops {
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(stop.to_css(dest));
|
|
||||||
}
|
|
||||||
try!(dest.write_str(")"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for LinearGradient {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let _ = write!(f, "{:?}", self.angle_or_corner);
|
|
||||||
for stop in &self.stops {
|
|
||||||
let _ = write!(f, ", {:?}", stop);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computed values for one color stop in a linear gradient.
|
|
||||||
#[derive(Clone, PartialEq, Copy)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct ColorStop {
|
|
||||||
/// The color of this stop.
|
|
||||||
pub color: CSSColor,
|
|
||||||
|
|
||||||
/// The position of this stop. If not specified, this stop is placed halfway between the
|
|
||||||
/// point that precedes it and the point that follows it per CSS-IMAGES § 3.4.
|
|
||||||
pub position: Option<LengthOrPercentage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::cssparser::ToCss for ColorStop {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
try!(self.color.to_css(dest));
|
|
||||||
if let Some(position) = self.position {
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
try!(position.to_css(dest));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ColorStop {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let _ = write!(f, "{:?}", self.color);
|
|
||||||
self.position.map(|pos| {
|
|
||||||
let _ = write!(f, " {:?}", pos);
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::LinearGradient {
|
|
||||||
type ComputedValue = LinearGradient;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, context: &Context) -> LinearGradient {
|
|
||||||
let specified::LinearGradient {
|
|
||||||
angle_or_corner,
|
|
||||||
ref stops
|
|
||||||
} = *self;
|
|
||||||
LinearGradient {
|
|
||||||
angle_or_corner: angle_or_corner,
|
|
||||||
stops: stops.iter().map(|stop| {
|
|
||||||
ColorStop {
|
|
||||||
color: stop.color.parsed,
|
|
||||||
position: match stop.position {
|
|
||||||
None => None,
|
|
||||||
Some(value) => Some(value.to_computed_value(context)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[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 Length = Au;
|
||||||
pub type Number = CSSFloat;
|
pub type Number = CSSFloat;
|
||||||
pub type Opacity = CSSFloat;
|
pub type Opacity = CSSFloat;
|
||||||
|
|
420
components/style/values/specified/image.rs
Normal file
420
components/style/values/specified/image.rs
Normal file
|
@ -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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
use values::LocalToCss;
|
||||||
|
match *self {
|
||||||
|
Image::Url(ref url, ref _extra_data) => {
|
||||||
|
url.to_css(dest)
|
||||||
|
}
|
||||||
|
Image::Gradient(ref gradient) => gradient.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Image, ()> {
|
||||||
|
if let Ok(url) = input.try(|input| input.expect_url()) {
|
||||||
|
match UrlExtraData::make_from(context) {
|
||||||
|
Some(extra_data) => {
|
||||||
|
Ok(Image::Url(context.parse_url(&url), extra_data))
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// FIXME(heycam) should ensure we always have a principal, etc., when
|
||||||
|
// parsing style attributes and re-parsing due to CSS Variables.
|
||||||
|
println!("stylo: skipping declaration without ParserContextExtraData");
|
||||||
|
Err(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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<ColorStop>,
|
||||||
|
/// 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<W>(&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<Gradient, ()> {
|
||||||
|
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<GradientKind, ()> {
|
||||||
|
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<GradientKind, ()> {
|
||||||
|
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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
AngleOrCorner::Angle(angle) => angle.to_css(dest),
|
||||||
|
AngleOrCorner::Corner(horizontal, vertical) => {
|
||||||
|
try!(dest.write_str("to "));
|
||||||
|
try!(horizontal.to_css(dest));
|
||||||
|
try!(dest.write_str(" "));
|
||||||
|
try!(vertical.to_css(dest));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for AngleOrCorner {
|
||||||
|
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
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<LengthOrPercentage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for ColorStop {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
try!(self.color.to_css(dest));
|
||||||
|
if let Some(position) = self.position {
|
||||||
|
try!(dest.write_str(" "));
|
||||||
|
try!(position.to_css(dest));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right);
|
||||||
|
define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom);
|
||||||
|
|
||||||
|
impl Parse for ColorStop {
|
||||||
|
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
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 <shape> is omitted, the ending shape defaults to a circle
|
||||||
|
/// if the <size> is a single <length>, 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<Self, ()> {
|
||||||
|
// 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 <length> 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<W>(&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<Self, ()> {
|
||||||
|
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<W>(&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<Self, ()> {
|
||||||
|
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<W>(&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);
|
|
@ -7,7 +7,7 @@ use cssparser::{self, Parser, ToCss, Token};
|
||||||
use euclid::size::Size2D;
|
use euclid::size::Size2D;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
|
use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
|
||||||
use parser::ParserContext;
|
use parser::{Parse, ParserContext};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use parser::ParserContextExtraData;
|
use parser::ParserContextExtraData;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
@ -18,9 +18,13 @@ use std::ops::Mul;
|
||||||
use style_traits::values::specified::AllowedNumericType;
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewportPercentage};
|
use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewportPercentage};
|
||||||
use super::computed::{self, ComputedValueAsSpecified, Context, ToComputedValue};
|
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 basic_shape;
|
||||||
|
pub mod image;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
|
|
||||||
impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order
|
impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order
|
||||||
|
@ -299,9 +303,6 @@ impl Length {
|
||||||
_ => Err(())
|
_ => Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn parse(input: &mut Parser) -> Result<Length, ()> {
|
|
||||||
Length::parse_internal(input, AllowedNumericType::All)
|
|
||||||
}
|
|
||||||
pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> {
|
pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> {
|
||||||
Length::parse_internal(input, AllowedNumericType::NonNegative)
|
Length::parse_internal(input, AllowedNumericType::NonNegative)
|
||||||
}
|
}
|
||||||
|
@ -333,6 +334,12 @@ impl Length {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for Length {
|
||||||
|
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
Length::parse_internal(input, AllowedNumericType::All)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct CalcSumNode {
|
struct CalcSumNode {
|
||||||
products: Vec<CalcProductNode>,
|
products: Vec<CalcProductNode>,
|
||||||
|
@ -908,16 +915,20 @@ impl LengthOrPercentage {
|
||||||
_ => Err(())
|
_ => Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn parse(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
|
|
||||||
LengthOrPercentage::parse_internal(input, AllowedNumericType::All)
|
|
||||||
}
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
|
pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
|
||||||
LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative)
|
LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for LengthOrPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
LengthOrPercentage::parse_internal(input, AllowedNumericType::All)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
#[derive(Clone, PartialEq, Copy, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub enum LengthOrPercentageOrAuto {
|
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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
use values::LocalToCss;
|
|
||||||
match *self {
|
|
||||||
Image::Url(ref url, ref _extra_data) => {
|
|
||||||
url.to_css(dest)
|
|
||||||
}
|
|
||||||
Image::LinearGradient(ref gradient) => gradient.to_css(dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Image {
|
|
||||||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Image, ()> {
|
|
||||||
if let Ok(url) = input.try(|input| input.expect_url()) {
|
|
||||||
match UrlExtraData::make_from(context) {
|
|
||||||
Some(extra_data) => {
|
|
||||||
Ok(Image::Url(context.parse_url(&url), extra_data))
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// FIXME(heycam) should ensure we always have a principal, etc., when
|
|
||||||
// parsing style attributes and re-parsing due to CSS Variables.
|
|
||||||
println!("stylo: skipping declaration without ParserContextExtraData");
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match_ignore_ascii_case! { try!(input.expect_function()),
|
|
||||||
"linear-gradient" => {
|
|
||||||
Ok(Image::LinearGradient(try!(
|
|
||||||
input.parse_nested_block(LinearGradient::parse_function))))
|
|
||||||
},
|
|
||||||
_ => Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specified values for a CSS linear gradient.
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct LinearGradient {
|
|
||||||
/// The angle or corner of the gradient.
|
|
||||||
pub angle_or_corner: AngleOrCorner,
|
|
||||||
|
|
||||||
/// The color stops.
|
|
||||||
pub stops: Vec<ColorStop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LinearGradient {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
try!(dest.write_str("linear-gradient("));
|
|
||||||
try!(self.angle_or_corner.to_css(dest));
|
|
||||||
for stop in &self.stops {
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(stop.to_css(dest));
|
|
||||||
}
|
|
||||||
try!(dest.write_str(")"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specified values for an angle or a corner in a linear gradient.
|
|
||||||
#[derive(Clone, PartialEq, Copy, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub enum AngleOrCorner {
|
|
||||||
Angle(Angle),
|
|
||||||
Corner(HorizontalDirection, VerticalDirection),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for AngleOrCorner {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
match *self {
|
|
||||||
AngleOrCorner::Angle(angle) => angle.to_css(dest),
|
|
||||||
AngleOrCorner::Corner(horizontal, vertical) => {
|
|
||||||
try!(dest.write_str("to "));
|
|
||||||
try!(horizontal.to_css(dest));
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
try!(vertical.to_css(dest));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specified values for one color stop in a linear gradient.
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct ColorStop {
|
|
||||||
/// The color of this stop.
|
|
||||||
pub color: CSSColor,
|
|
||||||
|
|
||||||
/// The position of this stop. If not specified, this stop is placed halfway between the
|
|
||||||
/// point that precedes it and the point that follows it.
|
|
||||||
pub position: Option<LengthOrPercentage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for ColorStop {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
try!(self.color.to_css(dest));
|
|
||||||
if let Some(position) = self.position {
|
|
||||||
try!(dest.write_str(" "));
|
|
||||||
try!(position.to_css(dest));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right);
|
|
||||||
define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom);
|
|
||||||
|
|
||||||
fn parse_one_color_stop(input: &mut Parser) -> Result<ColorStop, ()> {
|
|
||||||
Ok(ColorStop {
|
|
||||||
color: try!(CSSColor::parse(input)),
|
|
||||||
position: input.try(LengthOrPercentage::parse).ok(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LinearGradient {
|
|
||||||
/// Parses a linear gradient from the given arguments.
|
|
||||||
pub fn parse_function(input: &mut Parser) -> Result<LinearGradient, ()> {
|
|
||||||
let angle_or_corner = if input.try(|input| input.expect_ident_matching("to")).is_ok() {
|
|
||||||
let (horizontal, vertical) =
|
|
||||||
if let Ok(value) = input.try(HorizontalDirection::parse) {
|
|
||||||
(Some(value), input.try(VerticalDirection::parse).ok())
|
|
||||||
} else {
|
|
||||||
let value = try!(VerticalDirection::parse(input));
|
|
||||||
(input.try(HorizontalDirection::parse).ok(), Some(value))
|
|
||||||
};
|
|
||||||
try!(input.expect_comma());
|
|
||||||
match (horizontal, vertical) {
|
|
||||||
(None, Some(VerticalDirection::Top)) => {
|
|
||||||
AngleOrCorner::Angle(Angle(0.0))
|
|
||||||
},
|
|
||||||
(Some(HorizontalDirection::Right), None) => {
|
|
||||||
AngleOrCorner::Angle(Angle(PI * 0.5))
|
|
||||||
},
|
|
||||||
(None, Some(VerticalDirection::Bottom)) => {
|
|
||||||
AngleOrCorner::Angle(Angle(PI))
|
|
||||||
},
|
|
||||||
(Some(HorizontalDirection::Left), None) => {
|
|
||||||
AngleOrCorner::Angle(Angle(PI * 1.5))
|
|
||||||
},
|
|
||||||
(Some(horizontal), Some(vertical)) => {
|
|
||||||
AngleOrCorner::Corner(horizontal, vertical)
|
|
||||||
}
|
|
||||||
(None, None) => unreachable!(),
|
|
||||||
}
|
|
||||||
} else if let Ok(angle) = input.try(Angle::parse) {
|
|
||||||
try!(input.expect_comma());
|
|
||||||
AngleOrCorner::Angle(angle)
|
|
||||||
} else {
|
|
||||||
AngleOrCorner::Angle(Angle(PI))
|
|
||||||
};
|
|
||||||
// Parse the color stops.
|
|
||||||
let stops = try!(input.parse_comma_separated(parse_one_color_stop));
|
|
||||||
if stops.len() < 2 {
|
|
||||||
return Err(())
|
|
||||||
}
|
|
||||||
Ok(LinearGradient {
|
|
||||||
angle_or_corner: angle_or_corner,
|
|
||||||
stops: stops,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
|
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
|
||||||
input.try(BorderRadiusSize::parse).or_else(|()| {
|
input.try(BorderRadiusSize::parse).or_else(|()| {
|
||||||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
|
//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
|
||||||
|
|
||||||
use cssparser::{Parser, ToCss, Token};
|
use cssparser::{Parser, ToCss, Token};
|
||||||
|
use parser::Parse;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::HasViewportPercentage;
|
use values::HasViewportPercentage;
|
||||||
use values::computed::{CalcLengthOrPercentage, Context};
|
use values::computed::{CalcLengthOrPercentage, Context};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue