/* 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::f32::consts::PI; use std::fmt; use style_traits::ToCss; use values::computed::{Angle, Context, Length, LengthOrPercentage, ToComputedValue}; use values::computed::position::Position; use values::specified::{self, HorizontalDirection, SizeKeyword, VerticalDirection}; use values::specified::url::SpecifiedUrl; impl ToComputedValue for specified::Image { type ComputedValue = Image; #[inline] fn to_computed_value(&self, context: &Context) -> Image { match *self { specified::Image::Url(ref url_value) => { Image::Url(url_value.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_value) => { specified::Image::Url(url_value.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))] #[allow(missing_docs)] pub enum Image { Url(SpecifiedUrl), Gradient(Gradient), } impl fmt::Debug for Image { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Image::Url(ref url) => url.to_css(f), 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 ToCss for Image { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { 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 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))] #[allow(missing_docs)] 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.to_computed_value(context)) }, specified::GradientKind::Radial(ref shape, ref 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 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(ref 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))] #[allow(missing_docs)] pub enum EndingShape { Circle(LengthOrKeyword), Ellipse(LengthOrPercentageOrKeyword), } 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(()) } } 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))] #[allow(missing_docs)] pub enum LengthOrKeyword { Length(Length), Keyword(SizeKeyword), } 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), } } } 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(ref 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))] #[allow(missing_docs)] pub enum LengthOrPercentageOrKeyword { LengthOrPercentage(LengthOrPercentage, LengthOrPercentage), Keyword(SizeKeyword), } 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), } } } 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(ref first_len, ref 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) }, } } } #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] pub enum AngleOrCorner { Angle(Angle), Corner(HorizontalDirection, VerticalDirection) } impl ToComputedValue for specified::AngleOrCorner { type ComputedValue = AngleOrCorner; #[inline] fn to_computed_value(&self, _: &Context) -> AngleOrCorner { match *self { specified::AngleOrCorner::None => { AngleOrCorner::Angle(Angle(PI)) }, specified::AngleOrCorner::Angle(angle) => { AngleOrCorner::Angle(angle) }, specified::AngleOrCorner::Corner(horizontal, vertical) => { 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!() } } } } } #[inline] fn from_computed_value(computed: &AngleOrCorner) -> Self { match *computed { AngleOrCorner::Angle(angle) => { specified::AngleOrCorner::Angle(angle) }, AngleOrCorner::Corner(horizontal, vertical) => { specified::AngleOrCorner::Corner(Some(horizontal), Some(vertical)) } } } } 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(()) } } } }