Implement parsing of all gradients

This commit is contained in:
Nazım Can Altınova 2016-10-05 23:20:54 +03:00
parent 522734de22
commit 26c98edd68
8 changed files with 962 additions and 393 deletions

View 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)
},
}
}
}